func checkToolsAvailability(cfg *config.Config, finder toolsFinder) (version.Number, error) { currentVersion, ok := cfg.AgentVersion() if !ok || currentVersion == version.Zero { return version.Zero, nil } env, err := newEnvirons(cfg) if err != nil { return version.Zero, errors.Annotatef(err, "cannot make environ") } // finder receives major and minor as parameters as it uses them to filter versions and // only return patches for the passed major.minor (from major.minor.patch). // We'll try the released stream first, then fall back to the current configured stream // if no released tools are found. vers, err := finder(env, currentVersion.Major, currentVersion.Minor, tools.ReleasedStream, coretools.Filter{}) preferredStream := tools.PreferredStream(¤tVersion, cfg.Development(), cfg.AgentStream()) if preferredStream != tools.ReleasedStream && errors.Cause(err) == coretools.ErrNoMatches { vers, err = finder(env, currentVersion.Major, currentVersion.Minor, preferredStream, coretools.Filter{}) } if err != nil { return version.Zero, errors.Annotatef(err, "cannot find available tools") } // Newest also returns a list of the items in this list matching with the // newest version. newest, _ := vers.Newest() return newest, nil }
// DefaultVersions returns a slice of unique 'versions' for the current // environment's preferred series and host architecture, as well supported LTS // series for the host architecture. Additionally, it ensures that 'versions' // for amd64 are returned if that is not the current host's architecture. func DefaultVersions(conf *config.Config) []version.Binary { var versions []version.Binary supported := series.SupportedLts() defaultSeries := set.NewStrings(supported...) defaultSeries.Add(config.PreferredSeries(conf)) defaultSeries.Add(series.HostSeries()) agentVersion, set := conf.AgentVersion() if !set { agentVersion = jujuversion.Current } for _, s := range defaultSeries.Values() { versions = append(versions, version.Binary{ Number: agentVersion, Arch: arch.HostArch(), Series: s, }) if arch.HostArch() != "amd64" { versions = append(versions, version.Binary{ Number: agentVersion, Arch: "amd64", Series: s, }) } } return versions }
// checkEnvironConfig returns an error if the config is definitely invalid. func checkEnvironConfig(cfg *config.Config) error { if cfg.AdminSecret() != "" { return fmt.Errorf("admin-secret should never be written to the state") } if _, ok := cfg.AgentVersion(); !ok { return fmt.Errorf("agent-version must always be set in state") } return nil }
func (c *ModelConfigCreator) checkVersion(base *config.Config, attrs map[string]interface{}) error { baseVersion, ok := base.AgentVersion() if !ok { return errors.Errorf("agent-version not found in base config") } // If there is no agent-version specified, use the current version. // otherwise we need to check for tools value, ok := attrs["agent-version"] if !ok { attrs["agent-version"] = baseVersion.String() return nil } versionStr, ok := value.(string) if !ok { return errors.Errorf("agent-version must be a string but has type '%T'", value) } versionNumber, err := version.Parse(versionStr) if err != nil { return errors.Trace(err) } n := versionNumber.Compare(baseVersion) switch { case n > 0: return errors.Errorf( "agent-version (%s) cannot be greater than the controller (%s)", versionNumber, baseVersion, ) case n == 0: // If the version is the same as the base config, // then assume tools are available. return nil case n < 0: if c.FindTools == nil { return errors.New( "agent-version does not match base config, " + "and no tools-finder is supplied", ) } } // Look to see if we have tools available for that version. list, err := c.FindTools(versionNumber) if err != nil { return errors.Trace(err) } if len(list) == 0 { return errors.Errorf("no tools found for version %s", versionNumber) } logger.Tracef("found tools: %#v", list) return nil }
// bootstrapConfig returns a copy of the supplied configuration with the // admin-secret and ca-private-key attributes removed. If the resulting // config is not suitable for bootstrapping an environment, an error is // returned. // This function is copied from environs in here so we can avoid an import loop func bootstrapConfig(cfg *config.Config) (*config.Config, error) { m := cfg.AllAttrs() // We never want to push admin-secret or the root CA private key to the cloud. delete(m, "admin-secret") delete(m, "ca-private-key") cfg, err := config.New(config.NoDefaults, m) if err != nil { return nil, err } if _, ok := cfg.AgentVersion(); !ok { return nil, fmt.Errorf("model configuration has no agent-version") } return cfg, nil }
// checkModelConfig returns an error if the config is definitely invalid. func checkModelConfig(cfg *config.Config) error { allAttrs := cfg.AllAttrs() for _, attr := range disallowedModelConfigAttrs { if _, ok := allAttrs[attr]; ok { return errors.Errorf(attr + " should never be written to the state") } } if _, ok := cfg.AgentVersion(); !ok { return errors.Errorf("agent-version must always be set in state") } for attr := range allAttrs { if controller.ControllerOnlyAttribute(attr) { return errors.Errorf("cannot set controller attribute %q on a model", attr) } } return nil }
// Open implements environs.EnvironProvider.Open. func (environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening environment %q", cfg.Name()) if _, ok := cfg.AgentVersion(); !ok { newCfg, err := cfg.Apply(map[string]interface{}{ "agent-version": version.Current.Number.String(), }) if err != nil { return nil, err } cfg = newCfg } // Set the "namespace" attribute. We do this here, and not in Prepare, // for backwards compatibility: older versions did not store the namespace // in config. if namespace, _ := cfg.UnknownAttrs()["namespace"].(string); namespace == "" { username := os.Getenv("USER") if username == "" { u, err := userCurrent() if err != nil { return nil, fmt.Errorf("failed to determine username for namespace: %v", err) } username = u.Username } var err error namespace = fmt.Sprintf("%s-%s", username, cfg.Name()) cfg, err = cfg.Apply(map[string]interface{}{"namespace": namespace}) if err != nil { return nil, fmt.Errorf("failed to create namespace: %v", err) } } // Do the initial validation on the config. localConfig, err := providerInstance.newConfig(cfg) if err != nil { return nil, err } if err := VerifyPrerequisites(localConfig.container()); err != nil { return nil, fmt.Errorf("failed verification of local provider prerequisites: %v", err) } environ := &localEnviron{name: cfg.Name()} if err := environ.SetConfig(cfg); err != nil { return nil, fmt.Errorf("failure setting config: %v", err) } return environ, nil }
// initVersions collects state relevant to an upgrade decision. The returned // agent and client versions, and the list of currently available tools, will // always be accurate; the chosen version, and the flag indicating development // mode, may remain blank until uploadTools or validate is called. func (c *UpgradeJujuCommand) initVersions(client *api.Client, cfg *config.Config) (*upgradeContext, error) { agent, ok := cfg.AgentVersion() if !ok { // Can't happen. In theory. return nil, fmt.Errorf("incomplete environment configuration") } if c.Version == agent { return nil, errUpToDate } clientVersion := version.Current.Number findResult, err := client.FindTools(clientVersion.Major, -1, "", "") var availableTools coretools.List if params.IsCodeNotImplemented(err) { availableTools, err = findTools1dot17(cfg) } else { availableTools = findResult.List } if err != nil { return nil, err } err = findResult.Error if findResult.Error != nil { if !params.IsCodeNotFound(err) { return nil, err } if !c.UploadTools { // No tools found and we shouldn't upload any, so if we are not asking for a // major upgrade, pretend there is no more recent version available. if c.Version == version.Zero && agent.Major == clientVersion.Major { return nil, errUpToDate } return nil, err } } return &upgradeContext{ agent: agent, client: clientVersion, chosen: c.Version, tools: availableTools, apiClient: client, config: cfg, }, nil }
func checkToolsAvailability(cfg *config.Config, finder toolsFinder) (version.Number, error) { currentVersion, ok := cfg.AgentVersion() if !ok || currentVersion == version.Zero { return version.Zero, nil } env, err := newEnvirons(cfg) if err != nil { return version.Zero, errors.Annotatef(err, "cannot make environ") } // finder receives major and minor as parameters as it uses them to filter versions and // only return patches for the passed major.minor (from major.minor.patch). vers, err := finder(env, currentVersion.Major, currentVersion.Minor, tools.ReleasedStream, coretools.Filter{}) if err != nil { return version.Zero, errors.Annotatef(err, "canot find available tools") } // Newest also returns a list of the items in this list matching with the // newest version. newest, _ := vers.Newest() return newest, nil }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (p environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { attrs := map[string]interface{}{ // We must not proxy SSH through the API server in a // local provider environment. Besides not being useful, // it may not work; there is no requirement for sshd to // be available on machine-0. "proxy-ssh": false, } if _, ok := cfg.AgentVersion(); !ok { attrs["agent-version"] = version.Current.Number.String() } if namespace, _ := cfg.UnknownAttrs()["namespace"].(string); namespace == "" { username := os.Getenv("USER") if username == "" { u, err := userCurrent() if err != nil { return nil, errors.Annotate(err, "failed to determine username for namespace") } username = u.Username } attrs["namespace"] = fmt.Sprintf("%s-%s", username, cfg.Name()) } setIfNotBlank := func(key, value string) { if value != "" { attrs[key] = value } } // If the user has specified no values for any of the four normal // proxies, then look in the environment and set them. logger.Tracef("Look for proxies?") if cfg.HttpProxy() == "" && cfg.HttpsProxy() == "" && cfg.FtpProxy() == "" && cfg.NoProxy() == "" { proxySettings := proxy.DetectProxies() logger.Tracef("Proxies detected %#v", proxySettings) setIfNotBlank(config.HttpProxyKey, proxySettings.Http) setIfNotBlank(config.HttpsProxyKey, proxySettings.Https) setIfNotBlank(config.FtpProxyKey, proxySettings.Ftp) setIfNotBlank(config.NoProxyKey, proxySettings.NoProxy) } if version.Current.OS == version.Ubuntu { if cfg.AptHttpProxy() == "" && cfg.AptHttpsProxy() == "" && cfg.AptFtpProxy() == "" { proxySettings, err := detectPackageProxies() if err != nil { return nil, errors.Trace(err) } setIfNotBlank(config.AptHttpProxyKey, proxySettings.Http) setIfNotBlank(config.AptHttpsProxyKey, proxySettings.Https) setIfNotBlank(config.AptFtpProxyKey, proxySettings.Ftp) } } cfg, err := cfg.Apply(attrs) if err != nil { return nil, errors.Trace(err) } // Make sure everything is valid. cfg, err = p.Validate(cfg, nil) if err != nil { return nil, errors.Trace(err) } return cfg, nil }
// PrepareForBootstrap implements environs.EnvironProvider.PrepareForBootstrap. func (p environProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { attrs := map[string]interface{}{ // We must not proxy SSH through the API server in a // local provider environment. Besides not being useful, // it may not work; there is no requirement for sshd to // be available on machine-0. "proxy-ssh": false, } if _, ok := cfg.AgentVersion(); !ok { attrs["agent-version"] = version.Current.String() } if namespace, _ := cfg.UnknownAttrs()["namespace"].(string); namespace == "" { username := os.Getenv("USER") if username == "" { u, err := userCurrent() if err != nil { return nil, errors.Annotate(err, "failed to determine username for namespace") } username = u.Username } attrs["namespace"] = fmt.Sprintf("%s-%s", username, cfg.Name()) } setIfNotBlank := func(key, value string) { if value != "" { attrs[key] = value } } // If the user has specified no values for any of the four normal // proxies, then look in the environment and set them. logger.Tracef("Look for proxies?") if cfg.HttpProxy() == "" && cfg.HttpsProxy() == "" && cfg.FtpProxy() == "" && cfg.NoProxy() == "" { proxySettings := proxy.DetectProxies() logger.Tracef("Proxies detected %#v", proxySettings) setIfNotBlank(config.HttpProxyKey, proxySettings.Http) setIfNotBlank(config.HttpsProxyKey, proxySettings.Https) setIfNotBlank(config.FtpProxyKey, proxySettings.Ftp) setIfNotBlank(config.NoProxyKey, proxySettings.NoProxy) } if jujuos.HostOS() == jujuos.Ubuntu { if cfg.AptHttpProxy() == "" && cfg.AptHttpsProxy() == "" && cfg.AptFtpProxy() == "" { proxySettings, err := detectPackageProxies() if err != nil { return nil, errors.Trace(err) } setIfNotBlank(config.AptHttpProxyKey, proxySettings.Http) setIfNotBlank(config.AptHttpsProxyKey, proxySettings.Https) setIfNotBlank(config.AptFtpProxyKey, proxySettings.Ftp) } } cfg, err := cfg.Apply(attrs) if err != nil { return nil, errors.Trace(err) } // Make sure everything is valid. cfg, err = p.Validate(cfg, nil) if err != nil { return nil, errors.Trace(err) } // The user must not set bootstrap-ip; this is determined by the provider, // and its presence used to determine whether the environment has yet been // bootstrapped. if _, ok := cfg.UnknownAttrs()["bootstrap-ip"]; ok { return nil, errors.Errorf("bootstrap-ip must not be specified") } err = checkLocalPort(cfg.StatePort(), "state port") if err != nil { return nil, errors.Trace(err) } err = checkLocalPort(cfg.APIPort(), "API port") if err != nil { return nil, errors.Trace(err) } return p.Open(cfg) }
func finalizeInstanceBootstrapConfig( ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig, args BootstrapParams, cfg *config.Config, customImageMetadata []*imagemetadata.ImageMetadata, ) error { if icfg.APIInfo != nil || icfg.Controller.MongoInfo != nil { return errors.New("machine configuration already has api/state info") } controllerCfg := icfg.Controller.Config caCert, hasCACert := controllerCfg.CACert() if !hasCACert { return errors.New("controller configuration has no ca-cert") } icfg.APIInfo = &api.Info{ Password: args.AdminSecret, CACert: caCert, ModelTag: names.NewModelTag(cfg.UUID()), } icfg.Controller.MongoInfo = &mongo.MongoInfo{ Password: args.AdminSecret, Info: mongo.Info{CACert: caCert}, } // These really are directly relevant to running a controller. // Initially, generate a controller certificate with no host IP // addresses in the SAN field. Once the controller is up and the // NIC addresses become known, the certificate can be regenerated. cert, key, err := controller.GenerateControllerCertAndKey(caCert, args.CAPrivateKey, nil) if err != nil { return errors.Annotate(err, "cannot generate controller certificate") } icfg.Bootstrap.StateServingInfo = params.StateServingInfo{ StatePort: controllerCfg.StatePort(), APIPort: controllerCfg.APIPort(), Cert: string(cert), PrivateKey: string(key), CAPrivateKey: args.CAPrivateKey, } if _, ok := cfg.AgentVersion(); !ok { return errors.New("controller model configuration has no agent-version") } icfg.Bootstrap.ControllerModelConfig = cfg icfg.Bootstrap.CustomImageMetadata = customImageMetadata icfg.Bootstrap.ControllerCloudName = args.CloudName icfg.Bootstrap.ControllerCloud = args.Cloud icfg.Bootstrap.ControllerCloudRegion = args.CloudRegion icfg.Bootstrap.ControllerCloudCredential = args.CloudCredential icfg.Bootstrap.ControllerCloudCredentialName = args.CloudCredentialName icfg.Bootstrap.ControllerConfig = args.ControllerConfig icfg.Bootstrap.ControllerInheritedConfig = args.ControllerInheritedConfig icfg.Bootstrap.RegionInheritedConfig = args.Cloud.RegionConfig icfg.Bootstrap.HostedModelConfig = args.HostedModelConfig icfg.Bootstrap.Timeout = args.DialOpts.Timeout icfg.Bootstrap.GUI = guiArchive(args.GUIDataSourceBaseURL, func(msg string) { ctx.Infof(msg) }) return nil }