// cacheAPIInfo updates the local environment settings (.jenv file) // with the provided apiInfo, assuming we've just successfully // connected to the API server. func cacheAPIInfo(st api.Connection, info configstore.EnvironInfo, apiInfo *api.Info) (err error) { defer errors.DeferredAnnotatef(&err, "failed to cache API credentials") var modelUUID string if names.IsValidModel(apiInfo.ModelTag.Id()) { modelUUID = apiInfo.ModelTag.Id() } else { // For backwards-compatibility, we have to allow connections // with an empty UUID. Login will work for the same reasons. logger.Warningf("ignoring invalid cached API endpoint model UUID %v", apiInfo.ModelTag.Id()) } hostPorts, err := network.ParseHostPorts(apiInfo.Addrs...) if err != nil { return errors.Annotatef(err, "invalid API addresses %v", apiInfo.Addrs) } addrConnectedTo, err := network.ParseHostPorts(st.Addr()) if err != nil { // Should never happen, since we've just connected with it. return errors.Annotatef(err, "invalid API address %q", st.Addr()) } addrs, hostnames, addrsChanged := PrepareEndpointsForCaching( info, [][]network.HostPort{hostPorts}, addrConnectedTo[0], ) endpoint := configstore.APIEndpoint{ CACert: string(apiInfo.CACert), ModelUUID: modelUUID, } if addrsChanged { endpoint.Addresses = addrs endpoint.Hostnames = hostnames } info.SetAPIEndpoint(endpoint) tag, ok := apiInfo.Tag.(names.UserTag) if !ok { return errors.Errorf("apiInfo.Tag was of type %T, expecting names.UserTag", apiInfo.Tag) } info.SetAPICredentials(configstore.APICredentials{ // This looks questionable. We have a tag, say "user-admin", but then only // the Id portion of the tag is recorded, "admin", so this is really a // username, not a tag, and cannot be reconstructed accurately. User: tag.Id(), Password: apiInfo.Password, }) return info.Write() }
func (c *LoginCommand) cacheConnectionInfo(serverDetails envcmd.ServerFile, apiState api.Connection) (configstore.EnvironInfo, error) { store, err := configstore.Default() if err != nil { return nil, errors.Trace(err) } serverInfo := store.CreateInfo(c.Name) serverTag, err := apiState.ServerTag() if err != nil { return nil, errors.Wrap(err, errors.New("juju system too old to support login")) } connectedAddresses, err := network.ParseHostPorts(apiState.Addr()) if err != nil { // Should never happen, since we've just connected with it. return nil, errors.Annotatef(err, "invalid API address %q", apiState.Addr()) } addressConnectedTo := connectedAddresses[0] addrs, hosts, changed := juju.PrepareEndpointsForCaching(serverInfo, apiState.APIHostPorts(), addressConnectedTo) if !changed { logger.Infof("api addresses: %v", apiState.APIHostPorts()) logger.Infof("address connected to: %v", addressConnectedTo) return nil, errors.New("no addresses returned from prepare for caching") } serverInfo.SetAPICredentials( configstore.APICredentials{ User: serverDetails.Username, Password: serverDetails.Password, }) serverInfo.SetAPIEndpoint(configstore.APIEndpoint{ Addresses: addrs, Hostnames: hosts, CACert: serverDetails.CACert, ServerUUID: serverTag.Id(), }) if err = serverInfo.Write(); err != nil { return nil, errors.Trace(err) } return serverInfo, nil }
// updateSupportedContainers records in state that a machine can run the specified containers. // It starts a watcher and when a container of a given type is first added to the machine, // the watcher is killed, the machine is set up to be able to start containers of the given type, // and a suitable provisioner is started. func (a *MachineAgent) updateSupportedContainers( runner worker.Runner, st api.Connection, containers []instance.ContainerType, agentConfig agent.Config, ) error { pr := st.Provisioner() tag := agentConfig.Tag().(names.MachineTag) machine, err := pr.Machine(tag) if errors.IsNotFound(err) || err == nil && machine.Life() == params.Dead { return worker.ErrTerminateAgent } if err != nil { return errors.Annotatef(err, "cannot load machine %s from state", tag) } if len(containers) == 0 { if err := machine.SupportsNoContainers(); err != nil { return errors.Annotatef(err, "clearing supported containers for %s", tag) } return nil } if err := machine.SetSupportedContainers(containers...); err != nil { return errors.Annotatef(err, "setting supported containers for %s", tag) } initLock, err := cmdutil.HookExecutionLock(agentConfig.DataDir()) if err != nil { return err } // Start the watcher to fire when a container is first requested on the machine. modelUUID, err := st.ModelTag() if err != nil { return err } watcherName := fmt.Sprintf("%s-container-watcher", machine.Id()) // There may not be a CA certificate private key available, and without // it we can't ensure that other Juju nodes can connect securely, so only // use an image URL getter if there's a private key. var imageURLGetter container.ImageURLGetter if agentConfig.Value(agent.AllowsSecureConnection) == "true" { cfg, err := pr.ModelConfig() if err != nil { return errors.Annotate(err, "unable to get environ config") } imageURLGetter = container.NewImageURLGetter( // Explicitly call the non-named constructor so if anyone // adds additional fields, this fails. container.ImageURLGetterConfig{ ServerRoot: st.Addr(), ModelUUID: modelUUID.Id(), CACert: []byte(agentConfig.CACert()), CloudimgBaseUrl: cfg.CloudImageBaseURL(), Stream: cfg.ImageStream(), ImageDownloadFunc: container.ImageDownloadURL, }) } params := provisioner.ContainerSetupParams{ Runner: runner, WorkerName: watcherName, SupportedContainers: containers, ImageURLGetter: imageURLGetter, Machine: machine, Provisioner: pr, Config: agentConfig, InitLock: initLock, } handler := provisioner.NewContainerSetupHandler(params) a.startWorkerAfterUpgrade(runner, watcherName, func() (worker.Worker, error) { w, err := watcher.NewStringsWorker(watcher.StringsConfig{ Handler: handler, }) if err != nil { return nil, errors.Annotatef(err, "cannot start %s worker", watcherName) } return w, nil }) return nil }