func ExampleLog() { // logging and waiting ch := rec.Log("*****@*****.**", "action", "arg1", 10, true) <-ch // logging without blocking rec.Log("*****@*****.**", "action-2", "arg1", 10, true) // logging and checking for errors ch = rec.Log("*****@*****.**", "action-3", "arg1", 10, true) if err, ok := <-ch; ok { panic(err) } }
func revokeServiceAccess(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } serviceName := r.URL.Query().Get(":service") teamName := r.URL.Query().Get(":team") rec.Log(u.Email, "revoke-service-access", "service="+serviceName, "team="+teamName) service, team, err := getServiceAndTeam(serviceName, teamName, u) if err != nil { return err } if len(service.Teams) < 2 { msg := "You can not revoke the access from this team, because it is the unique team with access to this service, and a service can not be orphaned" return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } err = service.RevokeAccess(team) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } conn, err := db.Conn() if err != nil { return err } return conn.Services().Update(bson.M{"_id": service.Name}, service) }
func addUserToTeam(w http.ResponseWriter, r *http.Request, t *auth.Token) error { teamName := r.URL.Query().Get(":team") email := r.URL.Query().Get(":user") u, err := t.User() if err != nil { return err } rec.Log(u.Email, "add-user-to-team", "team="+teamName, "user="******"Team not found"} } if !team.ContainsUser(u) { msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name) return &errors.HTTP{Code: http.StatusUnauthorized, Message: msg} } user, err := auth.GetUserByEmail(email) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: "User not found"} } actions := []*action.Action{ &addUserToTeamInGandalfAction, &addUserToTeamInDatabaseAction, } pipeline := action.NewPipeline(actions...) return pipeline.Execute(user.Email, u, team) }
func setCName(w http.ResponseWriter, r *http.Request, t *auth.Token) error { msg := "You must provide the cname." if r.Body == nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } var v map[string]string err := json.NewDecoder(r.Body).Decode(&v) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: "Invalid JSON in request body."} } if _, ok := v["cname"]; !ok { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") rec.Log(u.Email, "set-cname", "app="+appName, "cname="+v["cname"]) app, err := getApp(appName, u) if err != nil { return err } if err = app.SetCName(v["cname"]); err == nil { return nil } if err.Error() == "Invalid cname" { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } return err }
func serviceDelete(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "delete-service", r.URL.Query().Get(":name")) s, err := getServiceByOwner(r.URL.Query().Get(":name"), u) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } n, err := conn.ServiceInstances().Find(bson.M{"service_name": s.Name}).Count() if err != nil { return err } if n > 0 { msg := "This service cannot be removed because it has instances.\nPlease remove these instances before removing the service." return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } err = s.Delete() if err != nil { return err } w.WriteHeader(http.StatusNoContent) return nil }
func setEnv(w http.ResponseWriter, r *http.Request, t *auth.Token) error { msg := "You must provide the environment variables in a JSON object" if r.Body == nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } var variables map[string]string err := json.NewDecoder(r.Body).Decode(&variables) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") rec.Log(u.Email, "set-env", "app="+appName, variables) app, err := getApp(appName, u) if err != nil { return err } envs := make([]bind.EnvVar, 0, len(variables)) for k, v := range variables { envs = append(envs, bind.EnvVar{Name: k, Value: v, Public: true}) } return app.SetEnvs(envs, true) }
func unsetEnv(w http.ResponseWriter, r *http.Request, t *auth.Token) error { msg := "You must provide the list of environment variables, in JSON format" if r.Body == nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } var variables []string defer r.Body.Close() err := json.NewDecoder(r.Body).Decode(&variables) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } if len(variables) == 0 { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } appName := r.URL.Query().Get(":app") u, err := t.User() if err != nil { return err } rec.Log(u.Email, "unset-env", "app="+appName, fmt.Sprintf("envs=%s", variables)) app, err := getApp(appName, u) if err != nil { return err } return app.UnsetEnvs(variables, true) }
func teamList(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "list-teams") teams, err := u.Teams() if err != nil { return err } if len(teams) > 0 { var result []map[string]string for _, team := range teams { result = append(result, map[string]string{"name": team.Name}) } b, err := json.Marshal(result) if err != nil { return err } n, err := w.Write(b) if err != nil { return err } if n != len(b) { return &errors.HTTP{Code: http.StatusInternalServerError, Message: "Failed to write response body."} } } else { w.WriteHeader(http.StatusNoContent) } return nil }
func runCommand(w http.ResponseWriter, r *http.Request, t *auth.Token) error { w.Header().Set("Content-Type", "text") msg := "You must provide the command to run" if r.Body == nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } c, err := ioutil.ReadAll(r.Body) if err != nil { return err } if len(c) < 1 { return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") once := r.URL.Query().Get("once") rec.Log(u.Email, "run-command", "app="+appName, "command="+string(c)) app, err := getApp(appName, u) if err != nil { return err } return app.Run(string(c), w, once == "true") }
func removeUserFromTeam(w http.ResponseWriter, r *http.Request, t *auth.Token) error { email := r.URL.Query().Get(":user") teamName := r.URL.Query().Get(":team") u, err := t.User() if err != nil { return err } rec.Log(u.Email, "remove-user-from-team", "team="+teamName, "user="******"Team not found"} } if !team.ContainsUser(u) { msg := fmt.Sprintf("You are not authorized to remove a member from the team %s", team.Name) return &errors.HTTP{Code: http.StatusUnauthorized, Message: msg} } if len(team.Users) == 1 { msg := "You can not remove this user from this team, because it is the last user within the team, and a team can not be orphaned" return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } user, err := auth.GetUserByEmail(email) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } err = removeUserFromTeamInGandalf(user, team.Name) if err != nil { return nil } return removeUserFromTeamInDatabase(user, team) }
func createTeam(w http.ResponseWriter, r *http.Request, t *auth.Token) error { var params map[string]string err := json.NewDecoder(r.Body).Decode(¶ms) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } name, ok := params["name"] if !ok { msg := "You must provide the team name" return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } u, err := t.User() if err != nil { return err } rec.Log(u.Email, "create-team", name) conn, err := db.Conn() if err != nil { return err } defer conn.Close() team := &auth.Team{Name: name, Users: []string{u.Email}} if err := conn.Teams().Insert(team); err != nil && strings.Contains(err.Error(), "duplicate key error") { msg := "This team already exists" return &errors.HTTP{Code: http.StatusConflict, Message: msg} } return nil }
func serviceInstances(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "list-service-instances") services, _ := service.GetServicesByTeamKindAndNoRestriction("teams", u) sInstances, _ := service.GetServiceInstancesByServicesAndTeams(services, u) result := make([]service.ServiceModel, len(services)) for i, s := range services { result[i].Service = s.Name for _, si := range sInstances { if si.ServiceName == s.Name { result[i].Instances = append(result[i].Instances, si.Name) } } } body, err := json.Marshal(result) if err != nil { return err } n, err := w.Write(body) if n != len(body) { return &errors.HTTP{Code: http.StatusInternalServerError, Message: "Failed to write the response body."} } return err }
func createUser(w http.ResponseWriter, r *http.Request) error { var u auth.User err := json.NewDecoder(r.Body).Decode(&u) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } if !validation.ValidateEmail(u.Email) { return &errors.HTTP{Code: http.StatusBadRequest, Message: emailError} } if !validation.ValidateLength(u.Password, passwordMinLen, passwordMaxLen) { return &errors.HTTP{Code: http.StatusBadRequest, Message: passwordError} } gURL := repository.ServerURL() c := gandalf.Client{Endpoint: gURL} if _, err := c.NewUser(u.Email, keyToMap(u.Keys)); err != nil { return fmt.Errorf("Failed to create user in the git server: %s", err) } if err := u.Create(); err == nil { rec.Log(u.Email, "create-user") if limit, err := config.GetUint("quota:apps-per-user"); err == nil { quota.Create(u.Email, uint(limit)) } w.WriteHeader(http.StatusCreated) return nil } if _, err = auth.GetUserByEmail(u.Email); err == nil { err = &errors.HTTP{Code: http.StatusConflict, Message: "This email is already registered"} } return err }
func serviceInfo(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } serviceName := r.URL.Query().Get(":name") rec.Log(u.Email, "service-info", serviceName) _, err = getServiceOrError(serviceName, u) if err != nil { return err } instances := []service.ServiceInstance{} teams, err := u.Teams() if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() teamsNames := auth.GetTeamsNames(teams) q := bson.M{"service_name": serviceName, "teams": bson.M{"$in": teamsNames}} err = conn.ServiceInstances().Find(q).All(&instances) if err != nil { return err } b, err := json.Marshal(instances) if err != nil { return nil } w.Write(b) return nil }
func appLog(w http.ResponseWriter, r *http.Request, t *auth.Token) error { var err error var lines int if l := r.URL.Query().Get("lines"); l != "" { lines, err = strconv.Atoi(l) if err != nil { msg := `Parameter "lines" must be an integer.` return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } } else { return &errors.HTTP{Code: http.StatusBadRequest, Message: `Parameter "lines" is mandatory.`} } w.Header().Set("Content-Type", "application/json") source := r.URL.Query().Get("source") u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") extra := []interface{}{ "app=" + appName, fmt.Sprintf("lines=%d", lines), } if source != "" { extra = append(extra, "source="+source) } if r.URL.Query().Get("follow") == "1" { extra = append(extra, "follow=1") } rec.Log(u.Email, "app-log", extra...) a, err := getApp(appName, u) if err != nil { return err } logs, err := a.LastLogs(lines, source) if err != nil { return err } encoder := json.NewEncoder(w) err = encoder.Encode(logs) if err != nil { return err } // TODO(fss): write an automated test for this code. if r.URL.Query().Get("follow") == "1" { l := app.NewLogListener(&a) defer l.Close() for log := range l.C { err := encoder.Encode([]app.Applog{log}) if err != nil { break } } } return nil }
func resetPassword(w http.ResponseWriter, r *http.Request) error { email := r.URL.Query().Get(":email") token := r.URL.Query().Get("token") 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 == "" { rec.Log(email, "reset-password-gen-token") return u.StartPasswordReset() } rec.Log(email, "reset-password") return u.ResetPassword(token) }
func platformList(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "platform-list") platforms, err := app.Platforms() if err != nil { return err } return json.NewEncoder(w).Encode(platforms) }
func serviceCreate(w http.ResponseWriter, r *http.Request, t *auth.Token) error { defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return err } var sy serviceYaml err = goyaml.Unmarshal(body, &sy) if err != nil { return err } if _, ok := sy.Endpoint["production"]; !ok { return &errors.HTTP{Code: http.StatusBadRequest, Message: "You must provide a production endpoint in the manifest file."} } u, err := t.User() if err != nil { return err } rec.Log(u.Email, "create-service", sy.Id, sy.Endpoint) conn, err := db.Conn() if err != nil { return err } defer conn.Close() var teams []auth.Team err = conn.Teams().Find(bson.M{"users": u.Email}).All(&teams) if err != nil { return err } if len(teams) == 0 { msg := "In order to create a service, you should be member of at least one team" return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } n, err := conn.Services().Find(bson.M{"_id": sy.Id}).Count() if err != nil { return &errors.HTTP{Code: http.StatusInternalServerError, Message: err.Error()} } if n != 0 { msg := fmt.Sprintf("Service with name %s already exists.", sy.Id) return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg} } s := service.Service{ Name: sy.Id, Endpoint: sy.Endpoint, OwnerTeams: auth.GetTeamsNames(teams), } err = s.Create() if err != nil { return err } fmt.Fprint(w, "success") return nil }
func createApp(w http.ResponseWriter, r *http.Request, t *auth.Token) error { var a app.App var japp jsonApp defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if err = json.Unmarshal(body, &japp); err != nil { return err } a.Name = japp.Name a.Platform = japp.Platform if japp.Units == 0 { japp.Units = 1 } u, err := t.User() if err != nil { return err } rec.Log(u.Email, "create-app", "name="+japp.Name, "platform="+japp.Platform, fmt.Sprintf("units=%d", japp.Units)) teams, err := u.Teams() if err != nil { return err } if len(teams) < 1 { msg := "In order to create an app, you should be member of at least one team" return &errors.Http{Code: http.StatusForbidden, Message: msg} } err = app.CreateApp(&a, japp.Units, teams) if err != nil { log.Printf("Got error while creating app: %s", err) if e, ok := err.(*errors.ValidationError); ok { return &errors.Http{Code: http.StatusBadRequest, Message: e.Message} } if strings.Contains(err.Error(), "key error") { msg := fmt.Sprintf(`There is already an app named "%s".`, a.Name) return &errors.Http{Code: http.StatusConflict, Message: msg} } return err } msg := map[string]string{ "status": "success", "repository_url": repository.GetUrl(a.Name), } jsonMsg, err := json.Marshal(msg) if err != nil { return err } fmt.Fprintf(w, "%s", jsonMsg) return nil }
func unbindServiceInstance(w http.ResponseWriter, r *http.Request, t *auth.Token) error { instanceName, appName := r.URL.Query().Get(":instance"), r.URL.Query().Get(":app") u, err := t.User() if err != nil { return err } instance, a, err := getServiceInstance(instanceName, appName, u) if err != nil { return err } rec.Log(u.Email, "unbind-app", "instance="+instanceName, "app="+appName) return instance.UnbindApp(a) }
func unsetCName(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") app, err := getApp(appName, u) if err != nil { return err } rec.Log(u.Email, "unset-cname", "app="+appName) return app.UnsetCName() }
func createApp(w http.ResponseWriter, r *http.Request, t *auth.Token) error { var a app.App defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if err = json.Unmarshal(body, &a); err != nil { return err } u, err := t.User() if err != nil { return err } rec.Log(u.Email, "create-app", "name="+a.Name, "platform="+a.Platform) err = app.CreateApp(&a, u) if err != nil { log.Errorf("Got error while creating app: %s", err) if e, ok := err.(*errors.ValidationError); ok { return &errors.HTTP{Code: http.StatusBadRequest, Message: e.Message} } if _, ok := err.(app.NoTeamsError); ok { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "In order to create an app, you should be member of at least one team", } } if e, ok := err.(*app.AppCreationError); ok { if e.Err == app.ErrAppAlreadyExists { return &errors.HTTP{Code: http.StatusConflict, Message: e.Error()} } if _, ok := e.Err.(*quota.QuotaExceededError); ok { return &errors.HTTP{ Code: http.StatusForbidden, Message: "Quota exceeded", } } } return err } msg := map[string]string{ "status": "success", "repository_url": repository.ReadWriteURL(a.Name), } jsonMsg, err := json.Marshal(msg) if err != nil { return err } fmt.Fprintf(w, "%s", jsonMsg) return nil }
func serviceDoc(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } sName := r.URL.Query().Get(":name") rec.Log(u.Email, "service-doc", sName) s, err := getServiceOrError(sName, u) if err != nil { return err } w.Write([]byte(s.Doc)) return nil }
func appDelete(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "app-delete", r.URL.Query().Get(":app")) a, err := getApp(r.URL.Query().Get(":app"), u) if err != nil { return err } app.Delete(&a) fmt.Fprint(w, "success") return nil }
func restart(w http.ResponseWriter, r *http.Request, t *auth.Token) error { w.Header().Set("Content-Type", "text") u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") rec.Log(u.Email, "restart", appName) instance, err := getApp(appName, u) if err != nil { return err } return instance.Restart(w) }
// removeUser removes the user from the database and from gandalf server // // If the user is the only one in a team an error will be returned. func removeUser(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } gURL := repository.ServerURL() c := gandalf.Client{Endpoint: gURL} alwdApps, err := u.AllowedApps() if err != nil { return err } if err := c.RevokeAccess(alwdApps, []string{u.Email}); err != nil { log.Printf("Failed to revoke access in Gandalf: %s", err) return fmt.Errorf("Failed to revoke acess from git repositories: %s", err) } teams, err := u.Teams() if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() for _, team := range teams { if len(team.Users) < 2 { msg := fmt.Sprintf(`This user is the last member of the team "%s", so it cannot be removed. Please remove the team, them remove the user.`, team.Name) return &errors.HTTP{Code: http.StatusForbidden, Message: msg} } err = team.RemoveUser(u) if err != nil { return err } // this can be done without the loop err = conn.Teams().Update(bson.M{"_id": team.Name}, team) if err != nil { return err } } rec.Log(u.Email, "remove-user") if err := c.RemoveUser(u.Email); err != nil { log.Printf("Failed to remove user from gandalf: %s", err) return fmt.Errorf("Failed to remove the user from the git server: %s", err) } quota.Delete(u.Email) return conn.Users().Remove(bson.M{"email": u.Email}) }
func appList(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "app-list") apps, err := app.List(u) if err != nil { return err } if len(apps) == 0 { w.WriteHeader(http.StatusNoContent) return nil } return json.NewEncoder(w).Encode(apps) }
func getTeam(w http.ResponseWriter, r *http.Request, t *auth.Token) error { teamName := r.URL.Query().Get(":name") user, err := t.User() if err != nil { return err } rec.Log(user.Email, "get-team", teamName) team, err := auth.GetTeam(teamName) if err != nil { return &errors.HTTP{Code: http.StatusNotFound, Message: "Team not found"} } if !team.ContainsUser(user) { return &errors.HTTP{Code: http.StatusForbidden, Message: "User is not member of this team"} } w.Header().Set("Content-Type", "application/json") return json.NewEncoder(w).Encode(team) }
func appInfo(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } rec.Log(u.Email, "app-info", r.URL.Query().Get(":app")) app, err := getApp(r.URL.Query().Get(":app"), u) if err != nil { return err } host, err := config.GetString("host") if err != nil { return err } w.Header().Set("Content-Type", "application/json; profile="+host+"/schema/app") return json.NewEncoder(w).Encode(&app) }
func removeUnits(w http.ResponseWriter, r *http.Request, t *auth.Token) error { n, err := numberOfUnits(r) if err != nil { return err } u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") rec.Log(u.Email, "remove-units", "app="+appName, fmt.Sprintf("units=%d", n)) app, err := getApp(appName, u) if err != nil { return err } return app.RemoveUnits(uint(n)) }