func NewUserManagerAPI( st *state.State, resources facade.Resources, authorizer facade.Authorizer, ) (*UserManagerAPI, error) { if !authorizer.AuthClient() { return nil, common.ErrPerm } // Since we know this is a user tag (because AuthClient is true), // we just do the type assertion to the UserTag. apiUser, _ := authorizer.GetAuthTag().(names.UserTag) // Pretty much all of the user manager methods have special casing for admin // users, so look once when we start and remember if the user is an admin. isAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, st.ControllerTag()) if err != nil { return nil, errors.Trace(err) } return &UserManagerAPI{ state: st, authorizer: authorizer, check: common.NewBlockChecker(st), apiUser: apiUser, isAdmin: isAdmin, }, nil }
// populateGUIArchive stores the uploaded Juju GUI archive in provider storage, // updates the GUI metadata and set the current Juju GUI version. func (c *BootstrapCommand) populateGUIArchive(st *state.State, env environs.Environ) error { agentConfig := c.CurrentConfig() dataDir := agentConfig.DataDir() guistorage, err := st.GUIStorage() if err != nil { return errors.Trace(err) } defer guistorage.Close() gui, err := agenttools.ReadGUIArchive(dataDir) if err != nil { return errors.Annotate(err, "cannot fetch GUI info") } f, err := os.Open(filepath.Join(agenttools.SharedGUIDir(dataDir), "gui.tar.bz2")) if err != nil { return errors.Annotate(err, "cannot read GUI archive") } defer f.Close() if err := guistorage.Add(f, binarystorage.Metadata{ Version: gui.Version.String(), Size: gui.Size, SHA256: gui.SHA256, }); err != nil { return errors.Annotate(err, "cannot store GUI archive") } if err = st.GUISetVersion(gui.Version); err != nil { return errors.Annotate(err, "cannot set current GUI version") } return nil }
// ImportModel deserializes a model description from the bytes, transforms // the model config based on information from the controller model, and then // imports that as a new database model. func ImportModel(st *state.State, bytes []byte) (*state.Model, *state.State, error) { model, err := description.Deserialize(bytes) if err != nil { return nil, nil, errors.Trace(err) } controllerModel, err := st.ControllerModel() if err != nil { return nil, nil, errors.Trace(err) } controllerConfig, err := controllerModel.Config() if err != nil { return nil, nil, errors.Trace(err) } model.UpdateConfig(controllerValues(controllerConfig)) if err := updateConfigFromProvider(model, controllerConfig); err != nil { return nil, nil, errors.Trace(err) } dbModel, dbState, err := st.Import(model) if err != nil { return nil, nil, errors.Trace(err) } return dbModel, dbState, nil }
func (s *ModelSuite) assertDyingEnvironTransitionDyingToDead(c *gc.C, st *state.State) { // Add a service to prevent the model from transitioning directly to Dead. // Add the service before getting the Model, otherwise we'll have to run // the transaction twice, and hit the hook point too early. svc := factory.NewFactory(st).MakeService(c, nil) env, err := st.Model() c.Assert(err, jc.ErrorIsNil) // ProcessDyingModel is called by a worker after Destroy is called. To // avoid a race, we jump the gun here and test immediately after the // environement was set to dead. defer state.SetAfterHooks(c, st, func() { c.Assert(env.Refresh(), jc.ErrorIsNil) c.Assert(env.Life(), gc.Equals, state.Dying) err := svc.Destroy() c.Assert(err, jc.ErrorIsNil) c.Assert(st.ProcessDyingModel(), jc.ErrorIsNil) c.Assert(env.Refresh(), jc.ErrorIsNil) c.Assert(env.Life(), gc.Equals, state.Dead) }).Check() c.Assert(env.Destroy(), jc.ErrorIsNil) }
// addServiceUnits adds a given number of units to a service. func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) { service, err := state.Service(args.ServiceName) if err != nil { return nil, err } if args.NumUnits < 1 { return nil, fmt.Errorf("must add at least one unit") } // New API uses placement directives. if len(args.Placement) > 0 { return jjj.AddUnitsWithPlacement(state, service, args.NumUnits, args.Placement) } // Otherwise we use the older machine spec. if args.NumUnits > 1 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec") } if args.ToMachineSpec != "" && names.IsValidMachine(args.ToMachineSpec) { _, err = state.Machine(args.ToMachineSpec) if err != nil { return nil, errors.Annotatef(err, `cannot add units for service "%v" to machine %v`, args.ServiceName, args.ToMachineSpec) } } return jjj.AddUnits(state, service, args.NumUnits, args.ToMachineSpec) }
// assertMachineStorageRefs ensures that the specified machine's set of volume // and filesystem references corresponds exactly to the volume and filesystem // attachments that relate to the machine. func assertMachineStorageRefs(c *gc.C, st *state.State, m names.MachineTag) { machines, closer := state.GetRawCollection(st, state.MachinesC) defer closer() var doc struct { Volumes []string `bson:"volumes,omitempty"` Filesystems []string `bson:"filesystems,omitempty"` } err := machines.FindId(state.DocID(st, m.Id())).One(&doc) c.Assert(err, jc.ErrorIsNil) have := make(set.Tags) for _, v := range doc.Volumes { have.Add(names.NewVolumeTag(v)) } for _, f := range doc.Filesystems { have.Add(names.NewFilesystemTag(f)) } expect := make(set.Tags) volumeAttachments, err := st.MachineVolumeAttachments(m) c.Assert(err, jc.ErrorIsNil) for _, a := range volumeAttachments { expect.Add(a.Volume()) } filesystemAttachments, err := st.MachineFilesystemAttachments(m) c.Assert(err, jc.ErrorIsNil) for _, a := range filesystemAttachments { expect.Add(a.Filesystem()) } c.Assert(have, jc.DeepEquals, expect) }
func currentInfo(st *state.State, db *mgo.Database) (*serverInfo, error) { var doc stateServersDoc err := db.C("stateServers").Find(bson.D{{"_id", "e"}}).One(&doc) if err != nil { return nil, fmt.Errorf("cannot get state server info: %v", err) } ms := make(map[string]*state.Machine) var all []string all = append(all, doc.MachineIds...) all = append(all, doc.VotingMachineIds...) for _, id := range all { if _, ok := ms[id]; ok { continue } m, err := st.Machine(id) if err != nil { return nil, fmt.Errorf("cannot get info on machine %s: %v", id, err) } ms[id] = m } return &serverInfo{ servers: &doc, machines: ms, }, nil }
func (payloads) newPublicFacade(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*server.PublicAPI, error) { up, err := st.EnvPayloads() if err != nil { return nil, errors.Trace(err) } return server.NewPublicAPI(up), nil }
func (c *MigrateCommand) exportModel(ctx *cmd.Context, st *state.State) error { ctx.Infof("\nexport %s", c.modelUUID) // first make sure the uuid is good enough tag := names.NewModelTag(c.modelUUID) _, err := st.GetModel(tag) if err != nil { return errors.Trace(err) } modelState, err := st.ForModel(tag) if err != nil { return errors.Trace(err) } defer modelState.Close() model, err := modelState.Export() if err != nil { return errors.Trace(err) } bytes, err := yaml.Marshal(model) if err != nil { return errors.Trace(err) } ctx.Stdout.Write(bytes) return nil }
func (s *MachineRemovalSuite) createRemovalWatcher(c *gc.C, st *state.State) ( state.NotifyWatcher, testing.NotifyWatcherC, ) { w := st.WatchMachineRemovals() s.AddCleanup(func(c *gc.C) { workertest.CleanKill(c, w) }) return w, testing.NewNotifyWatcherC(c, st, w) }
func (payloads) newHookContextFacade(st *state.State, unit *state.Unit) (interface{}, error) { up, err := st.UnitPayloads(unit) if err != nil { return nil, errors.Trace(err) } return internalserver.NewUnitFacade(up), nil }
// getAllUnitNames returns a sequence of valid Unit objects from state. If any // of the service names or unit names are not found, an error is returned. func getAllUnitNames(st *state.State, units, services []string) (result []*state.Unit, err error) { unitsSet := set.NewStrings(units...) for _, name := range services { service, err := st.Service(name) if err != nil { return nil, err } units, err := service.AllUnits() if err != nil { return nil, err } for _, unit := range units { unitsSet.Add(unit.Name()) } } for _, unitName := range unitsSet.Values() { unit, err := st.Unit(unitName) if err != nil { return nil, err } // We only operate on units that have an assigned machine. if _, err := unit.AssignedMachineId(); err != nil { return nil, err } result = append(result, unit) } return result, nil }
func (s *MigrationSuite) createStatusWatcher(c *gc.C, st *state.State) ( state.NotifyWatcher, statetesting.NotifyWatcherC, ) { w := st.WatchMigrationStatus() s.AddCleanup(func(c *gc.C) { statetesting.AssertStop(c, w) }) return w, statetesting.NewNotifyWatcherC(c, st, w) }
func handleResponse(mm *state.MetricsManager, st *state.State, response wireformat.Response) { for _, envResp := range response.EnvResponses { err := st.SetMetricBatchesSent(envResp.AcknowledgedBatches) if err != nil { logger.Errorf("failed to set sent on metrics %v", err) } for unitName, status := range envResp.UnitStatuses { unit, err := st.Unit(unitName) if err != nil { logger.Errorf("failed to retrieve unit %q: %v", unitName, err) continue } err = unit.SetMeterStatus(status.Status, status.Info) if err != nil { logger.Errorf("failed to set unit %q meter status to %v: %v", unitName, status, err) } } } if response.NewGracePeriod > 0 { err := mm.SetGracePeriod(response.NewGracePeriod) if err != nil { logger.Errorf("failed to set new grace period %v", err) } } }
// processGet handles a tools GET request. func (h *toolsDownloadHandler) processGet(r *http.Request, st *state.State) ([]byte, error) { version, err := version.ParseBinary(r.URL.Query().Get(":version")) if err != nil { return nil, errors.Annotate(err, "error parsing version") } storage, err := st.ToolsStorage() if err != nil { return nil, errors.Annotate(err, "error getting tools storage") } defer storage.Close() _, reader, err := storage.Open(version.String()) if errors.IsNotFound(err) { // Tools could not be found in tools storage, // so look for them in simplestreams, fetch // them and cache in tools storage. logger.Infof("%v tools not found locally, fetching", version) reader, err = h.fetchAndCacheTools(version, storage, st) if err != nil { err = errors.Annotate(err, "error fetching tools") } } if err != nil { return nil, err } defer reader.Close() data, err := ioutil.ReadAll(reader) if err != nil { return nil, errors.Annotate(err, "failed to read tools tarball") } return data, nil }
// loadImage loads an os image from the blobstore, // downloading and caching it if necessary. func (h *imagesDownloadHandler) loadImage(st *state.State, envuuid, kind, series, arch string) ( *imagestorage.Metadata, io.ReadCloser, error, ) { // We want to ensure that if an image needs to be downloaded and cached, // this only happens once. imageIdent := fmt.Sprintf("image-%s-%s-%s-%s", envuuid, kind, series, arch) lockDir := filepath.Join(h.dataDir, "locks") lock, err := fslock.NewLock(lockDir, imageIdent, fslock.Defaults()) if err != nil { return nil, nil, errors.Trace(err) } lock.Lock("fetch and cache image " + imageIdent) defer lock.Unlock() storage := st.ImageStorage() metadata, imageReader, err := storage.Image(kind, series, arch) // Not in storage, so go fetch it. if errors.IsNotFound(err) { if err := h.fetchAndCacheLxcImage(storage, envuuid, series, arch); err != nil { return nil, nil, errors.Annotate(err, "error fetching and caching image") } err = networkOperationWitDefaultRetries(func() error { metadata, imageReader, err = storage.Image(string(instance.LXC), series, arch) return err }, "streaming os image from blobstore")() } if err != nil { return nil, nil, errors.Trace(err) } return metadata, imageReader, nil }
// AddSubnetsWithTemplate adds numSubnets subnets, using the given // infoTemplate. Any string field in the infoTemplate can be specified // as a text/template string containing {{.}}, which is the current // index of the subnet-to-add (between 0 and numSubnets-1). // // Example: // // AddSubnetsWithTemplate(c, st, 2, state.SubnetInfo{ // CIDR: "10.10.{{.}}.0/24", // ProviderId: "subnet-{{.}}", // SpaceName: "space1", // AvailabilityZone: "zone-{{.}}", // AllocatableIPLow: "{{if (gt . 0)}}10.10.{{.}}.5{{end}}", // AllocatableIPHigh: "{{if (gt . 0)}}10.10.{{.}}.254{{end}}", // VLANTag: 42, // }) // // This is equivalent to the following calls: // // _, err := st.AddSubnet(state.SubnetInfo{ // CIDR: "10.10.0.0/24", // ProviderId: "subnet-0", // SpaceName: "space1", // AvailabilityZone: "zone-0", // VLANTag: 42, // }) // c.Assert(err, jc.ErrorIsNil) // _, err = st.AddSubnet(state.SubnetInfo{ // CIDR: "10.10.1.0/24", // ProviderId: "subnet-1", // SpaceName: "space1", // AvailabilityZone: "zone-1", // AllocatableIPLow: "10.10.1.5", // AllocatableIPHigh: "10.10.1.254", // VLANTag: 42, // }) func AddSubnetsWithTemplate(c *gc.C, st *state.State, numSubnets uint, infoTemplate state.SubnetInfo) { for subnetIndex := 0; subnetIndex < int(numSubnets); subnetIndex++ { info := infoTemplate // make a copy each time. // permute replaces the contents of *s with the result of interpreting // *s as a template. permute := func(s string) string { t, err := template.New("").Parse(s) c.Assert(err, jc.ErrorIsNil) var buf bytes.Buffer err = t.Execute(&buf, subnetIndex) c.Assert(err, jc.ErrorIsNil) return buf.String() } info.ProviderId = network.Id(permute(string(info.ProviderId))) info.CIDR = permute(info.CIDR) info.AllocatableIPHigh = permute(info.AllocatableIPHigh) info.AllocatableIPLow = permute(info.AllocatableIPLow) info.AvailabilityZone = permute(info.AvailabilityZone) info.SpaceName = permute(info.SpaceName) _, err := st.AddSubnet(info) c.Assert(err, jc.ErrorIsNil) } }
// checkCreds validates the entities credentials in the current environment. // If the entity is a user, and lookForEnvUser is true, an env user must exist // for the environment. In the case of a user logging in to the server, but // not an environment, there is no env user needed. While we have the env // user, if we do have it, update the last login time. func checkCreds(st *state.State, req params.LoginRequest, lookForEnvUser bool) (state.Entity, *time.Time, error) { tag, err := names.ParseTag(req.AuthTag) if err != nil { return nil, nil, err } entity, err := st.FindEntity(tag) if errors.IsNotFound(err) { // We return the same error when an entity does not exist as for a bad // password, so that we don't allow unauthenticated users to find // information about existing entities. logger.Debugf("entity %q not found", tag) return nil, nil, common.ErrBadCreds } if err != nil { return nil, nil, errors.Trace(err) } authenticator, err := authentication.FindEntityAuthenticator(entity) if err != nil { return nil, nil, err } if err = authenticator.Authenticate(entity, req.Credentials, req.Nonce); err != nil { logger.Debugf("bad credentials") return nil, nil, err } // For user logins, update the last login time. // NOTE: this code path is only for local users. When we support remote // user logins with bearer tokens, we will need to make sure that we also // update the last connection times for the environment users there. var lastLogin *time.Time if user, ok := entity.(*state.User); ok { userLastLogin, err := user.LastLogin() if err != nil && !state.IsNeverLoggedInError(err) { return nil, nil, errors.Trace(err) } if lookForEnvUser { envUser, err := st.EnvironmentUser(user.UserTag()) if err != nil { return nil, nil, errors.Wrap(err, common.ErrBadCreds) } // The last connection for the environment takes precedence over // the local user last login time. userLastLogin, err = envUser.LastConnection() if err != nil && !state.IsNeverConnectedError(err) { return nil, nil, errors.Trace(err) } envUser.UpdateLastConnection() } // Only update the user's last login time if it is a successful // login, meaning that if we are logging into an environment, make // sure that there is an environment user in that environment for // this user. user.UpdateLastLogin() lastLogin = &userLastLogin } return entity, lastLogin, nil }
// fetchMachines returns a map from top level machine id to machines, where machines[0] is the host // machine and machines[1..n] are any containers (including nested ones). // // If machineIds is non-nil, only machines whose IDs are in the set are returned. func fetchMachines(st *state.State, machineIds *set.Strings) (map[string][]*state.Machine, error) { v := make(map[string][]*state.Machine) machines, err := st.AllMachines() if err != nil { return nil, err } // AllMachines gives us machines sorted by id. for _, m := range machines { if machineIds != nil && !machineIds.Contains(m.Id()) { continue } parentId, ok := m.ParentId() if !ok { // Only top level host machines go directly into the machine map. v[m.Id()] = []*state.Machine{m} } else { topParentId := state.TopParentId(m.Id()) machines, ok := v[topParentId] if !ok { panic(fmt.Errorf("unexpected machine id %q", parentId)) } machines = append(machines, m) v[topParentId] = machines } } return v, nil }
func getMachine(st *state.State, tag names.Tag) (*state.Machine, error) { m0, err := st.FindEntity(tag) if err != nil { return nil, err } return m0.(*state.Machine), nil }
func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) { res, err := st.Resources() if err != nil { return nil, errors.Trace(err) } return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit}), nil }
func modelIsImporting(st *state.State) (bool, error) { model, err := st.Model() if err != nil { return false, errors.Trace(err) } return model.MigrationMode() == state.MigrationModeImporting, nil }
// NewUniterAPIV1 creates a new instance of the Uniter API, version 1. func NewUniterAPIV1(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*UniterAPIV1, error) { baseAPI, err := newUniterBaseAPI(st, resources, authorizer) if err != nil { return nil, err } accessMachine := func() (common.AuthFunc, error) { switch tag := authorizer.GetAuthTag().(type) { case names.UnitTag: entity, err := st.Unit(tag.Id()) if err != nil { return nil, errors.Trace(err) } machineId, err := entity.AssignedMachineId() if err != nil { return nil, errors.Trace(err) } machineTag := names.NewMachineTag(machineId) return func(tag names.Tag) bool { return tag == machineTag }, nil default: return nil, errors.Errorf("expected names.UnitTag, got %T", tag) } } return &UniterAPIV1{ uniterBaseAPI: *baseAPI, accessMachine: accessMachine, }, nil }
// destroyInstances directly destroys all non-manager, // non-manual machine instances. func destroyInstances(st *state.State, machines []*state.Machine) error { var ids []instance.Id for _, m := range machines { if m.IsManager() { continue } if _, isContainer := m.ParentId(); isContainer { continue } manual, err := m.IsManual() if manual { continue } else if err != nil { return err } id, err := m.InstanceId() if err != nil { continue } ids = append(ids, id) } if len(ids) == 0 { return nil } envcfg, err := st.EnvironConfig() if err != nil { return err } env, err := environs.New(envcfg) if err != nil { return err } return env.StopInstances(ids...) }
// NewRebootAPI creates a new server-side RebootAPI facade. func NewRebootAPI(st *state.State, resources *common.Resources, auth common.Authorizer) (*RebootAPI, error) { if !auth.AuthMachineAgent() { return nil, common.ErrPerm } tag, ok := auth.GetAuthTag().(names.MachineTag) if !ok { return nil, errors.Errorf("Expected names.MachineTag, got %T", auth.GetAuthTag()) } machine, err := st.Machine(tag.Id()) if err != nil { return nil, errors.Trace(err) } canAccess := func() (common.AuthFunc, error) { return auth.AuthOwner, nil } return &RebootAPI{ RebootActionGetter: common.NewRebootActionGetter(st, canAccess), RebootRequester: common.NewRebootRequester(st, canAccess), RebootFlagClearer: common.NewRebootFlagClearer(st, canAccess), st: st, machine: machine, resources: resources, auth: auth, }, nil }
// PutCharm uploads the given charm to provider storage, and adds a // state.Charm to the state. The charm is not uploaded if a charm with // the same URL already exists in the state. // If bumpRevision is true, the charm must be a local directory, // and the revision number will be incremented before pushing. func PutCharm(st *state.State, curl *charm.URL, repo charmrepo.Interface, bumpRevision bool) (*state.Charm, error) { if curl.Revision == -1 { rev, err := charmrepo.Latest(repo, curl) if err != nil { return nil, fmt.Errorf("cannot get latest charm revision: %v", err) } curl = curl.WithRevision(rev) } ch, err := repo.Get(curl) if err != nil { return nil, fmt.Errorf("cannot get charm: %v", err) } if bumpRevision { chd, ok := ch.(*charm.CharmDir) if !ok { return nil, fmt.Errorf("cannot increment revision of charm %q: not a directory", curl) } if err = chd.SetDiskRevision(chd.Revision() + 1); err != nil { return nil, fmt.Errorf("cannot increment revision of charm %q: %v", curl, err) } curl = curl.WithRevision(chd.Revision()) } if sch, err := st.Charm(curl); err == nil { return sch, nil } return addCharm(st, curl, ch) }
// NewControllerAPI creates a new api server endpoint for managing // environments. func NewControllerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*ControllerAPI, error) { if !authorizer.AuthClient() { return nil, errors.Trace(common.ErrPerm) } // Since we know this is a user tag (because AuthClient is true), // we just do the type assertion to the UserTag. apiUser, _ := authorizer.GetAuthTag().(names.UserTag) isAdmin, err := st.IsControllerAdministrator(apiUser) if err != nil { return nil, errors.Trace(err) } // The entire end point is only accessible to controller administrators. if !isAdmin { return nil, errors.Trace(common.ErrPerm) } return &ControllerAPI{ state: st, authorizer: authorizer, apiUser: apiUser, resources: resources, }, nil }
// AddUnits starts n units of the given service using the specified placement // directives to allocate the machines. func AddUnits(st *state.State, svc *state.Service, n int, placement []*instance.Placement) ([]*state.Unit, error) { units := make([]*state.Unit, n) // Hard code for now till we implement a different approach. policy := state.AssignCleanEmpty // All units should have the same networks as the service. networks, err := svc.Networks() if err != nil { return nil, errors.Errorf("cannot get service %q networks", svc.Name()) } // TODO what do we do if we fail half-way through this process? for i := 0; i < n; i++ { unit, err := svc.AddUnit() if err != nil { return nil, errors.Annotatef(err, "cannot add unit %d/%d to service %q", i+1, n, svc.Name()) } // Are there still placement directives to use? if i > len(placement)-1 { if err := st.AssignUnit(unit, policy); err != nil { return nil, errors.Trace(err) } units[i] = unit continue } if err := st.AssignUnitWithPlacement(unit, placement[i], networks); err != nil { return nil, errors.Annotatef(err, "adding new machine to host unit %q", unit.Name()) } units[i] = unit } return units, nil }
// NewUpgraderAPI creates a new server-side UpgraderAPI facade. func NewUpgraderAPI( st *state.State, resources facade.Resources, authorizer facade.Authorizer, ) (*UpgraderAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getCanReadWrite := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } env, err := st.Model() if err != nil { return nil, err } urlGetter := common.NewToolsURLGetter(env.UUID(), st) configGetter := stateenvirons.EnvironConfigGetter{st} return &UpgraderAPI{ ToolsGetter: common.NewToolsGetter(st, configGetter, st, urlGetter, getCanReadWrite), ToolsSetter: common.NewToolsSetter(st, getCanReadWrite), st: st, resources: resources, authorizer: authorizer, }, nil }
// newAuthContext creates a new authentication context for st. func newAuthContext(st *state.State) (*authContext, error) { ctxt := &authContext{ st: st, // TODO(fwereade) 2016-07-21 there should be a clock parameter clock: clock.WallClock, localUserInteractions: authentication.NewInteractions(), } // Create a bakery service for discharging third-party caveats for // local user authentication. This service does not persist keys; // its macaroons should be very short-lived. localUserThirdPartyBakeryService, _, err := newBakeryService(st, nil, nil) if err != nil { return nil, errors.Trace(err) } ctxt.localUserThirdPartyBakeryService = localUserThirdPartyBakeryService // Create a bakery service for local user authentication. This service // persists keys into MongoDB in a TTL collection. store, err := st.NewBakeryStorage() if err != nil { return nil, errors.Trace(err) } locator := bakeryServicePublicKeyLocator{ctxt.localUserThirdPartyBakeryService} localUserBakeryService, localUserBakeryServiceKey, err := newBakeryService( st, store, locator, ) if err != nil { return nil, errors.Trace(err) } ctxt.localUserBakeryService = &expirableStorageBakeryService{ localUserBakeryService, localUserBakeryServiceKey, store, locator, } return ctxt, nil }