// title: set node status // path: /node/status // method: POST // consume: application/x-www-form-urlencoded // produce: application/json // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: App or unit not found func setNodeStatus(w http.ResponseWriter, r *http.Request, t auth.Token) error { if t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusForbidden, Message: "this token is not allowed to execute this action"} } err := r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var hostInput provision.NodeStatusData dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&hostInput, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } result, err := app.UpdateNodeStatus(hostInput) if err != nil { if err == provision.ErrNodeNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } return err } w.Header().Add("Content-Type", "application/json") return json.NewEncoder(w).Encode(result) }
func setUnitsStatus(w http.ResponseWriter, r *http.Request, t auth.Token) error { if t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusForbidden, Message: "this token is not allowed to execute this action"} } defer r.Body.Close() var input []map[string]string err := json.NewDecoder(r.Body).Decode(&input) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } units := make(map[string]provision.Status, len(input)) for _, unit := range input { units[unit["ID"]] = provision.Status(unit["Status"]) } result, err := app.UpdateUnitsStatus(units) if err != nil { return err } resp := make([]updateUnitsResponse, 0, len(result)) for unit, found := range result { resp = append(resp, updateUnitsResponse{ID: unit, Found: found}) } w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) return json.NewEncoder(w).Encode(resp) }
// title: app log // path: /apps/{app}/log // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: App not found func addLog(w http.ResponseWriter, r *http.Request, t auth.Token) error { a, err := app.GetByName(r.URL.Query().Get(":app")) if err != nil { return err } err = r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } if t.GetAppName() != app.InternalAppName { allowed := permission.Check(t, permission.PermAppUpdateLog, contextsForApp(a)..., ) if !allowed { return permission.ErrUnauthorized } } logs := r.Form["message"] source := r.FormValue("source") if source == "" { source = "app" } unit := r.FormValue("unit") for _, log := range logs { err := a.Log(log, source, unit) if err != nil { return err } } return nil }
func setUnitsStatus(w http.ResponseWriter, r *http.Request, t auth.Token) error { if t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusForbidden, Message: "this token is not allowed to execute this action"} } defer r.Body.Close() var input []provision.UnitStatusData err := json.NewDecoder(r.Body).Decode(&input) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } result, err := app.UpdateNodeStatus(provision.NodeStatusData{Units: input}) if err != nil { return err } w.Header().Add("Content-Type", "application/json") return json.NewEncoder(w).Encode(result) }
func appIsAvailable(w http.ResponseWriter, r *http.Request, t auth.Token) error { a, err := app.GetByName(r.URL.Query().Get(":appname")) if err != nil { return err } if t.GetAppName() != app.InternalAppName { allowed := permission.Check(t, permission.PermAppUpdateUnitAdd, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } } if !a.Available() { return fmt.Errorf("App must be available to receive pushs.") } w.WriteHeader(http.StatusOK) return nil }
func diffDeploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { writer := io.NewKeepAliveWriter(w, 30*time.Second, "") defer writer.Stop() data, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Fprint(w, err.Error()) return err } fmt.Fprint(w, "Saving the difference between the old and new code\n") appName := r.URL.Query().Get(":appname") val, err := url.ParseQuery(string(data)) if err != nil { fmt.Fprint(w, err.Error()) return err } diff := val.Get("customdata") instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } if t.GetAppName() != app.InternalAppName { canDiffDeploy := permission.Check(t, permission.PermAppReadDeploy, append(permission.Contexts(permission.CtxTeam, instance.Teams), permission.Context(permission.CtxApp, instance.Name), permission.Context(permission.CtxPool, instance.Pool), )..., ) if !canDiffDeploy { return &errors.HTTP{Code: http.StatusForbidden, Message: permission.ErrUnauthorized.Error()} } } err = app.SaveDiffData(diff, instance.Name) if err != nil { fmt.Fprintln(w, err.Error()) return err } return nil }
func addLog(w http.ResponseWriter, r *http.Request, t auth.Token) error { queryValues := r.URL.Query() a, err := app.GetByName(queryValues.Get(":app")) if err != nil { return err } defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if t.GetAppName() != app.InternalAppName { allowed := permission.Check(t, permission.PermAppUpdateLog, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )..., ) if !allowed { return permission.ErrUnauthorized } } var logs []string err = json.Unmarshal(body, &logs) source := queryValues.Get("source") if len(source) == 0 { source = "app" } unit := queryValues.Get("unit") for _, log := range logs { err := a.Log(log, source, unit) if err != nil { return err } } w.WriteHeader(http.StatusOK) return nil }
// title: deploy diff // path: /apps/{appname}/diff // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: OK // 400: Invalid data // 403: Forbidden // 404: Not found func diffDeploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { writer := io.NewKeepAliveWriter(w, 30*time.Second, "") defer writer.Stop() fmt.Fprint(w, "Saving the difference between the old and new code\n") appName := r.URL.Query().Get(":appname") diff := r.FormValue("customdata") instance, err := app.GetByName(appName) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } if t.GetAppName() != app.InternalAppName { canDiffDeploy := permission.Check(t, permission.PermAppReadDeploy, contextsForApp(instance)...) if !canDiffDeploy { return &tsuruErrors.HTTP{Code: http.StatusForbidden, Message: permission.ErrUnauthorized.Error()} } } evt, err := event.GetRunning(appTarget(appName), permission.PermAppDeploy.FullName()) if err != nil { return err } return evt.SetOtherCustomData(map[string]string{ "diff": diff, }) }
func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { var file multipart.File var err error if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { file, _, err = r.FormFile("file") if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } version := r.PostFormValue("version") archiveURL := r.PostFormValue("archive-url") if version == "" && archiveURL == "" && file == nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version, the archive-url or upload a file", } } if version != "" && archiveURL != "" { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the version or the archive-url, but not both", } } commit := r.PostFormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") var userName string var instance *app.App if t.IsAppToken() { if t.GetAppName() != appName && t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusUnauthorized, Message: "invalid app token"} } instance, err = app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } userName = r.PostFormValue("user") } else { var user *auth.User var app app.App user, err = t.User() if err != nil { return err } app, err = getApp(appName, user, r) if err != nil { return err } instance = &app userName = t.GetUserName() } writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") defer writer.Stop() err = app.Deploy(app.DeployOptions{ App: instance, Version: version, Commit: commit, File: file, ArchiveURL: archiveURL, OutputStream: writer, User: userName, }) if err == nil { fmt.Fprintln(w, "\nOK") } return err }
func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) error { var file multipart.File var err error if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { file, _, err = r.FormFile("file") if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } archiveURL := r.PostFormValue("archive-url") image := r.PostFormValue("image") if image == "" && archiveURL == "" && file == nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the archive-url, a image url or upload a file.", } } commit := r.PostFormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") origin := r.URL.Query().Get("origin") if image != "" { origin = "image" } if origin != "" { if !app.ValidateOrigin(origin) { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid deployment origin", } } } var userName string if t.IsAppToken() { if t.GetAppName() != appName && t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusUnauthorized, Message: "invalid app token"} } userName = r.PostFormValue("user") } else { userName = t.GetUserName() } instance, err := app.GetByName(appName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } if t.GetAppName() != app.InternalAppName { canDeploy := permission.Check(t, permission.PermAppDeploy, append(permission.Contexts(permission.CtxTeam, instance.Teams), permission.Context(permission.CtxApp, appName), permission.Context(permission.CtxPool, instance.Pool), )..., ) if !canDeploy { return &errors.HTTP{Code: http.StatusForbidden, Message: "User does not have access to this app"} } } writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") defer writer.Stop() var build bool buildString := r.URL.Query().Get("build") if buildString != "" { build, err = strconv.ParseBool(buildString) if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } err = app.Deploy(app.DeployOptions{ App: instance, Commit: commit, File: file, ArchiveURL: archiveURL, OutputStream: writer, User: userName, Image: image, Origin: origin, Build: build, }) if err == nil { fmt.Fprintln(w, "\nOK") } return err }
// title: app deploy // path: /apps/{appname}/deploy // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: OK // 400: Invalid data // 403: Forbidden // 404: Not found func deploy(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { var file multipart.File var fileSize int64 if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/") { file, _, err = r.FormFile("file") if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } fileSize, err = file.Seek(0, os.SEEK_END) if err != nil { return errors.Wrap(err, "unable to find uploaded file size") } file.Seek(0, os.SEEK_SET) } archiveURL := r.FormValue("archive-url") image := r.FormValue("image") if image == "" && archiveURL == "" && file == nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "you must specify either the archive-url, a image url or upload a file.", } } commit := r.FormValue("commit") w.Header().Set("Content-Type", "text") appName := r.URL.Query().Get(":appname") origin := r.FormValue("origin") if image != "" { origin = "image" } if origin != "" { if !app.ValidateOrigin(origin) { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid deployment origin", } } } var userName string if t.IsAppToken() { if t.GetAppName() != appName && t.GetAppName() != app.InternalAppName { return &tsuruErrors.HTTP{Code: http.StatusUnauthorized, Message: "invalid app token"} } userName = r.FormValue("user") } else { commit = "" userName = t.GetUserName() } instance, err := app.GetByName(appName) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } var build bool buildString := r.FormValue("build") if buildString != "" { build, err = strconv.ParseBool(buildString) if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } } message := r.FormValue("message") if commit != "" && message == "" { var messages []string messages, err = repository.Manager().CommitMessages(instance.Name, commit, 1) if err != nil { return err } if len(messages) > 0 { message = messages[0] } } if origin == "" && commit != "" { origin = "git" } opts := app.DeployOptions{ App: instance, Commit: commit, FileSize: fileSize, File: file, ArchiveURL: archiveURL, User: userName, Image: image, Origin: origin, Build: build, Message: message, } opts.GetKind() if t.GetAppName() != app.InternalAppName { canDeploy := permission.Check(t, permSchemeForDeploy(opts), contextsForApp(instance)...) if !canDeploy { return &tsuruErrors.HTTP{Code: http.StatusForbidden, Message: "User does not have permission to do this action in this app"} } } var imageID string evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: userName}, CustomData: opts, Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(instance)...), AllowedCancel: event.Allowed(permission.PermAppUpdateEvents, contextsForApp(instance)...), Cancelable: true, }) if err != nil { return err } defer func() { evt.DoneCustomData(err, map[string]string{"image": imageID}) }() opts.Event = evt writer := io.NewKeepAliveWriter(w, 30*time.Second, "please wait...") defer writer.Stop() opts.OutputStream = writer imageID, err = app.Deploy(opts) if err == nil { fmt.Fprintln(w, "\nOK") } return err }