// title: logs config set // path: /docker/logs // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized func logsConfigSetHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: fmt.Sprintf("unable to parse form values: %s", err), } } pool := r.FormValue("pool") restart, _ := strconv.ParseBool(r.FormValue("restart")) delete(r.Form, "pool") delete(r.Form, "restart") var conf container.DockerLogConfig dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&conf, r.Form) if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: fmt.Sprintf("unable to parse fields in docker log config: %s", err), } } var ctxs []permission.PermissionContext if pool != "" { ctxs = append(ctxs, permission.Context(permission.CtxPool, pool)) } hasPermission := permission.Check(t, permission.PermPoolUpdateLogs, ctxs...) if !hasPermission { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: pool}, Kind: permission.PermPoolUpdateLogs, Owner: t, CustomData: event.FormToCustomData(r.Form), DisableLock: true, Allowed: event.Allowed(permission.PermPoolReadEvents, ctxs...), }) if err != nil { return err } defer func() { evt.Done(err) }() err = conf.Save(pool) if err != nil { return err } w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} fmt.Fprintln(writer, "Log config successfully updated.") if restart { filter := &app.Filter{} if pool != "" { filter.Pools = []string{pool} } return tryRestartAppsByFilter(filter, writer) } return nil }
// title: remove app // path: /apps/{name} // method: DELETE // produce: application/x-json-stream // responses: // 200: App removed // 401: Unauthorized // 404: Not found func appDelete(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() a, err := getAppFromContext(r.URL.Query().Get(":app"), r) if err != nil { return err } canDelete := permission.Check(t, permission.PermAppDelete, contextsForApp(&a)..., ) if !canDelete { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: appTarget(a.Name), Kind: permission.PermAppDelete, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} w.Header().Set("Content-Type", "application/x-json-stream") return app.Delete(&a, writer) }
// title: remove role // path: /roles/{name} // method: DELETE // responses: // 200: Role removed // 401: Unauthorized // 404: Role not found func removeRole(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() if !permission.Check(t, permission.PermRoleDelete) { return permission.ErrUnauthorized } roleName := r.URL.Query().Get(":name") evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeRole, Value: roleName}, Kind: permission.PermRoleDelete, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermRoleReadEvents), }) if err != nil { return err } defer func() { evt.Done(err) }() err = auth.RemoveRoleFromAllUsers(roleName) if err != nil { return err } err = permission.DestroyRole(roleName) if err == permission.ErrRoleNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } return err }
func (s *S) TestRollbackWithWrongVersionImage(c *check.C) { a := App{ Name: "otherapp", Plan: Plan{Router: "fake"}, Platform: "zend", Teams: []string{s.team.Name}, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) err = image.AppendAppImageName("otherapp", "registry.somewhere/tsuru/app-example:v1") c.Assert(err, check.IsNil) err = image.AppendAppImageName("otherapp", "registry.somewhere/tsuru/app-example:v2") c.Assert(err, check.IsNil) err = image.AppendAppImageName("invalid", "127.0.0.1:5000/tsuru/app-tsuru-dashboard:v2") c.Assert(err, check.IsNil) writer := &bytes.Buffer{} evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) imgID, err := Deploy(DeployOptions{ App: &a, OutputStream: writer, Image: "v20", Rollback: true, Event: evt, }) c.Assert(err, check.NotNil) c.Assert(err, check.ErrorMatches, `^invalid version: "v20"$`) c.Assert(imgID, check.Equals, "") }
// title: rebuild routes // path: /apps/{app}/routes // method: POST // produce: application/json // responses: // 200: Ok // 401: Unauthorized // 404: App not found func appRebuildRoutes(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { a, err := getAppFromContext(r.URL.Query().Get(":app"), r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppAdminRoutes, contextsForApp(&a)..., ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: appTarget(a.Name), Kind: permission.PermAppAdminRoutes, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() w.Header().Set("Content-Type", "application/json") result, err := rebuild.RebuildRoutes(&a) if err != nil { return err } return json.NewEncoder(w).Encode(&result) }
func (s *S) TestDeployAppSaveDeployDataOriginDragAndDrop(c *check.C) { a := App{ Name: "otherapp", Plan: Plan{Router: "fake"}, Platform: "zend", Teams: []string{s.team.Name}, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) writer := &bytes.Buffer{} evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) _, err = Deploy(DeployOptions{ App: &a, OutputStream: writer, File: ioutil.NopCloser(bytes.NewBuffer([]byte("my file"))), Origin: "drag-and-drop", Event: evt, }) c.Assert(err, check.IsNil) var updatedApp App s.conn.Apps().Find(bson.M{"name": a.Name}).One(&updatedApp) c.Assert(updatedApp.Deploys, check.Equals, uint(1)) }
func (s *S) TestDeployAsleepApp(c *check.C) { a := App{ Name: "some-app", Plan: Plan{Router: "fake"}, Platform: "django", Teams: []string{s.team.Name}, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) s.provisioner.AddUnits(&a, 1, "web", nil) writer := &bytes.Buffer{} err = a.Sleep(writer, "web", &url.URL{Scheme: "http", Host: "proxy:1234"}) c.Assert(err, check.IsNil) units, err := a.Units() c.Assert(err, check.IsNil) for _, u := range units { c.Assert(u.Status, check.Not(check.Equals), provision.StatusStarted) } evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) _, err = Deploy(DeployOptions{ App: &a, Commit: "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c", OutputStream: writer, Event: evt, }) c.Assert(err, check.IsNil) }
// title: regenerate token // path: /users/api-key // method: POST // produce: application/json // responses: // 200: OK // 401: Unauthorized // 404: User not found func regenerateAPIToken(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() email := r.URL.Query().Get("user") if email == "" { email = t.GetUserName() } allowed := permission.Check(t, permission.PermUserUpdateToken, permission.Context(permission.CtxUser, email), ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: userTarget(email), Kind: permission.PermUserUpdateToken, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermUserReadEvents, permission.Context(permission.CtxUser, email)), }) if err != nil { return err } defer func() { evt.Done(err) }() u, err := auth.GetUserByEmail(email) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } apiKey, err := u.RegenerateAPIKey() if err != nil { return err } w.Header().Add("Content-Type", "application/json") return json.NewEncoder(w).Encode(apiKey) }
// title: remove team from pool // path: /pools/{name}/team // method: DELETE // responses: // 200: Pool updated // 401: Unauthorized // 400: Invalid data // 404: Pool not found func removeTeamToPoolHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() allowed := permission.Check(t, permission.PermPoolUpdateTeamRemove) if !allowed { return permission.ErrUnauthorized } poolName := r.URL.Query().Get(":name") evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: poolName}, Kind: permission.PermPoolUpdateTeamRemove, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, poolName)), }) if err != nil { return err } defer func() { evt.Done(err) }() if teams, ok := r.URL.Query()["team"]; ok { err := provision.RemoveTeamsFromPool(poolName, teams) if err == provision.ErrPoolNotFound { return &terrors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } return err } return &terrors.HTTP{ Code: http.StatusBadRequest, Message: "You must provide the team", } }
// title: reset password // path: /users/{email}/password // method: POST // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 403: Forbidden // 404: Not found func resetPassword(w http.ResponseWriter, r *http.Request) (err error) { managed, ok := app.AuthScheme.(auth.ManagedScheme) if !ok { return &errors.HTTP{Code: http.StatusBadRequest, Message: nonManagedSchemeMsg} } r.ParseForm() email := r.URL.Query().Get(":email") token := r.FormValue("token") evt, err := event.New(&event.Opts{ Target: userTarget(email), Kind: permission.PermUserUpdateReset, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: email}, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermUserReadEvents, permission.Context(permission.CtxUser, email)), }) if err != nil { return err } defer func() { evt.Done(err) }() u, err := auth.GetUserByEmail(email) if err != nil { if err == auth.ErrUserNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } else if e, ok := err.(*errors.ValidationError); ok { return &errors.HTTP{Code: http.StatusBadRequest, Message: e.Error()} } return err } if token == "" { return managed.StartPasswordReset(u) } return managed.ResetPassword(u, token) }
// title: remove team // path: /teams/{name} // method: DELETE // responses: // 200: Team removed // 401: Unauthorized // 403: Forbidden // 404: Not found func removeTeam(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() name := r.URL.Query().Get(":name") allowed := permission.Check(t, permission.PermTeamDelete, permission.Context(permission.CtxTeam, name), ) if !allowed { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf(`Team "%s" not found.`, name)} } evt, err := event.New(&event.Opts{ Target: teamTarget(name), Kind: permission.PermTeamDelete, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermTeamReadEvents, permission.Context(permission.CtxTeam, name)), }) if err != nil { return err } defer func() { evt.Done(err) }() err = auth.RemoveTeam(name) if err != nil { if _, ok := err.(*auth.ErrTeamStillUsed); ok { msg := fmt.Sprintf("This team cannot be removed because there are still references to it:\n%s", err) return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } if err == auth.ErrTeamNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf(`Team "%s" not found.`, name)} } return err } return nil }
// title: machine destroy // path: /iaas/machines/{machine_id} // method: DELETE // responses: // 200: OK // 400: Invalid data // 401: Unauthorized // 404: Not found func machineDestroy(w http.ResponseWriter, r *http.Request, token auth.Token) (err error) { r.ParseForm() machineID := r.URL.Query().Get(":machine_id") if machineID == "" { return &errors.HTTP{Code: http.StatusBadRequest, Message: "machine id is required"} } m, err := iaas.FindMachineById(machineID) if err != nil { if err == mgo.ErrNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: "machine not found"} } return err } iaasCtx := permission.Context(permission.CtxIaaS, m.Iaas) allowed := permission.Check(token, permission.PermMachineDelete, iaasCtx) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeIaas, Value: m.Iaas}, Kind: permission.PermMachineDelete, Owner: token, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermMachineReadEvents, iaasCtx), }) if err != nil { return err } defer func() { evt.Done(err) }() return m.Destroy() }
// title: template destroy // path: /iaas/templates/{template_name} // method: DELETE // responses: // 200: OK // 401: Unauthorized // 404: Not found func templateDestroy(w http.ResponseWriter, r *http.Request, token auth.Token) (err error) { r.ParseForm() templateName := r.URL.Query().Get(":template_name") t, err := iaas.FindTemplate(templateName) if err != nil { if err == mgo.ErrNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: "template not found"} } return err } iaasCtx := permission.Context(permission.CtxIaaS, t.IaaSName) allowed := permission.Check(token, permission.PermMachineTemplateDelete, iaasCtx) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeIaas, Value: t.IaaSName}, Kind: permission.PermMachineTemplateDelete, Owner: token, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermMachineReadEvents, iaasCtx), }) if err != nil { return err } defer func() { evt.Done(err) }() return iaas.DestroyTemplate(templateName) }
// title: template create // path: /iaas/templates // method: POST // consume: application/x-www-form-urlencoded // responses: // 201: Template created // 400: Invalid data // 401: Unauthorized func templateCreate(w http.ResponseWriter, r *http.Request, token auth.Token) (err error) { err = r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var paramTemplate iaas.Template dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶mTemplate, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } iaasCtx := permission.Context(permission.CtxIaaS, paramTemplate.IaaSName) allowed := permission.Check(token, permission.PermMachineTemplateCreate, iaasCtx) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeIaas, Value: paramTemplate.IaaSName}, Kind: permission.PermMachineTemplateCreate, Owner: token, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermMachineReadEvents, iaasCtx), }) if err != nil { return err } defer func() { evt.Done(err) }() err = paramTemplate.Save() if err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
func (s *S) TestDeployAppWithUpdatePlatform(c *check.C) { a := App{ Name: "some-app", Plan: Plan{Router: "fake"}, Platform: "django", Teams: []string{s.team.Name}, UpdatePlatform: true, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) writer := &bytes.Buffer{} evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) _, err = Deploy(DeployOptions{ App: &a, Image: "myimage", Commit: "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c", OutputStream: writer, Event: evt, }) c.Assert(err, check.IsNil) logs := writer.String() c.Assert(logs, check.Equals, "Image deploy called") var updatedApp App s.conn.Apps().Find(bson.M{"name": "some-app"}).One(&updatedApp) c.Assert(updatedApp.UpdatePlatform, check.Equals, false) }
// title: service delete // path: /services/{name} // method: DELETE // responses: // 200: Service removed // 401: Unauthorized // 403: Forbidden (team is not the owner or service with instances) // 404: Service not found func serviceDelete(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() s, err := getService(r.URL.Query().Get(":name")) if err != nil { return err } allowed := permission.Check(t, permission.PermServiceDelete, contextsForServiceProvision(&s)..., ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: serviceTarget(s.Name), Kind: permission.PermServiceDelete, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermServiceReadEvents, contextsForServiceProvision(&s)...), }) if err != nil { return err } defer func() { evt.Done(err) }() instances, err := service.GetServiceInstancesByServices([]service.Service{s}) if err != nil { return err } if len(instances) > 0 { msg := "This service cannot be removed because it has instances.\n" msg += "Please remove these instances before removing the service." return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } return s.Delete() }
func (s *S) TestDeployAppSaveDeployData(c *check.C) { a := App{ Name: "otherapp", Plan: Plan{Router: "fake"}, Platform: "zend", Teams: []string{s.team.Name}, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) writer := &bytes.Buffer{} commit := "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c" evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) _, err = Deploy(DeployOptions{ App: &a, Image: "myimage", Commit: commit, OutputStream: writer, User: "******", Event: evt, }) c.Assert(err, check.IsNil) var updatedApp App s.conn.Apps().Find(bson.M{"name": a.Name}).One(&updatedApp) c.Assert(updatedApp.Deploys, check.Equals, uint(1)) }
// title: service proxy // path: /services/proxy/service/{service} // method: "*" // responses: // 401: Unauthorized // 404: Service not found func serviceProxy(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { parseFormPreserveBody(r) serviceName := r.URL.Query().Get(":service") s, err := getService(serviceName) if err != nil { return err } allowed := permission.Check(t, permission.PermServiceUpdateProxy, contextsForServiceProvision(&s)..., ) if !allowed { return permission.ErrUnauthorized } if r.Method != httpMethodGet && r.Method != httpMethodHead { evt, err := event.New(&event.Opts{ Target: serviceTarget(s.Name), Kind: permission.PermServiceUpdateProxy, Owner: t, CustomData: append(event.FormToCustomData(r.Form), map[string]interface{}{ "name": "method", "value": r.Method, }), Allowed: event.Allowed(permission.PermServiceReadEvents, contextsForServiceProvision(&s)...), }) if err != nil { return err } defer func() { evt.Done(err) }() } path := r.URL.Query().Get("callback") return service.Proxy(&s, path, w, r) }
func (s *S) TestDeployAppSaveDeployErrorData(c *check.C) { s.provisioner.PrepareFailure("ImageDeploy", errors.New("deploy error")) a := App{ Name: "testerrorapp", Platform: "zend", Teams: []string{s.team.Name}, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) writer := &bytes.Buffer{} evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) _, err = Deploy(DeployOptions{ App: &a, Image: "myimage", Commit: "1ee1f1084927b3a5db59c9033bc5c4abefb7b93c", OutputStream: writer, Event: evt, }) c.Assert(err, check.NotNil) }
// title: change service documentation // path: /services/{name}/doc // consume: application/x-www-form-urlencoded // method: PUT // responses: // 200: Documentation updated // 401: Unauthorized // 403: Forbidden (team is not the owner or service with instances) func serviceAddDoc(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { serviceName := r.URL.Query().Get(":name") s, err := getService(serviceName) if err != nil { return err } allowed := permission.Check(t, permission.PermServiceUpdateDoc, contextsForServiceProvision(&s)..., ) if !allowed { return permission.ErrUnauthorized } s.Doc = r.FormValue("doc") evt, err := event.New(&event.Opts{ Target: serviceTarget(s.Name), Kind: permission.PermServiceUpdateDoc, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermServiceReadEvents, contextsForServiceProvision(&s)...), }) if err != nil { return err } defer func() { evt.Done(err) }() return s.Update() }
func (s *S) TestRollbackWithNameImage(c *check.C) { a := App{ Name: "otherapp", Plan: Plan{Router: "fake"}, Platform: "zend", Teams: []string{s.team.Name}, TeamOwner: s.team.Name, } err := CreateApp(&a, s.user) c.Assert(err, check.IsNil) writer := &bytes.Buffer{} evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: a.Name}, Kind: permission.PermAppDeploy, RawOwner: event.Owner{Type: event.OwnerTypeUser, Name: s.user.Email}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) imgID, err := Deploy(DeployOptions{ App: &a, OutputStream: writer, Image: "registry.somewhere/tsuru/app-example:v2", Rollback: true, Event: evt, }) c.Assert(err, check.IsNil) c.Assert(writer.String(), check.Equals, "Rollback deploy called") c.Assert(imgID, check.Equals, "registry.somewhere/tsuru/app-example:v2") }
func (s *S) TestArchiveDeployCanceledEvent(c *check.C) { err := s.newFakeImage(s.p, "tsuru/python:latest", nil) c.Assert(err, check.IsNil) app := provisiontest.NewFakeApp("myapp", "python", 1) routertest.FakeRouter.AddBackend(app.GetName()) defer routertest.FakeRouter.RemoveBackend(app.GetName()) evt, err := event.New(&event.Opts{ Target: event.Target{Type: "app", Value: "myapp"}, Kind: permission.PermAppDeploy, Owner: s.token, Cancelable: true, Allowed: event.Allowed(permission.PermApp), AllowedCancel: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) done := make(chan bool) go func() { defer close(done) img, depErr := s.p.archiveDeploy(app, image.GetBuildImage(app), "https://s3.amazonaws.com/wat/archive.tar.gz", evt) c.Assert(depErr, check.ErrorMatches, "deploy canceled by user action") c.Assert(img, check.Equals, "") }() time.Sleep(100 * time.Millisecond) err = evt.TryCancel("because yes", "*****@*****.**") c.Assert(err, check.IsNil) <-done }
// title: app unlock // path: /apps/{app}/lock // method: DELETE // produce: application/json // responses: // 200: Ok // 401: Unauthorized // 404: App not found func forceDeleteLock(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppAdminUnlock, contextsForApp(&a)..., ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppAdminUnlock, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() app.ReleaseApplicationLock(a.Name) return nil }
func (s *EventSuite) TestEventInfoPermission(c *check.C) { token := customUserWithPermission(c, "myuser", permission.Permission{ Scheme: permission.PermAppRead, Context: permission.Context(permission.CtxTeam, s.team.Name), }) evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeApp, Value: "aha"}, Owner: s.token, Kind: permission.PermAppDeploy, Allowed: event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)), }) c.Assert(err, check.IsNil) u := fmt.Sprintf("/events/%s", evt.UniqueID.Hex()) request, err := http.NewRequest("GET", u, nil) c.Assert(err, check.IsNil) request.Header.Set("Authorization", "bearer "+token.GetValue()) recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, request) c.Assert(recorder.Code, check.Equals, http.StatusOK) var result event.Event err = json.Unmarshal(recorder.Body.Bytes(), &result) c.Assert(err, check.IsNil) c.Assert(result.Kind, check.DeepEquals, evt.Kind) c.Assert(result.Target, check.DeepEquals, evt.Target) }
// title: remove units // path: /apps/{name}/units // method: DELETE // produce: application/x-json-stream // responses: // 200: Units removed // 400: Invalid data // 401: Unauthorized // 404: App not found func removeUnits(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { n, err := numberOfUnits(r) if err != nil { return err } processName := r.FormValue("process") appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdateUnitRemove, contextsForApp(&a)..., ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppUpdateUnitRemove, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} return a.RemoveUnits(n, processName, writer) }
func (s *EventSuite) TestEventCancelWithoutPermission(c *check.C) { token := customUserWithPermission(c, "myuser", permission.Permission{ Scheme: permission.PermAppRead, Context: permission.Context(permission.CtxTeam, s.team.Name), }) evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeApp, Value: "anything"}, Owner: s.token, Kind: permission.PermAppDeploy, Cancelable: true, Allowed: event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)), AllowedCancel: event.Allowed(permission.PermAppUpdateEvents, permission.Context(permission.CtxTeam, "other-team")), }) c.Assert(err, check.IsNil) body := strings.NewReader("reason=we ain't gonna take it") u := fmt.Sprintf("/events/%s/cancel", evt.UniqueID.Hex()) request, err := http.NewRequest("POST", u, body) c.Assert(err, check.IsNil) request.Header.Set("Authorization", "bearer "+token.GetValue()) request.Header.Set("Content-Type", "application/x-www-form-urlencoded") recorder := httptest.NewRecorder() server := RunServer(true) server.ServeHTTP(recorder, request) c.Assert(recorder.Code, check.Equals, http.StatusForbidden) }
// title: dissociate role from user // path: /roles/{name}/user/{email} // method: DELETE // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: Role not found func dissociateRole(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() if !permission.Check(t, permission.PermRoleUpdateDissociate) { return permission.ErrUnauthorized } roleName := r.URL.Query().Get(":name") evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeRole, Value: roleName}, Kind: permission.PermRoleUpdateDissociate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermRoleReadEvents), }) if err != nil { return err } defer func() { evt.Done(err) }() email := r.URL.Query().Get(":email") contextValue := r.URL.Query().Get("context") user, err := auth.GetUserByEmail(email) if err != nil { return err } err = canUseRole(t, roleName, contextValue) if err != nil { return err } err = runWithPermSync([]auth.User{*user}, func() error { return user.RemoveRole(roleName, contextValue) }) return err }
func (s *EventSuite) insertEvents(target string, c *check.C) ([]*event.Event, error) { t, err := event.GetTargetType(target) if err != nil { return nil, err } evts := make([]*event.Event, 10) for i := 0; i < 10; i++ { name := fmt.Sprintf("app-%d", i) opts := &event.Opts{ Target: event.Target{Type: t, Value: name}, Owner: s.token, Kind: permission.PermAppDeploy, Cancelable: i == 0, } if t == event.TargetTypeApp { opts.Allowed = event.Allowed(permission.PermAppReadEvents, permission.Context(permission.CtxTeam, s.team.Name)) opts.AllowedCancel = event.Allowed(permission.PermAppUpdateEvents, permission.Context(permission.CtxTeam, s.team.Name)) } else { opts.Allowed = event.Allowed(permission.PermApp) opts.AllowedCancel = event.Allowed(permission.PermApp) } evt, err := event.New(opts) c.Assert(err, check.IsNil) if i == 1 { err = evt.Done(nil) c.Assert(err, check.IsNil) } evts[i] = evt } return evts, nil }
// title: update application quota // path: /apps/{appname}/quota // method: PUT // consume: application/x-www-form-urlencoded // responses: // 200: Quota updated // 400: Invalid data // 401: Unauthorized // 404: Application not found func changeAppQuota(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() appName := r.URL.Query().Get(":appname") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppAdminQuota, contextsForApp(&a)...) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeApp, Value: appName}, Kind: permission.PermAppAdminQuota, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() limit, err := strconv.Atoi(r.FormValue("limit")) if err != nil { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid limit", } } return app.ChangeQuota(&a, limit) }
// title: autoscale run // path: /docker/autoscale/run // method: POST // produce: application/x-json-stream // responses: // 200: Ok // 401: Unauthorized func autoScaleRunHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() if !permission.Check(t, permission.PermNodeAutoscaleUpdateRun) { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool}, Kind: permission.PermNodeAutoscaleUpdateRun, Owner: t, CustomData: event.FormToCustomData(r.Form), DisableLock: true, Allowed: event.Allowed(permission.PermPoolReadEvents), }) if err != nil { return err } defer func() { evt.Done(err) }() w.Header().Set("Content-Type", "application/x-json-stream") w.WriteHeader(http.StatusOK) keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{ Encoder: json.NewEncoder(keepAliveWriter), } autoScaleConfig := mainDockerProvisioner.initAutoScaleConfig() autoScaleConfig.writer = writer return autoScaleConfig.runOnce() }