func (l *lockUpdater) spin() { set := map[Target]struct{}{} for { select { case added := <-l.addCh: set[*added] = struct{}{} case removed := <-l.removeCh: delete(set, *removed) case <-l.stopCh: return case <-time.After(lockUpdateInterval): } conn, err := db.Conn() if err != nil { log.Errorf("[events] [lock update] error getting db conn: %s", err) continue } coll := conn.Events() slice := make([]interface{}, len(set)) i := 0 for id := range set { slice[i], _ = id.GetBSON() i++ } err = coll.Update(bson.M{"_id": bson.M{"$in": slice}}, bson.M{"$set": bson.M{"lockupdatetime": time.Now().UTC()}}) if err != nil && err != mgo.ErrNotFound { log.Errorf("[events] [lock update] error updating: %s", err) } conn.Close() } }
func (s *WriterSuite) SetUpSuite(c *gocheck.C) { var err error config.Set("database:url", "127.0.0.1:27017") config.Set("database:name", "tsuru_api_writer_test") s.conn, err = db.Conn() c.Assert(err, gocheck.IsNil) }
// UnbindUnit makes the unbind between the service instance and an unit. func (si *ServiceInstance) UnbindUnit(app bind.App, unit bind.Unit) error { endpoint, err := si.Service().getClient("production") if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() updateOp := bson.M{"$pull": bson.M{"units": unit.GetID()}} err = conn.ServiceInstances().Update(bson.M{"name": si.Name, "service_name": si.ServiceName, "units": unit.GetID()}, updateOp) if err != nil { if err == mgo.ErrNotFound { return ErrUnitNotBound } return err } err = endpoint.UnbindUnit(si, app, unit) if err != nil { rollbackErr := si.update(bson.M{"$addToSet": bson.M{"units": unit.GetID()}}) if rollbackErr != nil { log.Errorf("[unbind unit] could not add bound unit back to db after failure: %s", rollbackErr) } return err } return nil }
// Same as AcquireApplicationLock but it keeps trying to acquire the lock // until timeout is reached. func AcquireApplicationLockWait(appName string, owner string, reason string, timeout time.Duration) (bool, error) { timeoutChan := time.After(timeout) conn, err := db.Conn() if err != nil { return false, err } defer conn.Close() for { appLock := AppLock{ Locked: true, Reason: reason, Owner: owner, AcquireDate: time.Now().In(time.UTC), } err = conn.Apps().Update(bson.M{"name": appName, "lock.locked": bson.M{"$in": []interface{}{false, nil}}}, bson.M{"$set": bson.M{"lock": appLock}}) if err == nil { return true, nil } if err != mgo.ErrNotFound { return false, err } select { case <-timeoutChan: return false, nil case <-time.After(300 * time.Millisecond): } } }
// setEnvsToApp adds environment variables to an app, serializing the resulting // list of environment variables in all units of apps. This method can // serialize them directly or using a queue. // // Besides the slice of environment variables, this method also takes two other // parameters: publicOnly indicates whether only public variables can be // overridden (if set to false, setEnvsToApp may override a private variable). // // shouldRestart defines if the server should be restarted after saving vars. func (app *App) setEnvsToApp(envs []bind.EnvVar, publicOnly, shouldRestart bool, w io.Writer) error { if len(envs) == 0 { return nil } if w != nil { fmt.Fprintf(w, "---- Setting %d new environment variables ----\n", len(envs)) } for _, env := range envs { set := true if publicOnly { e, err := app.getEnv(env.Name) if err == nil && !e.Public && e.InstanceName != "" { set = false } } if set { app.setEnv(env) } } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$set": bson.M{"env": app.Env}}) if err != nil { return err } if !shouldRestart { return nil } return Provisioner.Restart(app, "", w) }
// AddCName adds a CName to app. It updates the attribute, // calls the SetCName function on the provisioner and saves // the app in the database, returning an error when it cannot save the change // in the database or add the CName on the provisioner. func (app *App) AddCName(cnames ...string) error { for _, cname := range cnames { if cname != "" && !cnameRegexp.MatchString(cname) { return stderr.New("Invalid cname") } if cnameExists(cname) { return stderr.New("cname already exists!") } if s, ok := Provisioner.(provision.CNameManager); ok { if err := s.SetCName(app, cname); err != nil { return err } } conn, err := db.Conn() if err != nil { return err } defer conn.Close() app.CName = append(app.CName, cname) err = conn.Apps().Update( bson.M{"name": app.Name}, bson.M{"$push": bson.M{"cname": cname}}, ) if err != nil { return err } } return nil }
// List returns the list of apps that the given user has access to. // // If the user does not have access to any app, this function returns an empty // list and a nil error. // // The list can be filtered through the filter parameter. func List(u *auth.User, filter *Filter) ([]App, error) { var apps []App conn, err := db.Conn() if err != nil { return nil, err } defer conn.Close() query := filter.Query() if u == nil || u.IsAdmin() { if err = conn.Apps().Find(query).All(&apps); err != nil { return []App{}, err } return apps, nil } ts, err := u.Teams() if err != nil { return []App{}, err } teams := auth.GetTeamsNames(ts) query["teams"] = bson.M{"$in": teams} if err := conn.Apps().Find(query).All(&apps); err != nil { return []App{}, err } return apps, nil }
// ReserveApp reserves an app for the user, reserving it in the database. It's // used to reserve the app in the user quota, returning an error when there // isn't any space available. func ReserveApp(user *User) error { user, err := checkUser(user.Email) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Users().Update( bson.M{"email": user.Email, "quota.inuse": user.InUse}, bson.M{"$inc": bson.M{"quota.inuse": 1}}, ) for err == mgo.ErrNotFound { user, err = checkUser(user.Email) if err != nil { return err } err = conn.Users().Update( bson.M{"email": user.Email, "quota.inuse": user.InUse}, bson.M{"$inc": bson.M{"quota.inuse": 1}}, ) } return err }
// ReleaseApp releases an app from the user list, releasing the quota spot for // another app. func ReleaseApp(user *User) error { errCantRelease := errors.New("Cannot release unreserved app") user, err := GetUserByEmail(user.Email) if err != nil { return err } if user.Quota.InUse == 0 { return errCantRelease } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Users().Update( bson.M{"email": user.Email, "quota.inuse": user.InUse}, bson.M{"$inc": bson.M{"quota.inuse": -1}}, ) for err == mgo.ErrNotFound { user, err = GetUserByEmail(user.Email) if err != nil { return err } if user.Quota.InUse == 0 { return errCantRelease } err = conn.Users().Update( bson.M{"email": user.Email, "quota.inuse": user.InUse}, bson.M{"$inc": bson.M{"quota.inuse": -1}}, ) } 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 } defer conn.Close() 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 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 } defer conn.Close() return conn.Services().Update(bson.M{"_id": service.Name}, service) }
func (s *S) TestContainerRemoveStopsContainer(c *check.C) { conn, err := db.Conn() c.Assert(err, check.IsNil) defer conn.Close() a := app.App{Name: "test-app"} err = conn.Apps().Insert(a) c.Assert(err, check.IsNil) container, err := s.newContainer(newContainerOpts{AppName: a.Name}, nil) c.Assert(err, check.IsNil) defer s.removeTestContainer(container) err = container.Start(&StartArgs{Provisioner: s.p, App: &a}) c.Assert(err, check.IsNil) err = container.Remove(s.p) c.Assert(err, check.IsNil) coll := s.p.Collection() defer coll.Close() err = coll.Find(bson.M{"id": container.ID}).One(&container) c.Assert(err, check.NotNil) c.Assert(err.Error(), check.Equals, "not found") client, _ := docker.NewClient(s.server.URL()) _, err = client.InspectContainer(container.ID) c.Assert(err, check.NotNil) _, ok := err.(*docker.NoSuchContainer) c.Assert(ok, check.Equals, true) }
func (u *User) AddRole(roleName string, contextValue string) error { _, err := permission.FindRole(roleName) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Users().Update(bson.M{"email": u.Email}, bson.M{ "$addToSet": bson.M{ // Order matters in $addToSet, that's why bson.D is used instead // of bson.M. "roles": bson.D([]bson.DocElem{ {Name: "name", Value: roleName}, {Name: "contextvalue", Value: contextValue}, }), }, }) if err != nil { return err } return u.Reload() }
func (u *User) Create() error { conn, err := db.Conn() if err != nil { return err } defer conn.Close() if u.Quota.Limit == 0 { u.Quota = quota.Unlimited var limit int if limit, err = config.GetInt("quota:apps-per-user"); err == nil && limit > -1 { u.Quota.Limit = limit } } err = conn.Users().Insert(u) if err != nil { return err } err = u.createOnRepositoryManager() if err != nil { u.Delete() return err } err = u.AddRolesForEvent(permission.RoleEventUserCreate, "") if err != nil { log.Errorf("unable to add default roles during user creation for %q: %s", u.Email, err) } return nil }
func collection() (*storage.Collection, error) { conn, err := db.Conn() if err != nil { return nil, err } return conn.Collection("bsconfig"), nil }
func (s *PlatformSuite) TestPlatformUpdateShouldSetUpdatePlatformFlagOnApps(c *gocheck.C) { provisioner := testing.ExtensibleFakeProvisioner{ FakeProvisioner: testing.NewFakeProvisioner(), } Provisioner = &provisioner defer func() { Provisioner = s.provisioner }() conn, err := db.Conn() c.Assert(err, gocheck.IsNil) defer conn.Close() name := "test_platform_update" args := make(map[string]string) args["dockerfile"] = "http://localhost/Dockerfile" err = PlatformAdd(name, nil, nil) c.Assert(err, gocheck.IsNil) defer conn.Platforms().Remove(bson.M{"_id": name}) appName := "test_app" app := App{ Name: appName, Platform: name, } err = conn.Apps().Insert(app) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"_id": appName}) err = PlatformUpdate(name, args, nil) c.Assert(err, gocheck.IsNil) a, err := GetByName(appName) c.Assert(err, gocheck.IsNil) c.Assert(a.UpdatePlatform, gocheck.Equals, true) }
func (app *App) unsetEnvsToApp(variableNames []string, publicOnly, shouldRestart bool, w io.Writer) error { if len(variableNames) == 0 { return nil } if w != nil { fmt.Fprintf(w, "---- Unsetting %d environment variables ----\n", len(variableNames)) } for _, name := range variableNames { var unset bool e, err := app.getEnv(name) if !publicOnly || (err == nil && e.Public) { unset = true } if unset { delete(app.Env, name) } } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$set": bson.M{"env": app.Env}}) if err != nil { return err } if !shouldRestart { return nil } return Provisioner.Restart(app, "", w) }
func saveDeployData(opts *DeployOptions, imageId, log string, duration time.Duration, deployError error) error { conn, err := db.Conn() if err != nil { return err } defer conn.Close() deploy := DeployData{ App: opts.App.Name, Timestamp: time.Now(), Duration: duration, Commit: opts.Commit, Image: imageId, Log: log, User: opts.User, } if opts.Commit != "" { deploy.Origin = "git" } else if opts.Image != "" { deploy.Origin = "rollback" } else { deploy.Origin = "app-deploy" } if deployError != nil { deploy.Error = deployError.Error() } return conn.Deploys().Insert(deploy) }
func (app *App) RemoveCName(cnames ...string) error { for _, cname := range cnames { count := 0 for _, appCname := range app.CName { if cname == appCname { count += 1 } } if count == 0 { return stderr.New("cname not exists!") } if s, ok := Provisioner.(provision.CNameManager); ok { if err := s.UnsetCName(app, cname); err != nil { return err } } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Apps().Update( bson.M{"name": app.Name}, bson.M{"$pull": bson.M{"cname": cname}}, ) if err != nil { return err } } return nil }
func (p *FakeDockerProvisioner) Collection() *storage.Collection { conn, err := db.Conn() if err != nil { panic(err) } return conn.Collection("fake_docker_provisioner") }
// Swap calls the Provisioner.Swap. // And updates the app.CName in the database. func Swap(app1, app2 *App) error { err := Provisioner.Swap(app1, app2) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() app1.CName, app2.CName = app2.CName, app1.CName updateCName := func(app *App) error { app.Ip, err = Provisioner.Addr(app) if err != nil { return err } return conn.Apps().Update( bson.M{"name": app.Name}, bson.M{"$set": bson.M{"cname": app.CName, "ip": app.Ip}}, ) } err = updateCName(app1) if err != nil { return err } return updateCName(app2) }
func nodeAddrCollection() (*storage.Collection, error) { conn, err := db.Conn() if err != nil { return nil, errors.WithStack(err) } return conn.Collection(mesosCollectionName), nil }
// Revoke removes the access from a team. It returns an error if the team do // not have access to the app. func (app *App) Revoke(team *auth.Team) error { if len(app.Teams) == 1 { return ErrCannotOrphanApp } index, found := app.findTeam(team) if !found { return ErrNoAccess } last := len(app.Teams) - 1 app.Teams[index] = app.Teams[last] app.Teams = app.Teams[:last] conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$pull": bson.M{"teams": team.Name}}) if err != nil { return err } for _, user := range app.usersToRevoke(team) { err = repository.Manager().RevokeAccess(app.Name, user) if err != nil { conn.Apps().Update(bson.M{"name": app.Name}, bson.M{"$addToSet": bson.M{"teams": team.Name}}) return err } } return nil }
func CheckUserAccess(teamNames []string, u *User) bool { q := bson.M{"_id": bson.M{"$in": teamNames}} var teams []Team conn, err := db.Conn() if err != nil { log.Errorf("Failed to connect to the database: %s", err) return false } defer conn.Close() conn.Teams().Find(q).All(&teams) var wg sync.WaitGroup found := make(chan bool, len(teams)+1) for _, team := range teams { wg.Add(1) go func(t Team) { if t.ContainsUser(u) { found <- true } wg.Done() }(team) } go func() { wg.Wait() found <- false }() return <-found }
func (p *dockerProvisioner) Collection() *storage.Collection { conn, err := db.Conn() if err != nil { log.Errorf("Failed to connect to the database: %s", err) } return conn.Collection(p.collectionName) }
func PlatformRemove(name string) error { var ( provisioner provision.ExtensibleProvisioner ok bool ) if provisioner, ok = Provisioner.(provision.ExtensibleProvisioner); !ok { return errors.New("Provisioner is not extensible") } if name == "" { return errors.New("Platform name is required!") } conn, err := db.Conn() if err != nil { return err } defer conn.Close() apps, _ := conn.Apps().Find(bson.M{"framework": name}).Count() if apps > 0 { return errors.New("Platform has apps. You should remove them before remove the platform.") } err = provisioner.PlatformRemove(name) if err != nil { log.Errorf("Failed to remove platform from provisioner: %s", err) } return conn.Platforms().Remove(bson.M{"_id": name}) }
func (s *S) SetUpSuite(c *gocheck.C) { s.collName = "docker_unit" s.imageCollName = "docker_image" s.gitHost = "my.gandalf.com" s.repoNamespace = "tsuru" s.sshUser = "******" config.Set("git:ro-host", s.gitHost) config.Set("database:url", "127.0.0.1:27017") config.Set("database:name", "docker_provision_tests_s") config.Set("docker:repository-namespace", s.repoNamespace) config.Set("docker:router", "fake") config.Set("docker:collection", s.collName) config.Set("docker:deploy-cmd", "/var/lib/tsuru/deploy") config.Set("docker:run-cmd:bin", "/usr/local/bin/circusd /etc/circus/circus.ini") config.Set("docker:run-cmd:port", "8888") config.Set("docker:ssh:add-key-cmd", "/var/lib/tsuru/add-key") config.Set("docker:ssh:user", s.sshUser) config.Set("queue", "fake") s.deployCmd = "/var/lib/tsuru/deploy" s.runBin = "/usr/local/bin/circusd" s.runArgs = "/etc/circus/circus.ini" s.port = "8888" fsystem = &ftesting.RecordingFs{} f, err := fsystem.Create(os.ExpandEnv("${HOME}/.ssh/id_rsa.pub")) c.Assert(err, gocheck.IsNil) f.Write([]byte("key-content")) f.Close() s.server, err = dtesting.NewServer("127.0.0.1:0", nil) c.Assert(err, gocheck.IsNil) s.targetRecover = tTesting.SetTargetFile(c) s.storage, err = db.Conn() c.Assert(err, gocheck.IsNil) }
// PlatformAdd add a new platform to tsuru func PlatformAdd(name string, args map[string]string, w io.Writer) error { var ( provisioner provision.ExtensibleProvisioner ok bool ) if provisioner, ok = Provisioner.(provision.ExtensibleProvisioner); !ok { return errors.New("Provisioner is not extensible") } if name == "" { return errors.New("Platform name is required.") } p := Platform{Name: name} conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Platforms().Insert(p) if err != nil { if mgo.IsDup(err) { return DuplicatePlatformError{} } return err } err = provisioner.PlatformAdd(name, args, w) if err != nil { dbErr := conn.Platforms().RemoveId(p.Name) if dbErr != nil { return fmt.Errorf("Caused by: %s and %s", err.Error(), dbErr.Error()) } return err } return nil }
func GetServicesInstancesByTeamsAndNames(teams []string, names []string, appName, serviceName string) ([]ServiceInstance, error) { filter := bson.M{} if teams != nil || names != nil { filter = bson.M{ "$or": []bson.M{ {"teams": bson.M{"$in": teams}}, {"name": bson.M{"$in": names}}, }, } } if appName != "" { filter["apps"] = appName } if serviceName != "" { filter["service_name"] = serviceName } conn, err := db.Conn() if err != nil { return nil, err } defer conn.Close() var instances []ServiceInstance err = conn.ServiceInstances().Find(filter).All(&instances) return instances, err }
func (e *Event) TryCancel(reason, owner string) error { if !e.Cancelable || !e.Running { return ErrNotCancelable } conn, err := db.Conn() if err != nil { return err } defer conn.Close() coll := conn.Events() change := mgo.Change{ Update: bson.M{"$set": bson.M{ "cancelinfo": cancelInfo{ Owner: owner, Reason: reason, StartTime: time.Now().UTC(), Asked: true, }, }}, ReturnNew: true, } _, err = coll.Find(bson.M{"_id": e.ID, "cancelinfo.asked": false}).Apply(change, &e.eventData) if err == mgo.ErrNotFound { return ErrEventNotFound } return err }