func AppCreate(rw http.ResponseWriter, r *http.Request) 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 err } return RenderForbidden(rw, fmt.Sprintf("There is already an app named %s (%s)", name, app.Status)) } if err != nil { return err } app, err = models.GetApp(name) if err != nil { return err } return RenderJson(rw, app) }
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 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 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 AppLogs(ws *websocket.Conn) error { defer ws.Close() app := mux.Vars(ws.Request())["app"] a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return fmt.Errorf("no such app: %s", app) } if err != nil { return err } logs := make(chan []byte) done := make(chan bool) a.SubscribeLogs(logs, done) for data := range logs { ws.Write(data) } return nil }
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 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 ProcessRunDetached(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] process := vars["process"] command := GetForm(r, "command") release := GetForm(r, "release") a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such app: %s", app) } err = a.RunDetached(process, command, release) if err != nil { if strings.HasPrefix(err.Error(), "no such release") { return httperr.Errorf(404, err.Error()) } return httperr.Server(err) } return RenderSuccess(rw) }
func BuildCreate(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] cache := !(r.FormValue("cache") == "false") manifest := r.FormValue("manifest") description := r.FormValue("description") repo := r.FormValue("repo") index := r.FormValue("index") source, _, err := r.FormFile("source") if err != nil && err != http.ErrMissingFile && err != http.ErrNotMultipart { helpers.TrackError("build", err, map[string]interface{}{"at": "FormFile"}) return httperr.Server(err) } // Log into private registries that we might pull from // TODO: move to prodiver BuildCreate err = models.LoginPrivateRegistries() if err != nil { return httperr.Server(err) } a, err := models.GetApp(app) if err != nil { return httperr.Server(err) } // Log into registry that we will push to _, err = models.AppDockerLogin(*a) if err != nil { return httperr.Server(err) } var b *structs.Build // if source file was posted, build from tar if source != nil { b, err = models.Provider().BuildCreateTar(app, source, r.FormValue("manifest"), r.FormValue("description"), cache) } else if repo != "" { b, err = models.Provider().BuildCreateRepo(app, repo, r.FormValue("manifest"), r.FormValue("description"), cache) } else if index != "" { var i structs.Index err := json.Unmarshal([]byte(index), &i) if err != nil { return httperr.Server(err) } b, err = models.Provider().BuildCreateIndex(app, i, manifest, description, cache) } else { return httperr.Errorf(403, "no source, repo or index") } if err != nil { return httperr.Server(err) } return RenderJson(rw, b) }
func ProcessStop(rw http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) app := vars["app"] process := vars["process"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) } ps, err := models.GetProcess(app, process) if err != nil { return err } if ps == nil { return RenderNotFound(rw, fmt.Sprintf("no such process: %s", process)) } err = ps.Stop() if err != nil { return err } return RenderJson(rw, ps) }
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 ReleasePromote(rw http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) app := vars["app"] release := vars["release"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) } rr, err := models.GetRelease(app, release) if err != nil && strings.HasPrefix(err.Error(), "no such release") { return RenderNotFound(rw, fmt.Sprintf("no such release: %s", release)) } if err != nil { return err } err = rr.Promote() if awsError(err) == "ValidationError" { return RenderForbidden(rw, err.(awserr.Error).Message()) } if err != nil { return err } return RenderJson(rw, rr) }
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 FormationSet(rw http.ResponseWriter, r *http.Request) 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 RenderNotFound(rw, fmt.Sprintf("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 fmt.Errorf("no updates are to be performed: %s", app) case strings.Index(ae.Error(), "can not be updated") > -1: return fmt.Errorf("app is already updating: %s", app) } } } if err != nil { return err } return RenderSuccess(rw) }
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 StartImages() { var log = logger.New("ns=app_images") if os.Getenv("DEVELOPMENT") == "true" { return } maxRetries := 5 var err error for i := 0; i < maxRetries; i++ { err := dockerLogin() if err == nil { break } time.Sleep(30 * time.Second) } if err != nil { return } apps, err := models.ListApps() if err != nil { log.Error(err) return } for _, app := range apps { a, err := models.GetApp(app.Name) if err != nil { log.Error(err) continue } for key, value := range a.Parameters { if strings.HasSuffix(key, "Image") { log.Log("cmd=%q", fmt.Sprintf("docker pull %s", value)) data, err := exec.Command("docker", "pull", value).CombinedOutput() if err != nil { fmt.Printf("%+v\n", string(data)) log.Error(err) continue } } } } }
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 BuildCopy(rw http.ResponseWriter, r *http.Request) *httperr.Error { vars := mux.Vars(r) app := vars["app"] build := vars["build"] dest := r.FormValue("app") _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such source app: %s", app) } srcBuild, err := models.GetBuild(app, build) if err != nil && strings.HasPrefix(err.Error(), "no such build") { return httperr.Errorf(404, err.Error()) } if err != nil { return httperr.Server(err) } destApp, err := models.GetApp(dest) if awsError(err) == "ValidationError" { return httperr.Errorf(404, "no such destination app: %s", dest) } destBuild, err := srcBuild.CopyTo(*destApp) if err != nil { return httperr.Server(err) } return RenderJson(rw, destBuild) }
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 AppShow(rw http.ResponseWriter, r *http.Request) error { app := mux.Vars(r)["app"] a, err := models.GetApp(mux.Vars(r)["app"]) if awsError(err) == "ValidationError" { return RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) } if err != nil { return err } return RenderJson(rw, a) }
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 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) error { vars := mux.Vars(ws.Request()) app := vars["app"] build := vars["build"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return fmt.Errorf("no such app: %s", app) } _, err = models.GetBuild(app, build) if err != nil { return 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 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 err }
func FormationList(rw http.ResponseWriter, r *http.Request) error { app := mux.Vars(r)["app"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { return RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) } formation, err := models.ListFormation(app) if err != nil { return err } return RenderJson(rw, formation) }
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 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 ProcessRunAttached(ws *websocket.Conn) error { vars := mux.Vars(ws.Request()) app := vars["app"] process := vars["process"] command := ws.Request().Header.Get("Command") a, err := models.GetApp(app) if awsError(err) == "ValidationError" { return fmt.Errorf("no such app: %s", app) } if err != nil { return err } return a.RunAttached(process, 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 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 ProcessRunDetached(rw http.ResponseWriter, r *http.Request) 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 RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) } err = a.RunDetached(process, command) if err != nil { return err } return RenderSuccess(rw) }