func (s segregatedScheduler) Schedule(opts docker.CreateContainerOptions) (string, *docker.Container, error) { conn, err := db.Conn() if err != nil { return "", nil, err } defer conn.Close() var cont container coll := collection() defer coll.Close() err = coll.Find(bson.M{"name": opts.Name}).One(&cont) if err != nil { return "", nil, err } app, err := app.GetByName(cont.AppName) if err != nil { return s.fallback(opts) } var nodes []node query := bson.M{"teams": bson.M{"$in": app.Teams}} err = conn.Collection(schedulerCollection).Find(query).All(&nodes) if err != nil || len(nodes) < 1 { return s.fallback(opts) } return s.handle(opts, nodes) }
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 (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) }
func update(units []provision.Unit) { log.Print("updating status from provisioner") var l AppList for _, unit := range units { a, index := l.Search(unit.AppName) if index > -1 { err := a.Get() if err != nil { log.Printf("collector: app %q not found. Skipping.\n", unit.AppName) continue } } u := app.Unit{} u.Name = unit.Name u.Type = unit.Type u.Machine = unit.Machine u.InstanceId = unit.InstanceId u.Ip = unit.Ip u.State = string(unit.Status) a.AddUnit(&u) if index > -1 { l.Add(a, index) } } conn, err := db.Conn() if err != nil { log.Printf("collector failed to connect to the database: %s", err) return } defer conn.Close() for _, a := range l { a.Ip, _ = app.Provisioner.Addr(a) conn.Apps().Update(bson.M{"name": a.Name}, a) } }
func collection() (*storage.Collection, error) { conn, err := db.Conn() if err != nil { return nil, err } return conn.Collection("routers"), nil }
func (s *S) TestUserStartPasswordReset(c *gocheck.C) { conn, err := db.Conn() c.Assert(err, gocheck.IsNil) defer conn.Close() defer s.server.Reset() u := User{Email: "*****@*****.**", Password: "******"} err = u.StartPasswordReset() c.Assert(err, gocheck.IsNil) defer conn.PasswordTokens().Remove(bson.M{"useremail": u.Email}) var token passwordToken err = conn.PasswordTokens().Find(bson.M{"useremail": u.Email}).One(&token) c.Assert(err, gocheck.IsNil) time.Sleep(1e9) // Let the email flow. s.server.Lock() defer s.server.Unlock() c.Assert(s.server.MailBox, gocheck.HasLen, 1) m := s.server.MailBox[0] c.Assert(m.From, gocheck.Equals, "root") c.Assert(m.To, gocheck.DeepEquals, []string{u.Email}) var buf bytes.Buffer err = resetEmailData.Execute(&buf, token) c.Assert(err, gocheck.IsNil) expected := strings.Replace(buf.String(), "\n", "\r\n", -1) + "\r\n" c.Assert(string(m.Data), gocheck.Equals, expected) }
func addUserToTeam(email, teamName string, u *auth.User) error { conn, err := db.Conn() if err != nil { return err } defer conn.Close() team, user := new(auth.Team), new(auth.User) selector := bson.M{"_id": teamName} if err := conn.Teams().Find(selector).One(team); err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "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} } if err := conn.Users().Find(bson.M{"email": email}).One(user); 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 removeOldTokens(userEmail string) error { conn, err := db.Conn() if err != nil { return err } defer conn.Close() var limit int if limit, err = config.GetInt("auth:max-simultaneous-sessions"); err != nil { return err } count, err := conn.Tokens().Find(bson.M{"useremail": userEmail}).Count() if err != nil { return err } diff := count - limit if diff < 1 { return nil } var tokens []map[string]interface{} err = conn.Tokens().Find(bson.M{"useremail": userEmail}).Select(bson.M{"_id": 1}).Limit(diff).All(&tokens) if err != nil { return nil } ids := make([]interface{}, 0, len(tokens)) for _, token := range tokens { ids = append(ids, token["_id"]) } _, err = conn.Tokens().RemoveAll(bson.M{"_id": bson.M{"$in": ids}}) return err }
func (s *S) TestReserveAppIsSafe(c *gocheck.C) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU())) email := "*****@*****.**" user := &User{ Email: email, Password: "******", Quota: quota.Quota{Limit: 10, InUse: 0}, } err := user.Create() c.Assert(err, gocheck.IsNil) conn, err := db.Conn() c.Assert(err, gocheck.IsNil) defer conn.Close() defer conn.Users().Remove(bson.M{"email": user.Email}) var wg sync.WaitGroup for i := 0; i < 24; i++ { wg.Add(1) go func() { defer wg.Done() ReserveApp(user) }() } wg.Wait() user, err = GetUserByEmail(email) c.Assert(err, gocheck.IsNil) c.Assert(user.Quota.InUse, gocheck.Equals, 10) }
func (s *S) TestGetImageAppWhenDeployIsMultipleOf10(c *gocheck.C) { var request http.Request server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { request = *r })) defer server.Close() u, _ := url.Parse(server.URL) imageRepo := u.Host + "/tsuru/python" err := newImage(imageRepo, s.server.URL()) c.Assert(err, gocheck.IsNil) conn, err := db.Conn() c.Assert(err, gocheck.IsNil) defer conn.Close() units := []app.Unit{{Name: "4fa6e0f0c678"}, {Name: "e90e34656806"}} app := &app.App{Name: "app1", Platform: "python", Deploys: 20, Units: units} err = conn.Apps().Insert(app) c.Assert(err, gocheck.IsNil) defer conn.Apps().Remove(bson.M{"name": "app1"}) cont := container{ID: "bleble", Type: "python", AppName: "app1", Image: imageRepo} coll := collection() err = coll.Insert(cont) c.Assert(err, gocheck.IsNil) defer coll.Close() c.Assert(err, gocheck.IsNil) defer coll.RemoveAll(bson.M{"id": "bleble"}) img := getImage(app) repoNamespace, err := config.GetString("docker:repository-namespace") c.Assert(err, gocheck.IsNil) c.Assert(img, gocheck.Equals, fmt.Sprintf("%s/python", repoNamespace)) c.Assert(request.Method, gocheck.Equals, "DELETE") path := "/v1/repositories/tsuru/python/tags" c.Assert(request.URL.Path, gocheck.Equals, path) }
func (s *S) TestUsePlatformImage(c *gocheck.C) { conn, err := db.Conn() c.Assert(err, gocheck.IsNil) defer conn.Close() units := []app.Unit{{Name: "4fa6e0f0c678"}, {Name: "e90e34656806"}} app1 := &app.App{Name: "app1", Platform: "python", Deploys: 40, Units: units} err = conn.Apps().Insert(app1) c.Assert(err, gocheck.IsNil) ok := usePlatformImage(app1) c.Assert(ok, gocheck.Equals, true) defer conn.Apps().Remove(bson.M{"name": "app1"}) app2 := &app.App{Name: "app2", Platform: "python", Deploys: 20, Units: units} err = conn.Apps().Insert(app2) c.Assert(err, gocheck.IsNil) ok = usePlatformImage(app2) c.Assert(ok, gocheck.Equals, true) defer conn.Apps().Remove(bson.M{"name": "app2"}) app3 := &app.App{Name: "app3", Platform: "python", Deploys: 0, Units: units} err = conn.Apps().Insert(app3) c.Assert(err, gocheck.IsNil) ok = usePlatformImage(app3) c.Assert(ok, gocheck.Equals, false) defer conn.Apps().Remove(bson.M{"name": "app3"}) app4 := &app.App{Name: "app4", Platform: "python", Deploys: 19, Units: units} err = conn.Apps().Insert(app4) c.Assert(err, gocheck.IsNil) ok = usePlatformImage(app4) c.Assert(ok, gocheck.Equals, false) defer conn.Apps().Remove(bson.M{"name": "app4"}) }
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 RemoveServiceInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error { name := r.URL.Query().Get(":name") si, err := getServiceInstanceOrError(name, u) if err != nil { return err } if len(si.Apps) > 0 { msg := "This service instance is bound to at least one app. Unbind them before removing it" return &errors.Http{Code: http.StatusInternalServerError, Message: msg} } if err = si.Service().ProductionEndpoint().Destroy(&si); err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.ServiceInstances().Remove(bson.M{"name": name}) if err != nil { return err } w.Write([]byte("service instance successfuly removed")) return nil }
func ServiceInstanceStatusHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error { // #TODO (flaviamissi) should check if user has access to service // just call GetServiceInstanceOrError should be enough siName := r.URL.Query().Get(":instance") var si service.ServiceInstance if siName == "" { return &errors.Http{Code: http.StatusBadRequest, Message: "Service instance name not provided."} } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.ServiceInstances().Find(bson.M{"name": siName}).One(&si) if err != nil { msg := fmt.Sprintf("Service instance does not exists, error: %s", err.Error()) return &errors.Http{Code: http.StatusInternalServerError, Message: msg} } s := si.Service() var b string if b, err = s.ProductionEndpoint().Status(&si); err != nil { msg := fmt.Sprintf("Could not retrieve status of service instance, error: %s", err.Error()) return &errors.Http{Code: http.StatusInternalServerError, Message: msg} } b = fmt.Sprintf(`Service instance "%s" is %s`, siName, b) n, err := w.Write([]byte(b)) if n != len(b) { return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write response body"} } return nil }
func ServiceInfoHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error { serviceName := r.URL.Query().Get(":name") _, 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 grantAccessToTeam(appName, teamName string, u *auth.User) error { t := new(auth.Team) app, err := getApp(appName, u) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Teams().Find(bson.M{"_id": teamName}).One(t) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } err = app.Grant(t) if err != nil { return &errors.Http{Code: http.StatusConflict, Message: err.Error()} } err = conn.Apps().Update(bson.M{"name": app.Name}, app) if err != nil { return err } gUrl := repository.GitServerUri() gClient := gandalf.Client{Endpoint: gUrl} if err := gClient.GrantAccess([]string{app.Name}, t.Users); err != nil { return fmt.Errorf("Failed to grant access in the git server: %s.", err) } return nil }
func (s *S) SetUpSuite(c *gocheck.C) { var err error config.Set("database:url", "127.0.0.1:27017") config.Set("database:name", "router_fake_tests") s.conn, err = db.Conn() c.Assert(err, gocheck.IsNil) }
func getServiceInstace(instanceName, appName string, u *auth.User) (service.ServiceInstance, app.App, error) { var app app.App conn, err := db.Conn() if err != nil { return service.ServiceInstance{}, app, err } defer conn.Close() instance, err := service.GetInstance(instanceName) if err != nil { err = &errors.Http{Code: http.StatusNotFound, Message: "Instance not found"} return instance, app, err } if !auth.CheckUserAccess(instance.Teams, u) { err = &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this instance"} return instance, app, err } err = conn.Apps().Find(bson.M{"name": appName}).One(&app) if err != nil { err = &errors.Http{Code: http.StatusNotFound, Message: fmt.Sprintf("App %s not found.", appName)} return instance, app, err } if !auth.CheckUserAccess(app.Teams, u) { err = &errors.Http{Code: http.StatusForbidden, Message: "This user does not have access to this app"} return instance, app, err } return instance, app, nil }
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 }
// unbind takes all service instances that are bound to the app, and unbind // them. This method is used by Destroy (before destroying the app, it unbinds // all service instances). Refer to Destroy docs for more details. func (app *App) unbind() error { var instances []service.ServiceInstance conn, err := db.Conn() if err != nil { return err } defer conn.Close() q := bson.M{"apps": bson.M{"$in": []string{app.Name}}} err = conn.ServiceInstances().Find(q).All(&instances) if err != nil { return err } var msg string var addMsg = func(instanceName string, reason error) { if msg == "" { msg = "Failed to unbind the following instances:\n" } msg += fmt.Sprintf("- %s (%s)", instanceName, reason.Error()) } for _, instance := range instances { err = instance.UnbindApp(app) if err != nil { addMsg(instance.Name, err) } } if msg != "" { return stderr.New(msg) } return nil }
func removeUserFromTeam(email, teamName string, u *auth.User) error { conn, err := db.Conn() if err != nil { return err } defer conn.Close() team := new(auth.Team) err = conn.Teams().FindId(teamName).One(team) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "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 := auth.User{Email: email} err = user.Get() 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) }
// RemoveUnit removes a unit by its InstanceId or Name. // // Will search first by InstanceId, if no instance is found, then tries to // search using the unit name, then calls the removal function from provisioner // // Returns an error in case of failure. func (app *App) RemoveUnit(id string) error { var ( unit Unit i int u Unit ) for i, u = range app.Units { if u.InstanceId == id || u.Name == id { unit = u break } } if unit.GetName() == "" { return stderr.New("Unit not found.") } if err := Provisioner.RemoveUnit(app, unit.GetName()); err != nil { return err } app.removeUnits([]int{i}) app.unbindUnit(&unit) conn, err := db.Conn() if err != nil { return err } defer conn.Close() return conn.Apps().Update( bson.M{"name": app.Name}, bson.M{"$set": bson.M{"units": app.Units}}, ) }
// Reserve reserves the given items to the owner. // // It may allocate part of the items before exceeding the quota. See the // example for more details. func Reserve(owner string, items ...string) error { conn, err := db.Conn() if err != nil { return err } defer conn.Close() locker.Lock(owner) defer locker.Unlock(owner) var u usage err = conn.Quota().Find(bson.M{"owner": owner}).One(&u) if err != nil { return ErrQuotaNotFound } available := int(u.Limit) - len(u.Items) if available < 0 { available = 0 } if available < len(items) { return &QuotaExceededError{Requested: uint(len(items)), Available: uint(available)} } update := bson.M{"$addToSet": bson.M{"items": bson.M{"$each": items}}} err = conn.Quota().Update(bson.M{"owner": owner}, update) if err != nil { return err } return nil }
// 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). // // If useQueue is true, it will use a queue to write the environment variables // in the units of the app. func (app *App) setEnvsToApp(envs []bind.EnvVar, publicOnly, useQueue bool) error { if len(envs) > 0 { for _, env := range envs { set := true if publicOnly { e, err := app.getEnv(env.Name) if err == nil && !e.Public { 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 useQueue { Enqueue(queue.Message{Action: regenerateApprc, Args: []string{app.Name}}) return nil } go app.SerializeEnvVars() } return nil }
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) }
// UnsetEnvs removes environment variables from an app, serializing the // remaining list of environment variables to all units of the app. // // Besides the slice with the name of the variables, this method also takes the // parameter publicOnly, which indicates whether only public variables can be // overridden (if set to false, setEnvsToApp may override a private variable). func (app *App) UnsetEnvs(variableNames []string, publicOnly bool) error { if len(variableNames) > 0 { 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 } go app.SerializeEnvVars() } return nil }
func (s *ELBSuite) TestELBInstanceHealerInstallingUnit(c *gocheck.C) { lb := "elbtest" instance := s.server.NewInstance() defer s.server.RemoveInstance(instance) s.server.NewLoadBalancer(lb) defer s.server.RemoveLoadBalancer(lb) s.server.RegisterInstance(instance, lb) defer s.server.DeregisterInstance(instance, lb) a := app.App{ Name: "elbtest", Units: []app.Unit{{InstanceId: instance, State: "installing", Name: "elbtest/0"}}, } storage, err := db.Conn() c.Assert(err, gocheck.IsNil) err = storage.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer storage.Apps().Remove(bson.M{"name": a.Name}) s.provisioner.Provision(&a) defer s.provisioner.Destroy(&a) state := elb.InstanceState{ Description: "Instance has failed at least the UnhealthyThreshold number of health checks consecutively.", State: "OutOfService", ReasonCode: "Instance", InstanceId: instance, } s.server.ChangeInstanceState(lb, state) healer := elbInstanceHealer{} err = healer.Heal() c.Assert(err, gocheck.IsNil) err = a.Get() c.Assert(err, gocheck.IsNil) c.Assert(a.Units, gocheck.HasLen, 1) c.Assert(a.Units[0].InstanceId, gocheck.Equals, instance) }
// Log adds a log message to the app. Specifying a good source is good so the // user can filter where the message come from. func (app *App) Log(message, source string) error { messages := strings.Split(message, "\n") logs := make([]interface{}, 0, len(messages)) for _, msg := range messages { if msg != "" { l := Applog{ Date: time.Now().In(time.UTC), Message: msg, Source: source, AppName: app.Name, } logs = append(logs, l) } } if len(logs) > 0 { go notify(app.Name, logs) conn, err := db.Conn() if err != nil { return err } defer conn.Close() return conn.Logs().Insert(logs...) } return nil }
// bindUnit handles the bind-service message, binding a unit to all service // instances bound to the app. func bindUnit(msg *queue.Message) error { a := App{Name: msg.Args[0]} err := a.Get() if err != nil { msg.Delete() return fmt.Errorf("Error handling %q: app %q does not exist.", msg.Action, a.Name) } conn, err := db.Conn() if err != nil { return fmt.Errorf("Error handling %q: %s", msg.Action, err) } defer conn.Close() units := getUnits(&a, msg.Args[1:]) if len(units) == 0 { msg.Delete() return errors.New("Unknown unit in the message.") } unit := units[0] var instances []service.ServiceInstance q := bson.M{"apps": bson.M{"$in": []string{msg.Args[0]}}} err = conn.ServiceInstances().Find(q).All(&instances) if err != nil { return err } for _, instance := range instances { _, err = instance.BindUnit(&a, &unit) if err != nil { log.Printf("Error binding the unit %s with the service instance %s.", unit.Name, instance.Name) } } return nil }
// LastLogs returns a list of the last `lines` log of the app, matching the // given source. func (a *App) LastLogs(lines int, source string) ([]Applog, error) { conn, err := db.Conn() if err != nil { return nil, err } defer conn.Close() match := bson.M{} match["name"] = a.Name if source != "" { match["logs.source"] = source } pipe := []bson.M{ {"$unwind": "$logs"}, {"$match": match}, {"$project": bson.M{"_id": 0, "logs": 1}}, {"$sort": bson.M{"logs.date": -1}}, {"$limit": lines}, } var result []map[string]map[string]interface{} err = conn.Apps().Pipe(pipe).All(&result) if err != nil { return nil, err } n := len(result) logs := make([]Applog, n) for i, row := range result { log := Applog{ Message: row["logs"]["message"].(string), Source: row["logs"]["source"].(string), Date: row["logs"]["date"].(time.Time), } logs[n-i-1] = log } return logs, nil }