func AppCreate(rw http.ResponseWriter, r *http.Request) { log := appsLogger("create").Start() name := GetForm(r, "name") repo := GetForm(r, "repo") app := &models.App{ Name: name, Repository: repo, } err := app.Create() if awsError(err) == "AlreadyExistsException" { app, err := models.GetApp(name) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } err = fmt.Errorf("There is already an app named %s (%s)", name, app.Status) helpers.Error(log, err) RenderError(rw, err) return } if err != nil { helpers.Error(log, err) RenderError(rw, err) return } Redirect(rw, r, fmt.Sprintf("/apps/%s", name)) }
func AppShow(rw http.ResponseWriter, r *http.Request) { log := appsLogger("show").Start() app := mux.Vars(r)["app"] a, err := models.GetApp(app) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) return } if err != nil { helpers.Error(log, err) RenderError(rw, err) return } switch r.Header.Get("Content-Type") { case "application/json": RenderJson(rw, a) default: RenderTemplate(rw, "app", a) } }
func AppDelete(rw http.ResponseWriter, r *http.Request) { log := appsLogger("delete").Start() vars := mux.Vars(r) name := vars["app"] app, err := models.GetApp(name) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such app: %s", name)) return } if err != nil { helpers.Error(log, err) RenderError(rw, err) return } log.Success("step=app.get app=%q", app.Name) err = app.Delete() if err != nil { helpers.Error(log, err) RenderError(rw, err) return } log.Success("step=app.delete app=%q", app.Name) RenderText(rw, "ok") }
func SystemShow(rw http.ResponseWriter, r *http.Request) { log := systemLogger("show").Start() rack := os.Getenv("RACK") a, err := models.GetApp(rack) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such stack: %s", rack)) return } if err != nil { helpers.Error(log, err) RenderError(rw, err) return } switch r.Header.Get("Content-Type") { case "application/json": RenderJson(rw, a) default: RenderTemplate(rw, "app", a) } }
func AppNameAvailable(rw http.ResponseWriter, r *http.Request) { app, _ := models.GetApp(mux.Vars(r)["app"]) if app != nil { RenderText(rw, "false") } else { RenderText(rw, "true") } }
func AppUpdate(rw http.ResponseWriter, r *http.Request) { log := appsLogger("update").Start() vars := mux.Vars(r) name := vars["app"] app, err := models.GetApp(name) if err != nil { log.Error(err) RenderError(rw, err) return } params := map[string]string{} if process := GetForm(r, "process"); process != "" { process = models.UpperName(process) if count := GetForm(r, "count"); count != "" { params[process+"DesiredCount"] = count } if cpu := GetForm(r, "cpu"); cpu != "" { params[process+"Cpu"] = cpu } if mem := GetForm(r, "mem"); mem != "" { params[process+"Memory"] = mem } } if len(params) > 0 { err := app.UpdateParams(params) if ae, ok := err.(awserr.Error); ok { if ae.Code() == "ValidationError" { switch { case strings.Index(ae.Error(), "No updates are to be performed") > -1: RenderNotFound(rw, fmt.Sprintf("no updates are to be performed: %s", name)) return case strings.Index(ae.Error(), "can not be updated") > -1: RenderNotFound(rw, fmt.Sprintf("app is already updating: %s", name)) return } } } if err != nil { log.Error(err) RenderError(rw, err) return } } Redirect(rw, r, fmt.Sprintf("/apps/%s", name)) }
func pullAppImages() { 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 AppStatus(rw http.ResponseWriter, r *http.Request) { log := appsLogger("status").Start() app, err := models.GetApp(mux.Vars(r)["app"]) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } RenderText(rw, app.Status) }
func AppDebug(rw http.ResponseWriter, r *http.Request) { log := appsLogger("environment").Start() app := mux.Vars(r)["app"] a, err := models.GetApp(app) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } RenderPartial(rw, "app", "debug", a) }
func pullAppImages() { if os.Getenv("DEVELOPMENT") == "true" { return } var log = logger.New("ns=app_images") apps, err := models.ListApps() if err != nil { log.Error(err) return } log.Log("cmd=%q", fmt.Sprintf("docker login -e [email protected] -u convox -p ***** %s", os.Getenv("REGISTRY_HOST"))) data, err := exec.Command("docker", "login", "-e", "*****@*****.**", "-u", "convox", "-p", os.Getenv("PASSWORD"), os.Getenv("REGISTRY_HOST")).CombinedOutput() if err != nil { fmt.Printf("%+v\n", string(data)) 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 AppReleases(rw http.ResponseWriter, r *http.Request) { log := appsLogger("releases").Start() vars := mux.Vars(r) app := vars["app"] l := map[string]string{ "id": r.URL.Query().Get("id"), "created": r.URL.Query().Get("created"), } a, err := models.GetApp(app) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } releases, err := models.ListReleases(app, l) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } params := map[string]interface{}{ "App": a, "Releases": releases, } if len(releases) > 0 { params["Last"] = releases[len(releases)-1] } switch r.Header.Get("Content-Type") { case "application/json": RenderJson(rw, releases) default: RenderPartial(rw, "app", "releases", params) } }
func AppStream(rw http.ResponseWriter, r *http.Request) { log := appsLogger("stream").Start() app := mux.Vars(r)["app"] ws, err := upgrader.Upgrade(rw, r, nil) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } log.Success("step=upgrade app=%q", app) defer ws.Close() a, err := models.GetApp(mux.Vars(r)["app"]) if awsError(err) == "ValidationError" { ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("ERROR: no such app: %s\n", app))) return } if err != nil { helpers.Error(log, err) RenderError(rw, err) return } logs := make(chan []byte) done := make(chan bool) a.SubscribeLogs(logs, done) for data := range logs { ws.WriteMessage(websocket.TextMessage, data) } log.Success("step=ended app=%q", app) }
func ProcessRun(rw http.ResponseWriter, r *http.Request) { log := processesLogger("run").Start() vars := mux.Vars(r) app := vars["app"] process := vars["process"] command := GetForm(r, "command") _, err := models.GetApp(app) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) return } ps, err := models.GetProcess(app, process) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } if ps == nil { RenderNotFound(rw, fmt.Sprintf("no such process: %s", process)) return } err = ps.Run(models.ProcessRunOptions{ Command: command, Process: process, }) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } RenderText(rw, "ok") }
func ProcessList(rw http.ResponseWriter, r *http.Request) { log := appsLogger("processes").Start() app := mux.Vars(r)["app"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) return } processes, err := models.ListProcesses(app) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } RenderJson(rw, processes) }
func ProcessTop(rw http.ResponseWriter, r *http.Request) { log := processesLogger("info").Start() vars := mux.Vars(r) app := vars["app"] id := vars["id"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) return } ps, err := models.GetProcessById(app, id) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } if ps == nil { RenderNotFound(rw, fmt.Sprintf("no such process: %s", id)) return } info, err := ps.Top() if err != nil { helpers.Error(log, err) RenderError(rw, err) return } RenderJson(rw, info) }
func ProcessShow(rw http.ResponseWriter, r *http.Request) { log := processesLogger("show").Start() vars := mux.Vars(r) app := vars["app"] process := vars["process"] _, err := models.GetApp(app) if awsError(err) == "ValidationError" { RenderNotFound(rw, fmt.Sprintf("no such app: %s", app)) return } p, err := models.GetProcess(app, process) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } RenderTemplate(rw, "process", p) }
func ReleaseCreate(rw http.ResponseWriter, r *http.Request) { log := releasesLogger("create").Start() vars := mux.Vars(r) name := vars["app"] manifest := GetForm(r, "manifest") tag := GetForm(r, "tag") app, err := models.GetApp(name) if err != nil { helpers.Error(log, err) RenderError(rw, err) return } release, err := app.ForkRelease() if err != nil { helpers.Error(log, err) RenderError(rw, err) return } build := models.NewBuild(app.Name) build.Id = tag build.Release = release.Id build.Status = "complete" release.Build = build.Id release.Manifest = manifest err = build.Save() if err != nil { helpers.Error(log, err) RenderError(rw, err) return } err = release.Save() if err != nil { helpers.Error(log, err) RenderError(rw, err) return } err = release.Promote() if err != nil { helpers.Error(log, err) RenderError(rw, err) return } log.Success("step=release.create app=%q", app.Name) RenderText(rw, "ok") }
func SystemUpdate(rw http.ResponseWriter, r *http.Request) { log := systemLogger("update").Start() app, err := models.GetApp(os.Getenv("RACK")) if err != nil { log.Error(err) RenderError(rw, err) return } p := map[string]string{} if version := GetForm(r, "version"); version != "" { p["Version"] = version } if count := GetForm(r, "count"); count != "" { p["InstanceCount"] = count } if t := GetForm(r, "type"); t != "" { p["InstanceType"] = t } if len(p) > 0 { req := &cloudformation.UpdateStackInput{ StackName: aws.String(app.Name), Capabilities: []*string{aws.String("CAPABILITY_IAM")}, } if p["Version"] == "" { req.UsePreviousTemplate = aws.Boolean(true) } else { req.TemplateURL = aws.String(fmt.Sprintf("http://convox.s3.amazonaws.com/release/%s/formation.json", p["Version"])) } params := app.Parameters for key, val := range p { params[key] = val } for key, val := range params { req.Parameters = append(req.Parameters, &cloudformation.Parameter{ ParameterKey: aws.String(key), ParameterValue: aws.String(val), }) } _, err := models.CloudFormation().UpdateStack(req) if ae, ok := err.(awserr.Error); ok { if ae.Code() == "ValidationError" { switch { case strings.Index(ae.Error(), "No updates are to be performed") > -1: RenderNotFound(rw, fmt.Sprintf("no system updates are to be performed.")) return case strings.Index(ae.Error(), "can not be updated") > -1: RenderNotFound(rw, fmt.Sprintf("system is already updating.")) return } } } if err != nil { log.Error(err) RenderError(rw, err) return } } Redirect(rw, r, "/system") }