// BaseStack builds new base stack for the given context value. func (s *Stacker) BaseStack(ctx context.Context) (*BaseStack, error) { bs := &BaseStack{ Planner: &Planner{ Provider: s.Provider.Name, ResourceType: s.Provider.resourceName(), Log: s.Log, }, Provider: s.Provider, KlientIDs: make(stack.KiteMap), Klients: make(map[string]*DialState), TunnelURL: s.TunnelURL, Keys: s.SSHKey, } var ok bool if bs.Req, ok = request.FromContext(ctx); !ok { return nil, errors.New("request not available in context") } req, ok := stack.TeamRequestFromContext(ctx) if !ok { return nil, errors.New("team request not available in context") } if bs.Session, ok = session.FromContext(ctx); !ok { return nil, errors.New("session not available in context") } bs.Log = s.Log.New(req.GroupName) if traceID, ok := stack.TraceFromContext(ctx); ok { bs.Log = logging.NewCustom("kloud-"+req.Provider, true).New(traceID) bs.TraceID = traceID } if keys, ok := publickeys.FromContext(ctx); ok { bs.Keys = keys } if ev, ok := eventer.FromContext(ctx); ok { bs.Eventer = ev } builderOpts := &BuilderOptions{ Log: s.Log.New("stackplan"), CredStore: s.CredStore, } bs.Builder = NewBuilder(builderOpts) if err := bs.Builder.BuildTeam(req.GroupName); err != nil { return nil, err } if !bs.Builder.Team.IsSubActive() { return nil, stack.NewError(stack.ErrTeamSubIsNotActive) } return bs, nil }
func (s *Stacker) BuildBaseMachine(ctx context.Context, m *models.Machine) (*BaseMachine, error) { req, ok := request.FromContext(ctx) if !ok { return nil, errors.New("request context is not available") } bm := &BaseMachine{ Machine: m, Session: &session.Session{ DB: s.DB, Kite: s.Kite, Userdata: s.Userdata, Log: s.Log.New(m.ObjectId.Hex()), }, Credential: s.Provider.newCredential(), Bootstrap: s.Provider.newBootstrap(), Metadata: s.Provider.newMetadata(nil), Req: req, Provider: s.Provider.Name, Debug: s.Debug, } // NOTE(rjeczalik): "internal" method is used by (*Queue).CheckAWS if req.Method != "internal" { // get user model which contains user ssh keys or the list of users that // are allowed to use this machine if len(m.Users) == 0 { return nil, errors.New("permitted users list is empty") } // get the user from the permitted list. If the list contains more than one // allowed person, fetch the one that is the same as requesterName, if // not pick up the first one. var err error bm.User, err = modelhelper.GetPermittedUser(req.Username, bm.Users) if err != nil { return nil, err } if err := s.ValidateUser(bm.User, bm.Users, req); err != nil { return nil, err } } if traceID, ok := stack.TraceFromContext(ctx); ok { bm.Log = logging.NewCustom("kloud-"+s.Provider.Name, true).New(m.ObjectId.Hex()).New(traceID) bm.Debug = true bm.TraceID = traceID } ev, ok := eventer.FromContext(ctx) if ok { bm.Eventer = ev } s.Log.Debug("BaseMachine: %+v", bm) return bm, nil }
func (k *Kloud) traceRequest(ctx context.Context, tags []string) context.Context { if r, ok := request.FromContext(ctx); ok { tags = append(tags, "action:"+r.Method) } if traceID, ok := ctx.Value(TraceKey).(string); ok { tags = append(tags, "trace:"+traceID) } return context.WithValue(ctx, RequestTraceKey, &RequestTrace{ start: time.Now(), tags: tags, histFunc: k.Metrics.Histogram, log: k.Log, }) }
// BuildMachines fetches machines that belongs to existing b.Stack. // // It validates whether user is allowed to perform apply operation. // When nil error is returned, the b.Machines field is non-nil. func (b *Builder) BuildMachines(ctx context.Context) error { ids := b.Stack.Machines sess, ok := session.FromContext(ctx) if !ok { return errors.New("session context is not passed") } req, ok := request.FromContext(ctx) if !ok { return errors.New("request context is not passed") } mongodbIds := make([]bson.ObjectId, len(ids)) for i, id := range ids { mongodbIds[i] = bson.ObjectIdHex(id) } b.Log.Debug("Building machines with IDs: %+v", ids) machines := make([]*models.Machine, 0) if err := sess.DB.Run("jMachines", func(c *mgo.Collection) error { return c.Find(bson.M{"_id": bson.M{"$in": mongodbIds}}).All(&machines) }); err != nil { return err } b.Log.Debug("Fetched machines: %+v", machines) validUsers := make(map[string]models.MachineUser, 0) validMachines := make(map[string]*models.Machine, 0) for _, machine := range machines { // machines with empty users are supposed to allowed by default // (gokmen) if len(machine.Users) == 0 { validMachines[machine.ObjectId.Hex()] = machine continue } // for others we need to be sure they are valid // TODO(arslan): add custom type with custom methods for type // []*Machineuser for _, user := range machine.Users { // we only going to select users that are allowed: // // - team member that owns vm (sudo + owner) // - team admin that owns all vms (owner + !permanent) // // A shared user is (owner + permanent). if (user.Sudo || !user.Permanent) && user.Owner { validUsers[user.Id.Hex()] = user } } } allowedIds := make([]bson.ObjectId, 0) for _, user := range validUsers { allowedIds = append(allowedIds, user.Id) } b.Log.Debug("Building users with allowed IDs: %+v", allowedIds) users, err := modelhelper.GetUsersById(allowedIds...) if err != nil { return models.ResError(err, "jUser") } // find whether requested user is among allowed ones var reqUser *models.User for _, u := range users { if u.Name == req.Username { reqUser = u break } } b.Log.Debug("Found requester: %v (requester username: %s)", reqUser, req.Username) if reqUser != nil { // now check if the requested user is inside the allowed users list for _, m := range machines { for _, user := range m.Users { if user.Id.Hex() == reqUser.ObjectId.Hex() { validMachines[m.ObjectId.Hex()] = m break } } } } if len(validMachines) == 0 { return fmt.Errorf("no valid machines found for the user: %s", req.Username) } b.Machines = make(map[string]*models.Machine, len(validMachines)) for _, m := range validMachines { label := m.Label if s, ok := m.Meta["assignedLabel"].(string); ok { label = s } b.Machines[label] = m } b.Log.Debug("Machines built: %+v", b.Machines) return nil }