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 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 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 IndexUpload(rw http.ResponseWriter, r *http.Request) *httperr.Error { err := r.ParseMultipartForm(10 * 1024 * 1024) hash := mux.Vars(r)["hash"] if err != nil { return httperr.Server(err) } 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 = models.IndexUpload(hash, data) if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }
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 SSLCreate(rw http.ResponseWriter, r *http.Request) *httperr.Error { a := mux.Vars(r)["app"] process := GetForm(r, "process") port := GetForm(r, "port") body := GetForm(r, "body") key := GetForm(r, "key") secure := GetForm(r, "secure") 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.CreateSSL(a, process, portn, body, key, (secure == "true")) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "%s", err) } if err != nil { return httperr.Server(err) } return RenderJson(rw, ssl) }
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 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) }
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 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 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" { message := err.(awserr.Error).Message() return httperr.Errorf(403, message) } if err != nil { return httperr.Server(err) } return RenderJson(rw, rr) }
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 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 AppShow(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] a, err := models.GetApp(mux.Vars(r)["app"]) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } if err != nil && strings.HasPrefix(err.Error(), "no such app") { return httperr.Errorf(404, "no such app: %s", app) } if err != nil { return httperr.Server(err) } return RenderJson(rw, a) }
func LinkDelete(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] 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 unlink service with status: %s", s.Status) } if s.Type != "papertrail" { return httperr.Errorf(403, "unlinking is not yet implemented for service type: %s", s.Type) } 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.UnlinkPapertrail(*a) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) }
func FormationSet(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) } // initialize to invalid values that indicate no change var count, memory int64 = -1, -1 // update based on form input if cc := GetForm(r, "count"); cc != "" { if c, err := strconv.ParseInt(cc, 10, 64); err != nil { return httperr.Errorf(403, "count must be numeric") } else { count = c } } if mm := GetForm(r, "memory"); mm != "" { if m, err := strconv.ParseInt(mm, 10, 64); err != nil { return httperr.Errorf(403, "memory must be numeric") } else { memory = m } } 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) }
func EnvironmentList(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] 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) } return RenderJson(rw, env) }
func ParametersList(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) } return RenderJson(rw, a.Parameters) }
func SSLList(rw http.ResponseWriter, r *http.Request) *httperr.Error { a := mux.Vars(r)["app"] ssls, err := models.ListSSLs(a) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", a) } if err != nil { return httperr.Server(err) } return RenderJson(rw, ssls) }
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 BuildLogs(ws *websocket.Conn) *httperr.Error { vars := mux.Vars(ws.Request()) app := vars["app"] build := vars["build"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } _, err = models.GetBuild(app, build) if err != nil { return httperr.Server(err) } // proxy to docker container logs // https://docs.docker.com/reference/api/docker_remote_api_v1.19/#get-container-logs client, err := docker.NewClient("unix:///var/run/docker.sock") if err != nil { return httperr.Server(err) } r, w := io.Pipe() quit := make(chan bool) go scanLines(r, ws) go keepAlive(ws, quit) err = client.Logs(docker.LogsOptions{ Container: fmt.Sprintf("build-%s", build), Follow: true, Stdout: true, Stderr: true, Tail: "all", RawTerminal: false, OutputStream: w, ErrorStream: w, }) quit <- true return httperr.Server(err) }
func FormationList(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } formation, err := models.ListFormation(app) if err != nil { return httperr.Server(err) } return RenderJson(rw, formation) }
func ServiceUpdate(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 = 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 } err = s.Update(models.CFParams(params)) if err != nil && awsError(err) == "ValidationError" { e := err.(awserr.Error) return httperr.Errorf(403, convoxifyCloudformationError(e.Message())) } if err != nil { return httperr.Server(err) } s, err = models.GetService(service) if err != nil { return httperr.Server(err) } return RenderJson(rw, s) }
func ProcessExecAttached(ws *websocket.Conn) *httperr.Error { vars := mux.Vars(ws.Request()) app := vars["app"] pid := vars["pid"] command := ws.Request().Header.Get("Command") 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) } return httperr.Server(a.ExecAttached(pid, command, ws)) }
func ProcessList(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] _, 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) } final := models.Processes{} if r.URL.Query().Get("stats") != "false" { psch := make(chan models.Process) errch := make(chan error) for _, p := range processes { p := p go p.FetchStatsAsync(psch, errch) } for _, _ = range processes { err := <-errch if err != nil { return httperr.Server(err) } final = append(final, <-psch) } } else { final = processes } sort.Sort(final) return RenderJson(rw, final) }
func ProcessRunDetached(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] process := vars["process"] command := GetForm(r, "command") a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } err = a.RunDetached(process, command) if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }
func BuildList(rw http.ResponseWriter, r *http.Request) *httperr.Error { app := mux.Vars(r)["app"] builds, err := models.ListBuilds(app) if err != nil { return httperr.Server(err) } _, err = models.GetApp(app) 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 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) } err = app.Delete() if err != nil { return httperr.Server(err) } return RenderSuccess(rw) }
func ServiceLogs(ws *websocket.Conn) *httperr.Error { service := mux.Vars(ws.Request())["service"] s, err := models.GetService(service) if err != nil { return httperr.Server(err) } logs := make(chan []byte) done := make(chan bool) s.SubscribeLogs(logs, done) go signalWsClose(ws, done) for data := range logs { ws.Write(data) } return nil }
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") height, _ := strconv.Atoi(header.Get("Height")) width, _ := strconv.Atoi(header.Get("Width")) 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) } return httperr.Server(a.RunAttached(process, command, height, width, ws)) }