// BuildDelete deletes a build. Makes sure not to delete a build that is contained in the active release func BuildDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) appName := vars["app"] buildID := vars["build"] active, err := isBuildActive(appName, buildID) if err != nil { return httperr.Errorf(404, err.Error()) } if active { return httperr.Errorf(400, "cannot delete build contained in active release") } err = models.Provider().ReleaseDelete(appName, buildID) if err != nil { return httperr.Server(err) } build, err := models.Provider().BuildDelete(appName, buildID) if err != nil { return httperr.Server(err) } return RenderJson(rw, build) }
func BuildGet(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] build := vars["build"] b, err := models.Provider().BuildGet(app, build) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil && strings.HasPrefix(err.Error(), "no such build") { return httperr.Errorf(404, err.Error()) } if err != nil { return httperr.Server(err) } l, err := models.Provider().BuildLogs(app, build) if err != nil { return httperr.Server(err) } b.Logs = l return RenderJson(rw, b) }
// try to find the docker host that's running a build // try a few times with a sleep func findBuildHost(build string) (string, error) { for i := 1; i < 5; i++ { pss, err := models.ListProcesses(os.Getenv("RACK")) if err != nil { return "", httperr.Server(err) } for _, ps := range pss { client, err := ps.Docker() if err != nil { return "", httperr.Server(err) } res, err := client.ListContainers(docker.ListContainersOptions{ All: true, Filters: map[string][]string{ "name": []string{fmt.Sprintf("build-%s", build)}, }, }) if len(res) > 0 { return fmt.Sprintf("http://%s:2376", ps.Host), nil } } time.Sleep(2 * time.Second) } return "", fmt.Errorf("could not find build host") }
func AppCreate(rw http.ResponseWriter, r *http.Request) *httperr.Error { name := r.FormValue("name") app := &models.App{ Name: name, } err := app.Create() if awsError(err) == "AlreadyExistsException" { app, err := models.GetApp(name) if err != nil { return httperr.Server(err) } return httperr.Errorf(403, "there is already an app named %s (%s)", name, app.Status) } if err != nil { return httperr.Server(err) } app, err = models.GetApp(name) if err != nil { return httperr.Server(err) } return RenderJson(rw, app) }
func EnvironmentDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] name := vars["name"] env, err := models.GetEnvironment(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } delete(env, name) releaseId, err := models.PutEnvironment(app, env) if err != nil { return httperr.Server(err) } rw.Header().Set("Release-Id", releaseId) env, err = models.GetEnvironment(app) if err != nil { return httperr.Server(err) } return RenderJson(rw, env) }
func IndexUpload(rw http.ResponseWriter, r *http.Request) *httperr.Error { hash := mux.Vars(r)["hash"] file, _, err := r.FormFile("data") if err != nil { return httperr.Server(err) } data, err := ioutil.ReadAll(file) if err != nil { return httperr.Server(err) } sum := sha256.Sum256(data) if hash != hex.EncodeToString(sum[:]) { return httperr.New(403, fmt.Errorf("invalid hash")) } err = provider.IndexUpload(hash, data) if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }
func ParametersSet(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } r.ParseMultipartForm(2048) params := map[string]string{} for key, values := range r.Form { params[key] = values[0] } err = a.UpdateParams(params) if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }
func EnvironmentSet(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] _, err := models.GetEnvironment(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } body, err := ioutil.ReadAll(r.Body) if err != nil { return httperr.Server(err) } releaseId, err := models.PutEnvironment(app, models.LoadEnvironment(body)) if err != nil { return httperr.Server(err) } rw.Header().Set("Release-Id", releaseId) env, err := models.GetEnvironment(app) if err != nil { return httperr.Server(err) } return RenderJson(rw, env) }
func AppCreate(rw http.ResponseWriter, r *http.Request) *httperr.Error { name := r.FormValue("name") if name == os.Getenv("RACK") { return httperr.Errorf(403, "application name cannot match rack name (%s). Please choose a different name for your app.", name) } // Early check for unbound app only. if app, err := models.GetAppUnbound(name); err == nil { return httperr.Errorf(403, "there is already a legacy app named %s (%s). We recommend you delete this app and create it again.", name, app.Status) } // If unbound check fails this will result in a bound app. app := &models.App{Name: name} err := app.Create() if awsError(err) == "AlreadyExistsException" { app, err := models.GetApp(name) if err != nil { return httperr.Server(err) } return httperr.Errorf(403, "there is already an app named %s (%s)", name, app.Status) } if err != nil { return httperr.Server(err) } app, err = models.GetApp(name) if err != nil { return httperr.Server(err) } return RenderJson(rw, app) }
func AppDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { name := mux.Vars(r)["app"] app, err := models.GetApp(name) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", name) } if err != nil { return httperr.Server(err) } if app.Tags["Type"] != "app" || app.Tags["System"] != "convox" || app.Tags["Rack"] != os.Getenv("RACK") { return httperr.Errorf(404, "invalid app: %s", name) } err = app.Delete() if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }
func RegistryDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { // server := mux.Vars(r)["server"] server := r.FormValue("server") env, acs, err := models.GetPrivateRegistriesAuth() if err != nil { return httperr.Server(err) } ac, ok := acs[server] if !ok { return httperr.Errorf(404, "no such registry: %s", server) } models.DockerLogout(ac) delete(acs, server) dat, err := json.Marshal(acs) if err != nil { return httperr.Server(err) } env["DOCKER_AUTH_DATA"] = string(dat) err = models.PutRackSettings(env) if err != nil { return httperr.Server(err) } return RenderJson(rw, ac) }
func ServiceDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { service := mux.Vars(r)["service"] s, err := models.GetService(service) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such service: %s", service) } if err != nil { return httperr.Server(err) } // new services should use the provider interfaces if s.Type == "syslog" { s, err := provider.ServiceDelete(service) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) } err = s.Delete() if err != nil { return httperr.Server(err) } s, err = models.GetService(service) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) }
func ServiceDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { service := mux.Vars(r)["service"] s, err := models.GetService(service) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such service: %s", service) } if err != nil { return httperr.Server(err) } err = s.Delete() if err != nil { return httperr.Server(err) } s, err = models.GetService(service) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) }
func ProcessShow(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] process := vars["process"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } p, err := models.GetProcess(app, process) if err != nil { return httperr.Server(err) } err = p.FetchStats() if err != nil { return httperr.Server(err) } return RenderJson(rw, p) }
func ReleasePromote(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] release := vars["release"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } rr, err := models.GetRelease(app, release) if err != nil && strings.HasPrefix(err.Error(), "no such release") { return httperr.Errorf(404, "no such release: %s", release) } if err != nil { return httperr.Server(err) } err = rr.Promote() if awsError(err) == "ValidationError" { return httperr.Errorf(403, err.(awserr.Error).Message()) } if err != nil { return httperr.Server(err) } return RenderJson(rw, rr) }
func SystemUpdate(rw http.ResponseWriter, r *http.Request) *httperr.Error { rack, err := models.GetSystem() if err != nil { return httperr.Server(err) } notifyData := map[string]string{} if count := GetForm(r, "count"); count != "" { count, err := strconv.Atoi(count) if err != nil { return httperr.Server(err) } rack.Count = count notifyData["count"] = strconv.Itoa(count) } if t := GetForm(r, "type"); t != "" { rack.Type = t notifyData["type"] = t } if version := GetForm(r, "version"); version != "" { rack.Version = version notifyData["version"] = version } err = rack.Save() if awsError(err) == "ValidationError" { switch { case strings.Index(err.Error(), "No updates are to be performed") > -1: return httperr.Errorf(403, "no system updates are to be performed") case strings.Index(err.Error(), "can not be updated") > -1: return httperr.Errorf(403, "system is already updating") } } if err != nil { return httperr.Server(err) } rack, err = models.GetSystem() if err != nil { return httperr.Server(err) } models.NotifySuccess("system:update", notifyData) return RenderJson(rw, rack) }
func ServiceCreate(rw http.ResponseWriter, r *http.Request) *httperr.Error { err := r.ParseForm() if err != nil { return httperr.Server(err) } // get the last set value for all form values // ie: foo=1&foo=2 sets foo to "2" params := make(map[string]string) for key, values := range r.Form { val := values[len(values)-1] params[key] = val } name := params["name"] delete(params, "name") kind := params["type"] delete(params, "type") // Early check for unbound service only. service, err := models.GetServiceUnbound(name) if err == nil { return httperr.Errorf(403, "there is already a legacy service named %s (%s). We recommend you delete this service and create it again.", name, service.Status) } if awsError(err) == "ValidationError" { // If unbound check fails this will result in a bound service. service = &models.Service{ Name: name, Type: kind, Parameters: models.CFParams(params), } } err = service.Create() if err != nil && strings.HasSuffix(err.Error(), "not found") { return httperr.Errorf(403, "invalid service type: %s", kind) } if err != nil && awsError(err) == "ValidationError" { e := err.(awserr.Error) return httperr.Errorf(403, convoxifyCloudformationError(e.Message())) } if err != nil { return httperr.Server(err) } service, err = models.GetService(name) if err != nil { return httperr.Server(err) } return RenderJson(rw, service) }
func BuildUpdate(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] build := vars["build"] b, err := provider.BuildGet(app, build) if err != nil { return httperr.Server(err) } if d := r.FormValue("description"); d != "" { b.Description = d } if m := r.FormValue("manifest"); m != "" { b.Manifest = m } if r := r.FormValue("reason"); r != "" { b.Reason = r } if s := r.FormValue("status"); s != "" { b.Status = s b.Ended = time.Now() } // if build was successful create a release if b.Status == "complete" && b.Manifest != "" { _, err := provider.BuildRelease(b) if err != nil { return httperr.Server(err) } } err = provider.BuildSave(b) if err != nil { return httperr.Server(err) } if b.Status == "failed" { provider.EventSend(&structs.Event{ Action: "build:create", Data: map[string]string{ "app": b.App, "id": b.Id, }, }, fmt.Errorf(b.Reason)) } return RenderJson(rw, b) }
func LinkCreate(rw http.ResponseWriter, r *http.Request) *httperr.Error { service := mux.Vars(r)["service"] s, err := models.GetService(service) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such service: %s", service) } if err != nil { return httperr.Server(err) } if s.Status != "running" { return httperr.Errorf(403, "can not link service with status: %s", s.Status) } // new services should use the provider interfaces if s.Type == "syslog" { s, err := provider.ServiceLink(service, GetForm(r, "app"), GetForm(r, "process")) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) } if s.Type != "papertrail" { return httperr.Errorf(403, "linking is not yet implemented for service type: %s", s.Type) } app := GetForm(r, "app") a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } err = s.LinkPapertrail(*a) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) }
func SystemUpdate(rw http.ResponseWriter, r *http.Request) *httperr.Error { rack, err := provider.SystemGet() if err != nil { return httperr.Server(err) } notifyData := map[string]string{} if count := GetForm(r, "count"); count != "" { count, err := strconv.Atoi(count) if err != nil { return httperr.Server(err) } rack.Count = count notifyData["count"] = strconv.Itoa(count) } if t := GetForm(r, "type"); t != "" { rack.Type = t notifyData["type"] = t } if version := GetForm(r, "version"); version != "" { rack.Version = version notifyData["version"] = version } err = provider.SystemSave(*rack) if err != nil { return httperr.Server(err) } rack, err = provider.SystemGet() if err != nil { return httperr.Server(err) } models.NotifySuccess("rack:update", notifyData) return RenderJson(rw, rack) }
func ServiceUpdate(rw http.ResponseWriter, r *http.Request) *httperr.Error { service := mux.Vars(r)["service"] params, err := formHash(r) if err != nil { return httperr.Server(err) } s, err := models.Provider().ServiceUpdate(service, params) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) }
func IndexDiff(rw http.ResponseWriter, r *http.Request) *httperr.Error { var index structs.Index err := json.Unmarshal([]byte(r.FormValue("index")), &index) if err != nil { return httperr.Server(err) } missing, err := models.Provider().IndexDiff(&index) if err != nil { return httperr.Server(err) } return RenderJson(rw, missing) }
func ProcessList(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] stats := r.URL.Query().Get("stats") == "true" _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } processes, err := models.ListProcesses(app) if err != nil { return httperr.Server(err) } if stats { w := new(sync.WaitGroup) erch := make(chan error, len(processes)) for _, p := range processes { w.Add(1) go func(p *models.Process, w *sync.WaitGroup, erch chan error) { err := p.FetchStats() w.Done() if err != nil { erch <- err } }(p, w, erch) } w.Wait() select { case err := <-erch: return httperr.Server(err) default: // noop } } sort.Sort(models.Processes(processes)) return RenderJson(rw, processes) }
func ReleaseShow(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] release := vars["release"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } rr, err := models.GetRelease(app, release) if err != nil && strings.HasPrefix(err.Error(), "no such release") { return httperr.Errorf(404, "no such release: %s", release) } fmt.Printf("err %+v\n", err) if err != nil { return httperr.Server(err) } return RenderJson(rw, rr) }
func ProcessRunAttached(ws *websocket.Conn) *httperr.Error { vars := mux.Vars(ws.Request()) header := ws.Request().Header app := vars["app"] process := vars["process"] command := header.Get("Command") release := header.Get("Release") height, _ := strconv.Atoi(header.Get("Height")) width, _ := strconv.Atoi(header.Get("Width")) _, err := models.Provider().ProcessRun(app, process, structs.ProcessRunOptions{ Command: command, Height: height, Width: width, Release: release, Stream: ws, }) if provider.ErrorNotFound(err) { return httperr.New(404, err) } if err != nil { return httperr.Server(err) } return nil }
func AppLogs(ws *websocket.Conn) *httperr.Error { app := mux.Vars(ws.Request())["app"] header := ws.Request().Header var err error follow := true if header.Get("Follow") == "false" { follow = false } since := 2 * time.Minute if s := header.Get("Since"); s != "" { since, err = time.ParseDuration(s) if err != nil { return httperr.Errorf(403, "Invalid duration %s", s) } } err = models.Provider().LogStream(app, ws, structs.LogStreamOptions{ Filter: header.Get("Filter"), Follow: follow, Since: time.Now().Add(-1 * since), }) if err != nil { if strings.HasSuffix(err.Error(), "write: broken pipe") { return nil } return httperr.Server(err) } return nil }
func AppLogs(ws *websocket.Conn) *httperr.Error { app := mux.Vars(ws.Request())["app"] a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } logs := make(chan []byte) done := make(chan bool) a.SubscribeLogs(logs, done) go signalWsClose(ws, done) for data := range logs { ws.Write(data) } return nil }
func SSLDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] process := vars["process"] port := vars["port"] if process == "" { return httperr.Errorf(403, "must specify a process") } portn, err := strconv.Atoi(port) if err != nil { return httperr.Errorf(403, "port must be numeric") } ssl, err := models.DeleteSSL(app, process, portn) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } return RenderJson(rw, ssl) }
func BuildList(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] l := r.URL.Query().Get("limit") var err error var limit int if l == "" { limit = 20 } else { limit, err = strconv.Atoi(l) if err != nil { return httperr.Errorf(400, err.Error()) } } builds, err := models.Provider().BuildList(app, int64(limit)) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } return RenderJson(rw, builds) }
func FormationSet(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] process := vars["process"] count := GetForm(r, "count") memory := GetForm(r, "memory") _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } err = models.SetFormation(app, process, count, memory) if ae, ok := err.(awserr.Error); ok { if ae.Code() == "ValidationError" { switch { case strings.Index(ae.Error(), "No updates are to be performed") > -1: return httperr.Errorf(403, "no updates are to be performed: %s", app) case strings.Index(ae.Error(), "can not be updated") > -1: return httperr.Errorf(403, "app is already updating: %s", app) } } } if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }