// FinishMachineConfig sets fields on a MachineConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before bootstrapping. It assumes that the supplied Config comes from // an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config, cons constraints.Value) (err error) { defer errors.Maskf(&err, "cannot complete machine configuration") if err := PopulateMachineConfig( mcfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), cfg.PreferIPv6(), ); err != nil { return err } // The following settings are only appropriate at bootstrap time. At the // moment, the only state server is the bootstrap node, but this // will probably change. if !mcfg.Bootstrap { return nil } if mcfg.APIInfo != nil || mcfg.MongoInfo != nil { return fmt.Errorf("machine configuration already has api/state info") } caCert, hasCACert := cfg.CACert() if !hasCACert { return fmt.Errorf("environment configuration has no ca-cert") } password := cfg.AdminSecret() if password == "" { return fmt.Errorf("environment configuration has no admin-secret") } passwordHash := utils.UserPasswordHash(password, utils.CompatSalt) mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert} mcfg.MongoInfo = &authentication.MongoInfo{Password: passwordHash, Info: mongo.Info{CACert: caCert}} // These really are directly relevant to running a state server. cert, key, err := cfg.GenerateStateServerCertAndKey() if err != nil { return errors.Annotate(err, "cannot generate state server certificate") } srvInfo := params.StateServingInfo{ StatePort: cfg.StatePort(), APIPort: cfg.APIPort(), Cert: string(cert), PrivateKey: string(key), SystemIdentity: mcfg.SystemPrivateSSHKey, } mcfg.StateServingInfo = &srvInfo mcfg.Constraints = cons if mcfg.Config, err = BootstrapConfig(cfg); err != nil { return err } return nil }
func controllerValues(config *config.Config) map[string]interface{} { result := make(map[string]interface{}) result["state-port"] = config.StatePort() result["api-port"] = config.APIPort() result["controller-uuid"] = config.ControllerUUID() // We ignore the second bool param from the CACert check as if there // wasn't a CACert, there is no way we'd be importing a new model // into the controller result["ca-cert"], _ = config.CACert() return result }
// PrepareForBootstrap implements environs.EnvironProvider.PrepareForBootstrap. func (p environProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { cfg, err := p.PrepareForCreateEnvironment(cfg) 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) }
// getStateInfo puts together the state.Info and api.Info for the given // config, with the given state-server host names. // The given config absolutely must have a CACert. func getStateInfo(config *config.Config, hostnames []string) (*state.Info, *api.Info) { cert, hasCert := config.CACert() if !hasCert { panic(errors.New("getStateInfo: config has no CACert")) } return &state.Info{ Info: mongo.Info{ Addrs: composeAddresses(hostnames, config.StatePort()), CACert: cert, }, }, &api.Info{ Addrs: composeAddresses(hostnames, config.APIPort()), CACert: cert, } }
// FinishInstanceConfig sets fields on a InstanceConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before bootstrapping. It assumes that the supplied Config comes from // an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishInstanceConfig(icfg *InstanceConfig, cfg *config.Config) (err error) { defer errors.DeferredAnnotatef(&err, "cannot complete machine configuration") if err := PopulateInstanceConfig( icfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), cfg.AptMirror(), cfg.PreferIPv6(), cfg.EnableOSRefreshUpdate(), cfg.EnableOSUpgrade(), ); err != nil { return errors.Trace(err) } if isStateInstanceConfig(icfg) { // Add NUMACTL preference. Needed to work for both bootstrap and high availability // Only makes sense for controller logger.Debugf("Setting numa ctl preference to %v", cfg.NumaCtlPreference()) // Unfortunately, AgentEnvironment can only take strings as values icfg.AgentEnvironment[agent.NumaCtlPreference] = fmt.Sprintf("%v", cfg.NumaCtlPreference()) } // The following settings are only appropriate at bootstrap time. At the // moment, the only controller is the bootstrap node, but this // will probably change. if !icfg.Bootstrap { return nil } if icfg.APIInfo != nil || icfg.MongoInfo != nil { return errors.New("machine configuration already has api/state info") } caCert, hasCACert := cfg.CACert() if !hasCACert { return errors.New("model configuration has no ca-cert") } password := cfg.AdminSecret() if password == "" { return errors.New("model configuration has no admin-secret") } icfg.APIInfo = &api.Info{ Password: password, CACert: caCert, ModelTag: names.NewModelTag(cfg.UUID()), } icfg.MongoInfo = &mongo.MongoInfo{Password: password, 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 := cfg.GenerateControllerCertAndKey(nil) if err != nil { return errors.Annotate(err, "cannot generate controller certificate") } caPrivateKey, hasCAPrivateKey := cfg.CAPrivateKey() if !hasCAPrivateKey { return errors.New("model configuration has no ca-private-key") } srvInfo := params.StateServingInfo{ StatePort: cfg.StatePort(), APIPort: cfg.APIPort(), Cert: string(cert), PrivateKey: string(key), CAPrivateKey: caPrivateKey, } icfg.StateServingInfo = &srvInfo if icfg.Config, err = bootstrapConfig(cfg); err != nil { return errors.Trace(err) } return 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) }
// Prepare implements environs.EnvironProvider.Prepare. func (p environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { // 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, fmt.Errorf("bootstrap-ip must not be specified") } err := checkLocalPort(cfg.StatePort(), "state port") if err != nil { return nil, err } err = checkLocalPort(cfg.APIPort(), "API port") if err != nil { return nil, err } // If the user has specified no values for any of the three normal // proxies, then look in the environment and set them. 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, } setIfNotBlank := func(key, value string) { if value != "" { attrs[key] = value } } logger.Tracef("Look for proxies?") if cfg.HttpProxy() == "" && cfg.HttpsProxy() == "" && cfg.FtpProxy() == "" && cfg.NoProxy() == "" { proxySettings := proxy.DetectProxies() logger.Tracef("Proxies detected %#v", proxySettings) setIfNotBlank("http-proxy", proxySettings.Http) setIfNotBlank("https-proxy", proxySettings.Https) setIfNotBlank("ftp-proxy", proxySettings.Ftp) setIfNotBlank("no-proxy", proxySettings.NoProxy) } if cfg.AptHttpProxy() == "" && cfg.AptHttpsProxy() == "" && cfg.AptFtpProxy() == "" { proxySettings, err := detectAptProxies() if err != nil { return nil, err } setIfNotBlank("apt-http-proxy", proxySettings.Http) setIfNotBlank("apt-https-proxy", proxySettings.Https) setIfNotBlank("apt-ftp-proxy", proxySettings.Ftp) } if len(attrs) > 0 { cfg, err = cfg.Apply(attrs) if err != nil { return nil, err } } return p.Open(cfg) }