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: rollback // path: /apps/{appname}/deploy/rollback // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: OK // 400: Invalid data // 403: Forbidden // 404: Not found func deployRollback(w http.ResponseWriter, r *http.Request, t auth.Token) error { appName := r.URL.Query().Get(":appname") instance, err := app.GetByName(appName) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} } image := r.FormValue("image") if image == "" { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "you cannot rollback without an image name", } } origin := r.FormValue("origin") if origin != "" { if !app.ValidateOrigin(origin) { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "Invalid deployment origin", } } } w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} opts := app.DeployOptions{ App: instance, OutputStream: writer, Image: image, User: t.GetUserName(), Origin: origin, Rollback: true, } opts.GetKind() canRollback := permission.Check(t, permSchemeForDeploy(opts), contextsForApp(instance)...) if !canRollback { return &tsuruErrors.HTTP{Code: http.StatusForbidden, Message: permission.ErrUnauthorized.Error()} } var imageID string evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppDeploy, Owner: t, 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 imageID, err = app.Deploy(opts) if err != nil { writer.Encode(io.SimpleJsonMessage{Error: err.Error()}) } return nil }
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 }
func deployDataToEvent(data *DeployData) error { var evt event.Event evt.UniqueID = data.ID evt.Target = event.Target{Type: event.TargetTypeApp, Value: data.App} evt.Owner = event.Owner{Type: event.OwnerTypeUser, Name: data.User} evt.Kind = event.Kind{Type: event.KindTypePermission, Name: permission.PermAppDeploy.FullName()} evt.StartTime = data.Timestamp evt.EndTime = data.Timestamp.Add(data.Duration) evt.Error = data.Error evt.Log = data.Log evt.RemoveDate = data.RemoveDate a, err := GetByName(data.App) if err == nil { evt.Allowed = event.Allowed(permission.PermAppReadEvents, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )...) } else { evt.Allowed = event.Allowed(permission.PermAppReadEvents) } startOpts := DeployOptions{ Commit: data.Commit, Origin: data.Origin, } var otherData map[string]string if data.Diff != "" { otherData = map[string]string{"diff": data.Diff} } endData := map[string]string{"image": data.Image} err = evt.RawInsert(startOpts, otherData, endData) if mgo.IsDup(err) { return nil } return err }
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) }
func (s *HandlersSuite) TestHealingHistoryHandler(c *check.C) { evt1, err := event.NewInternal(&event.Opts{ Target: event.Target{Type: event.TargetTypeNode, Value: "addr1"}, InternalKind: "healer", CustomData: map[string]interface{}{"node": cluster.Node{Address: "addr1"}}, Allowed: event.Allowed(permission.PermPool), }) c.Assert(err, check.IsNil) evt1.DoneCustomData(nil, cluster.Node{Address: "addr2"}) time.Sleep(10 * time.Millisecond) evt2, err := event.NewInternal(&event.Opts{ Target: event.Target{Type: event.TargetTypeNode, Value: "addr3"}, InternalKind: "healer", CustomData: map[string]interface{}{"node": cluster.Node{Address: "addr3"}}, Allowed: event.Allowed(permission.PermPool), }) c.Assert(err, check.IsNil) evt2.DoneCustomData(errors.New("some error"), cluster.Node{}) time.Sleep(10 * time.Millisecond) evt3, err := event.NewInternal(&event.Opts{ Target: event.Target{Type: event.TargetTypeContainer, Value: "1234"}, InternalKind: "healer", CustomData: container.Container{ID: "1234"}, Allowed: event.Allowed(permission.PermApp), }) c.Assert(err, check.IsNil) evt3.DoneCustomData(nil, container.Container{ID: "9876"}) recorder := httptest.NewRecorder() request, err := http.NewRequest("GET", "/docker/healing", nil) c.Assert(err, check.IsNil) request.Header.Set("Authorization", "bearer "+s.token.GetValue()) server := api.RunServer(true) server.ServeHTTP(recorder, request) c.Assert(recorder.Code, check.Equals, http.StatusOK) c.Assert(recorder.Header().Get("Content-Type"), check.Equals, "application/json") body := recorder.Body.Bytes() var healings []healer.HealingEvent err = json.Unmarshal(body, &healings) c.Assert(err, check.IsNil) c.Assert(healings, check.HasLen, 3) c.Assert(healings[2].StartTime.UTC().Format(time.Stamp), check.Equals, evt1.StartTime.UTC().Format(time.Stamp)) c.Assert(healings[2].EndTime.UTC().Format(time.Stamp), check.Equals, evt1.EndTime.UTC().Format(time.Stamp)) c.Assert(healings[2].FailingNode.Address, check.Equals, "addr1") c.Assert(healings[2].CreatedNode.Address, check.Equals, "addr2") c.Assert(healings[2].Error, check.Equals, "") c.Assert(healings[2].Successful, check.Equals, true) c.Assert(healings[2].Action, check.Equals, "node-healing") c.Assert(healings[1].FailingNode.Address, check.Equals, "addr3") c.Assert(healings[1].CreatedNode.Address, check.Equals, "") c.Assert(healings[1].Error, check.Equals, "some error") c.Assert(healings[1].Successful, check.Equals, false) c.Assert(healings[1].Action, check.Equals, "node-healing") c.Assert(healings[0].FailingContainer.ID, check.Equals, "1234") c.Assert(healings[0].CreatedContainer.ID, check.Equals, "9876") c.Assert(healings[0].Successful, check.Equals, true) c.Assert(healings[0].Error, check.Equals, "") c.Assert(healings[0].Action, check.Equals, "container-healing") }
func healingEventToEvent(data *HealingEvent) error { var evt event.Event evt.UniqueID = data.ID.(bson.ObjectId) var startOpts, endOpts interface{} switch data.Action { case "node-healing": evt.Target = event.Target{Type: event.TargetTypeNode, Value: data.FailingNode.Address} var lastCheck *healer.NodeChecks if data.Extra != nil { checkRaw, err := json.Marshal(data.Extra) if err == nil { json.Unmarshal(checkRaw, &lastCheck) } } startOpts = healer.NodeHealerCustomData{ Node: data.FailingNode, Reason: data.Reason, LastCheck: lastCheck, } endOpts = data.CreatedNode poolName := data.FailingNode.Metadata["pool"] evt.Allowed = event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, poolName)) case "container-healing": evt.Target = event.Target{Type: event.TargetTypeContainer, Value: data.FailingContainer.ID} startOpts = data.FailingContainer endOpts = data.CreatedContainer a, err := app.GetByName(data.FailingContainer.AppName) if err == nil { evt.Allowed = event.Allowed(permission.PermAppReadEvents, append(permission.Contexts(permission.CtxTeam, a.Teams), permission.Context(permission.CtxApp, a.Name), permission.Context(permission.CtxPool, a.Pool), )...) } else { evt.Allowed = event.Allowed(permission.PermAppReadEvents) } default: return errors.Errorf("invalid action %q", data.Action) } evt.Owner = event.Owner{Type: event.OwnerTypeInternal} evt.Kind = event.Kind{Type: event.KindTypeInternal, Name: "healer"} evt.StartTime = data.StartTime evt.EndTime = data.EndTime evt.Error = data.Error err := evt.RawInsert(startOpts, nil, endOpts) if mgo.IsDup(err) { return nil } return err }
func (s *HandlersSuite) TestAutoScaleHistoryHandler(c *check.C) { evt1, err := event.NewInternal(&event.Opts{ Target: event.Target{Type: poolMetadataName, Value: "poolx"}, InternalKind: autoScaleEventKind, Allowed: event.Allowed(permission.PermPool), }) c.Assert(err, check.IsNil) evt1.Logf("my evt1") err = evt1.DoneCustomData(nil, evtCustomData{ Result: &scalerResult{ToAdd: 1, Reason: "r1"}, }) c.Assert(err, check.IsNil) time.Sleep(100 * time.Millisecond) evt2, err := event.NewInternal(&event.Opts{ Target: event.Target{Type: poolMetadataName, Value: "pooly"}, InternalKind: autoScaleEventKind, Allowed: event.Allowed(permission.PermPool), }) c.Assert(err, check.IsNil) evt2.Logf("my evt2") err = evt2.DoneCustomData(nil, evtCustomData{ Result: &scalerResult{ToRebalance: true, Reason: "r2"}, }) c.Assert(err, check.IsNil) recorder := httptest.NewRecorder() request, err := http.NewRequest("GET", "/docker/autoscale", nil) c.Assert(err, check.IsNil) request.Header.Set("Authorization", "bearer "+s.token.GetValue()) server := api.RunServer(true) server.ServeHTTP(recorder, request) c.Assert(recorder.Code, check.Equals, http.StatusOK) c.Assert(recorder.Header().Get("Content-Type"), check.Equals, "application/json") body := recorder.Body.Bytes() history := []autoScaleEvent{} err = json.Unmarshal(body, &history) c.Assert(err, check.IsNil) c.Assert(history, check.HasLen, 2) c.Assert(evt1.StartTime.Sub(history[1].StartTime) < time.Second, check.Equals, true) c.Assert(evt2.StartTime.Sub(history[0].StartTime) < time.Second, check.Equals, true) c.Assert(history[1].MetadataValue, check.Equals, "poolx") c.Assert(history[0].MetadataValue, check.Equals, "pooly") c.Assert(history[1].Action, check.Equals, "add") c.Assert(history[0].Action, check.Equals, "rebalance") c.Assert(history[1].Reason, check.Equals, "r1") c.Assert(history[0].Reason, check.Equals, "r2") c.Assert(history[1].Log, check.Equals, "my evt1\n") c.Assert(history[0].Log, check.Equals, "my evt2\n") }
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) 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) }
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) }
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) 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) }
// 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) }
// 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) }
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: 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: 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) 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: 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 }
// 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 }
// 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: 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) }
// 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() }
// 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 *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: 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: 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) }