func transmitVendorMetrics(st *state.State) (bool, error) { cfg, err := st.ModelConfig() if err != nil { return false, errors.Annotatef(err, "failed to get model config for %s", st.ModelTag()) } return cfg.TransmitVendorMetrics(), nil }
// ResolveCharm resolves the best available charm URLs with series, for charm // locations without a series specified. func ResolveCharms(st *state.State, args params.ResolveCharms) (params.ResolveCharmResults, error) { var results params.ResolveCharmResults envConfig, err := st.ModelConfig() if err != nil { return params.ResolveCharmResults{}, err } repo := config.SpecializeCharmRepo( NewCharmStoreRepo(csclient.New(csclient.Params{})), envConfig) for _, ref := range args.References { result := params.ResolveCharmResult{} curl, err := charm.ParseURL(ref) if err != nil { result.Error = err.Error() } else { curl, err := resolveCharm(curl, repo) if err != nil { result.Error = err.Error() } else { result.URL = curl.String() } } results.URLs = append(results.URLs, result) } return results, nil }
// newExternalMacaroonAuth returns an authenticator that can authenticate // macaroon-based logins for external users. This is just a helper function // for authCtxt.macaroonAuth. func newExternalMacaroonAuth(st *state.State) (*authentication.ExternalMacaroonAuthenticator, error) { envCfg, err := st.ModelConfig() if err != nil { return nil, errors.Annotate(err, "cannot get model config") } idURL := envCfg.IdentityURL() if idURL == "" { return nil, errMacaroonAuthNotConfigured } // The identity server has been configured, // so configure the bakery service appropriately. idPK := envCfg.IdentityPublicKey() if idPK == nil { // No public key supplied - retrieve it from the identity manager. idPK, err = httpbakery.PublicKeyForLocation(http.DefaultClient, idURL) if err != nil { return nil, errors.Annotate(err, "cannot get identity public key") } } svc, err := newBakeryService(st, bakery.PublicKeyLocatorMap{ idURL: idPK, }) if err != nil { return nil, errors.Annotate(err, "cannot make bakery service") } var auth authentication.ExternalMacaroonAuthenticator auth.Service = svc auth.Macaroon, err = svc.NewMacaroon("api-login", nil, nil) if err != nil { return nil, errors.Annotate(err, "cannot make macaroon") } auth.IdentityLocation = idURL return &auth, nil }
// GetEnviron returns the environs.Environ ("provider") associated // with the model. func GetEnviron(st *state.State) (environs.Environ, error) { envcfg, err := st.ModelConfig() if err != nil { return nil, errors.Trace(err) } env, err := environs.New(envcfg) return env, errors.Trace(err) }
// New returns a new worker that maintains the mongo replica set // with respect to the given state. func New(st *state.State) (worker.Worker, error) { cfg, err := st.ModelConfig() if err != nil { return nil, err } return newWorker(&stateShim{ State: st, mongoPort: cfg.StatePort(), apiPort: cfg.APIPort(), }, newPublisher(st, cfg.PreferIPv6())), nil }
// New returns a new worker that maintains the mongo replica set // with respect to the given state. func New(st *state.State) (worker.Worker, error) { cfg, err := st.ModelConfig() if err != nil { return nil, err } shim := &stateShim{ State: st, mongoPort: cfg.StatePort(), apiPort: cfg.APIPort(), } supportsSpaces := networkingcommon.SupportsSpaces(shim) == nil return newWorker(shim, newPublisher(st, cfg.PreferIPv6()), supportsSpaces) }
// fetchAndCacheTools fetches tools with the specified version by searching for a URL // in simplestreams and GETting it, caching the result in tools storage before returning // to the caller. func (h *toolsDownloadHandler) fetchAndCacheTools(v version.Binary, stor binarystorage.Storage, st *state.State) (io.ReadCloser, error) { envcfg, err := st.ModelConfig() if err != nil { return nil, err } env, err := environs.New(envcfg) if err != nil { return nil, err } tools, err := envtools.FindExactTools(env, v.Number, v.Series, v.Arch) if err != nil { return nil, err } // No need to verify the server's identity because we verify the SHA-256 hash. logger.Infof("fetching %v tools from %v", v, tools.URL) resp, err := utils.GetNonValidatingHTTPClient().Get(tools.URL) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { msg := fmt.Sprintf("bad HTTP response: %v", resp.Status) if body, err := ioutil.ReadAll(resp.Body); err == nil { msg += fmt.Sprintf(" (%s)", bytes.TrimSpace(body)) } return nil, errors.New(msg) } data, sha256, err := readAndHash(resp.Body) if err != nil { return nil, err } if int64(len(data)) != tools.Size { return nil, errors.Errorf("size mismatch for %s", tools.URL) } if sha256 != tools.SHA256 { return nil, errors.Errorf("hash mismatch for %s", tools.URL) } // Cache tarball in tools storage before returning. metadata := binarystorage.Metadata{ Version: v.String(), Size: tools.Size, SHA256: tools.SHA256, } if err := stor.Add(bytes.NewReader(data), metadata); err != nil { return nil, errors.Annotate(err, "error caching tools") } return ioutil.NopCloser(bytes.NewReader(data)), nil }
func makeModelInfo(st *state.State) (coremigration.ModelInfo, error) { var empty coremigration.ModelInfo model, err := st.Model() if err != nil { return empty, errors.Trace(err) } conf, err := st.ModelConfig() if err != nil { return empty, errors.Trace(err) } agentVersion, _ := conf.AgentVersion() return coremigration.ModelInfo{ UUID: model.UUID(), Name: model.Name(), Owner: model.Owner(), AgentVersion: agentVersion, }, nil }
func updateSecrets(env environs.Environ, st *state.State) error { secrets, err := env.Provider().SecretAttrs(env.Config()) if err != nil { return err } cfg, err := st.ModelConfig() if err != nil { return err } secretAttrs := make(map[string]interface{}) attrs := cfg.AllAttrs() for k, v := range secrets { if _, exists := attrs[k]; exists { // Environment already has secrets. Won't send again. return nil } else { secretAttrs[k] = v } } return st.UpdateModelConfig(secretAttrs, nil, nil) }
// newExternalMacaroonAuth returns an authenticator that can authenticate // macaroon-based logins for external users. This is just a helper function // for authCtxt.macaroonAuth. func newExternalMacaroonAuth(st *state.State) (*authentication.ExternalMacaroonAuthenticator, error) { envCfg, err := st.ModelConfig() if err != nil { return nil, errors.Annotate(err, "cannot get model config") } idURL := envCfg.IdentityURL() if idURL == "" { return nil, errMacaroonAuthNotConfigured } // The identity server has been configured, // so configure the bakery service appropriately. idPK := envCfg.IdentityPublicKey() if idPK == nil { // No public key supplied - retrieve it from the identity manager. idPK, err = httpbakery.PublicKeyForLocation(http.DefaultClient, idURL) if err != nil { return nil, errors.Annotate(err, "cannot get identity public key") } } // We pass in nil for the storage, which leads to in-memory storage // being used. We only use in-memory storage for now, since we never // expire the keys, and don't want garbage to accumulate. // // TODO(axw) we should store the key in mongo, so that multiple servers // can authenticate. That will require that we encode the server's ID // in the macaroon ID so that servers don't overwrite each others' keys. svc, _, err := newBakeryService(st, nil, bakery.PublicKeyLocatorMap{idURL: idPK}) if err != nil { return nil, errors.Annotate(err, "cannot make bakery service") } var auth authentication.ExternalMacaroonAuthenticator auth.Service = svc auth.Macaroon, err = svc.NewMacaroon("api-login", nil, nil) if err != nil { return nil, errors.Annotate(err, "cannot make macaroon") } auth.IdentityLocation = idURL return &auth, nil }
// InstanceConfig returns information from the environment config that // is needed for machine cloud-init (for non-controllers only). It // is exposed for testing purposes. // TODO(rog) fix environs/manual tests so they do not need to call this, or move this elsewhere. func InstanceConfig(st *state.State, machineId, nonce, dataDir string) (*instancecfg.InstanceConfig, error) { environConfig, err := st.ModelConfig() if err != nil { return nil, err } // Get the machine so we can get its series and arch. // If the Arch is not set in hardware-characteristics, // an error is returned. machine, err := st.Machine(machineId) if err != nil { return nil, err } hc, err := machine.HardwareCharacteristics() if err != nil { return nil, err } if hc.Arch == nil { return nil, fmt.Errorf("arch is not set for %q", machine.Tag()) } // Find the appropriate tools information. agentVersion, ok := environConfig.AgentVersion() if !ok { return nil, errors.New("no agent version set in model configuration") } environment, err := st.Model() if err != nil { return nil, err } urlGetter := common.NewToolsURLGetter(environment.UUID(), st) toolsFinder := common.NewToolsFinder(st, st, urlGetter) findToolsResult, err := toolsFinder.FindTools(params.FindToolsParams{ Number: agentVersion, MajorVersion: -1, MinorVersion: -1, Series: machine.Series(), Arch: *hc.Arch, }) if err != nil { return nil, err } if findToolsResult.Error != nil { return nil, findToolsResult.Error } tools := findToolsResult.List[0] // Find the API endpoints. env, err := environs.New(environConfig) if err != nil { return nil, err } apiInfo, err := environs.APIInfo(env) if err != nil { return nil, err } auth := authentication.NewAuthenticator(st.MongoConnectionInfo(), apiInfo) mongoInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, err } // Find requested networks. networks, err := machine.RequestedNetworks() if err != nil { return nil, err } // Figure out if secure connections are supported. info, err := st.StateServingInfo() if err != nil { return nil, err } secureServerConnection := info.CAPrivateKey != "" icfg, err := instancecfg.NewInstanceConfig(machineId, nonce, env.Config().ImageStream(), machine.Series(), "", secureServerConnection, networks, mongoInfo, apiInfo, ) if err != nil { return nil, err } if dataDir != "" { icfg.DataDir = dataDir } icfg.Tools = tools err = instancecfg.FinishInstanceConfig(icfg, environConfig) if err != nil { return nil, err } return icfg, nil }
// AddCharmWithAuthorization adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). // // The authorization macaroon, args.CharmStoreMacaroon, may be // omitted, in which case this call is equivalent to AddCharm. func AddCharmWithAuthorization(st *state.State, args params.AddCharmWithAuthorization) error { charmURL, err := charm.ParseURL(args.URL) if err != nil { return err } if charmURL.Schema != "cs" { return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") } if charmURL.Revision < 0 { return fmt.Errorf("charm URL must include revision") } // First, check if a pending or a real charm exists in state. stateCharm, err := st.PrepareStoreCharmUpload(charmURL) if err != nil { return err } if stateCharm.IsUploaded() { // Charm already in state (it was uploaded already). return nil } // Get the charm and its information from the store. envConfig, err := st.ModelConfig() if err != nil { return err } csURL, err := url.Parse(csclient.ServerURL) if err != nil { return err } csParams := charmrepo.NewCharmStoreParams{ URL: csURL.String(), HTTPClient: httpbakery.NewHTTPClient(), } if args.CharmStoreMacaroon != nil { // Set the provided charmstore authorizing macaroon // as a cookie in the HTTP client. // TODO discharge any third party caveats in the macaroon. ms := []*macaroon.Macaroon{args.CharmStoreMacaroon} httpbakery.SetCookie(csParams.HTTPClient.Jar, csURL, ms) } repo := config.SpecializeCharmRepo( NewCharmStore(csParams), envConfig, ) downloadedCharm, err := repo.Get(charmURL) if err != nil { cause := errors.Cause(err) if httpbakery.IsDischargeError(cause) || httpbakery.IsInteractionError(cause) { return errors.NewUnauthorized(err, "") } return errors.Trace(err) } // Open it and calculate the SHA256 hash. downloadedBundle, ok := downloadedCharm.(*charm.CharmArchive) if !ok { return errors.Errorf("expected a charm archive, got %T", downloadedCharm) } archive, err := os.Open(downloadedBundle.Path) if err != nil { return errors.Annotate(err, "cannot read downloaded charm") } defer archive.Close() bundleSHA256, size, err := utils.ReadSHA256(archive) if err != nil { return errors.Annotate(err, "cannot calculate SHA256 hash of charm") } if _, err := archive.Seek(0, 0); err != nil { return errors.Annotate(err, "cannot rewind charm archive") } // Store the charm archive in environment storage. return StoreCharmArchive( st, charmURL, downloadedCharm, archive, size, bundleSHA256, ) }
// InstanceConfig returns information from the environment config that // is needed for machine cloud-init (for non-controllers only). It // is exposed for testing purposes. // TODO(rog) fix environs/manual tests so they do not need to call this, or move this elsewhere. func InstanceConfig(st *state.State, machineId, nonce, dataDir string) (*instancecfg.InstanceConfig, error) { environConfig, err := st.ModelConfig() if err != nil { return nil, errors.Annotate(err, "getting model config") } // Get the machine so we can get its series and arch. // If the Arch is not set in hardware-characteristics, // an error is returned. machine, err := st.Machine(machineId) if err != nil { return nil, errors.Annotate(err, "getting machine") } hc, err := machine.HardwareCharacteristics() if err != nil { return nil, errors.Annotate(err, "getting machine hardware characteristics") } if hc.Arch == nil { return nil, fmt.Errorf("arch is not set for %q", machine.Tag()) } // Find the appropriate tools information. agentVersion, ok := environConfig.AgentVersion() if !ok { return nil, errors.New("no agent version set in model configuration") } environment, err := st.Model() if err != nil { return nil, errors.Annotate(err, "getting state model") } urlGetter := common.NewToolsURLGetter(environment.UUID(), st) toolsFinder := common.NewToolsFinder(st, st, urlGetter) findToolsResult, err := toolsFinder.FindTools(params.FindToolsParams{ Number: agentVersion, MajorVersion: -1, MinorVersion: -1, Series: machine.Series(), Arch: *hc.Arch, }) if err != nil { return nil, errors.Annotate(err, "finding tools") } if findToolsResult.Error != nil { return nil, errors.Annotate(findToolsResult.Error, "finding tools") } tools := findToolsResult.List[0] // Get the API connection info; attempt all API addresses. apiHostPorts, err := st.APIHostPorts() if err != nil { return nil, errors.Annotate(err, "getting API addresses") } apiAddrs := make(set.Strings) for _, hostPorts := range apiHostPorts { for _, hp := range hostPorts { apiAddrs.Add(hp.NetAddr()) } } apiInfo := &api.Info{ Addrs: apiAddrs.SortedValues(), CACert: st.CACert(), ModelTag: st.ModelTag(), } auth := authentication.NewAuthenticator(st.MongoConnectionInfo(), apiInfo) mongoInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, errors.Annotate(err, "setting up machine authentication") } // Find requested networks. networks, err := machine.RequestedNetworks() if err != nil { return nil, errors.Annotate(err, "getting requested networks for machine") } // Figure out if secure connections are supported. info, err := st.StateServingInfo() if err != nil { return nil, errors.Annotate(err, "getting state serving info") } secureServerConnection := info.CAPrivateKey != "" icfg, err := instancecfg.NewInstanceConfig(machineId, nonce, environConfig.ImageStream(), machine.Series(), "", secureServerConnection, networks, mongoInfo, apiInfo, ) if err != nil { return nil, errors.Annotate(err, "initializing instance config") } if dataDir != "" { icfg.DataDir = dataDir } icfg.Tools = tools err = instancecfg.FinishInstanceConfig(icfg, environConfig) if err != nil { return nil, errors.Annotate(err, "finishing instance config") } return icfg, nil }
// AddCharmWithAuthorization adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). // // The authorization macaroon, args.CharmStoreMacaroon, may be // omitted, in which case this call is equivalent to AddCharm. func AddCharmWithAuthorization(st *state.State, args params.AddCharmWithAuthorization) error { charmURL, err := charm.ParseURL(args.URL) if err != nil { return err } if charmURL.Schema != "cs" { return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") } if charmURL.Revision < 0 { return fmt.Errorf("charm URL must include revision") } // First, check if a pending or a real charm exists in state. stateCharm, err := st.PrepareStoreCharmUpload(charmURL) if err != nil { return err } if stateCharm.IsUploaded() { // Charm already in state (it was uploaded already). return nil } // Open a charm store client. repo, err := openCSRepo(args) if err != nil { return err } envConfig, err := st.ModelConfig() if err != nil { return err } repo = config.SpecializeCharmRepo(repo, envConfig).(*charmrepo.CharmStore) // Get the charm and its information from the store. downloadedCharm, err := repo.Get(charmURL) if err != nil { cause := errors.Cause(err) if httpbakery.IsDischargeError(cause) || httpbakery.IsInteractionError(cause) { return errors.NewUnauthorized(err, "") } return errors.Trace(err) } if err := checkMinVersion(downloadedCharm); err != nil { return errors.Trace(err) } // Open it and calculate the SHA256 hash. downloadedBundle, ok := downloadedCharm.(*charm.CharmArchive) if !ok { return errors.Errorf("expected a charm archive, got %T", downloadedCharm) } archive, err := os.Open(downloadedBundle.Path) if err != nil { return errors.Annotate(err, "cannot read downloaded charm") } defer archive.Close() bundleSHA256, size, err := utils.ReadSHA256(archive) if err != nil { return errors.Annotate(err, "cannot calculate SHA256 hash of charm") } if _, err := archive.Seek(0, 0); err != nil { return errors.Annotate(err, "cannot rewind charm archive") } // Store the charm archive in environment storage. return StoreCharmArchive( st, CharmArchive{ ID: charmURL, Charm: downloadedCharm, Data: archive, Size: size, SHA256: bundleSHA256, }, ) }