// decorateAndWriteInfo decorates the info struct with information // from the given cfg, and the writes that out to the filesystem. func decorateAndWriteInfo(info configstore.EnvironInfo, cfg *config.Config) error { // Sanity check our config. var endpoint configstore.APIEndpoint if cert, ok := cfg.CACert(); !ok { return errors.Errorf("CACert is not set") } else if uuid, ok := cfg.UUID(); !ok { return errors.Errorf("UUID is not set") } else if adminSecret := cfg.AdminSecret(); adminSecret == "" { return errors.Errorf("admin-secret is not set") } else { endpoint = configstore.APIEndpoint{ CACert: cert, EnvironUUID: uuid, } } creds := configstore.APICredentials{ User: "******", // TODO(waigani) admin@local once we have that set Password: cfg.AdminSecret(), } info.SetAPICredentials(creds) info.SetAPIEndpoint(endpoint) info.SetBootstrapConfig(cfg.AllAttrs()) if err := info.Write(); err != nil { return errors.Annotatef(err, "cannot create environment info %q", cfg.Name()) } return nil }
// decorateAndWriteInfo decorates the info struct with information // from the given cfg, and the writes that out to the filesystem. func decorateAndWriteInfo(info configstore.EnvironInfo, cfg *config.Config) error { // Sanity check our config. var endpoint configstore.APIEndpoint if cert, ok := cfg.CACert(); !ok { return errors.Errorf("CACert is not set") } else if uuid, ok := cfg.UUID(); !ok { return errors.Errorf("UUID is not set") } else if adminSecret := cfg.AdminSecret(); adminSecret == "" { return errors.Errorf("admin-secret is not set") } else { endpoint = configstore.APIEndpoint{ CACert: cert, ModelUUID: uuid, } } creds := configstore.APICredentials{ User: configstore.DefaultAdminUsername, Password: cfg.AdminSecret(), } endpoint.ServerUUID = endpoint.ModelUUID info.SetAPICredentials(creds) info.SetAPIEndpoint(endpoint) info.SetBootstrapConfig(cfg.AllAttrs()) if err := info.Write(); err != nil { return errors.Annotatef(err, "cannot create model info %q", cfg.Name()) } return nil }
// 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 }
// Prepare prepares a new environment based on the provided configuration. // If the environment is already prepared, it behaves like New. func Prepare(cfg *config.Config, ctx BootstrapContext, store configstore.Storage) (Environ, error) { p, err := Provider(cfg.Type()) if err != nil { return nil, err } info, err := store.ReadInfo(cfg.Name()) if errors.IsNotFound(errors.Cause(err)) { info = store.CreateInfo(cfg.Name()) env, err := prepare(ctx, cfg, info, p) if err != nil { if err := info.Destroy(); err != nil { logger.Warningf("cannot destroy newly created environment info: %v", err) } return nil, err } cfg = env.Config() creds := configstore.APICredentials{ User: "******", // TODO(waigani) admin@local once we have that set Password: cfg.AdminSecret(), } info.SetAPICredentials(creds) endpoint := configstore.APIEndpoint{} var ok bool endpoint.CACert, ok = cfg.CACert() if !ok { return nil, errors.Errorf("CACert is not set") } endpoint.EnvironUUID, ok = cfg.UUID() if !ok { return nil, errors.Errorf("CACert is not set") } info.SetAPIEndpoint(endpoint) info.SetBootstrapConfig(env.Config().AllAttrs()) if err := info.Write(); err != nil { return nil, errors.Annotatef(err, "cannot create environment info %q", env.Config().Name()) } return env, nil } if err != nil { return nil, errors.Annotatef(err, "error reading environment info %q", cfg.Name()) } if !info.Initialized() { return nil, errors.Errorf("found uninitialized environment info for %q; environment preparation probably in progress or interrupted", cfg.Name()) } if len(info.BootstrapConfig()) == 0 { return nil, errors.New("found environment info but no bootstrap config") } cfg, err = config.New(config.NoDefaults, info.BootstrapConfig()) if err != nil { return nil, errors.Annotate(err, "cannot parse bootstrap config") } return New(cfg) }
// ensureCertificate generates a new CA certificate and // attaches it to the given environment configuration, // unless the configuration already has one. func ensureCertificate(cfg *config.Config) (*config.Config, error) { _, hasCACert := cfg.CACert() _, hasCAKey := cfg.CAPrivateKey() if hasCACert && hasCAKey { return cfg, nil } if hasCACert && !hasCAKey { return nil, fmt.Errorf("environment configuration with a certificate but no CA private key") } caCert, caKey, err := cert.NewCA(cfg.Name(), time.Now().UTC().AddDate(10, 0, 0)) if err != nil { return nil, err } return cfg.Apply(map[string]interface{}{ "ca-cert": string(caCert), "ca-private-key": string(caKey), }) }
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 }
// ensureCertificate generates a new CA certificate and // attaches it to the given controller configuration, // unless the configuration already has one. func ensureCertificate(cfg *config.Config) (*config.Config, string, error) { caCert, hasCACert := cfg.CACert() _, hasCAKey := cfg.CAPrivateKey() if hasCACert && hasCAKey { return cfg, caCert, nil } if hasCACert && !hasCAKey { return nil, "", errors.Errorf("controller configuration with a certificate but no CA private key") } caCert, caKey, err := cert.NewCA(cfg.Name(), cfg.UUID(), time.Now().UTC().AddDate(10, 0, 0)) if err != nil { return nil, "", errors.Trace(err) } cfg, err = cfg.Apply(map[string]interface{}{ config.CACertKey: string(caCert), "ca-private-key": string(caKey), }) if err != nil { return nil, "", errors.Trace(err) } return cfg, string(caCert), nil }
// 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 }
// SetConfig is specified in the Environ interface. func (env *localEnviron) SetConfig(cfg *config.Config) error { ecfg, err := providerInstance.newConfig(cfg) if err != nil { logger.Errorf("failed to create new environ config: %v", err) return errors.Trace(err) } env.localMutex.Lock() defer env.localMutex.Unlock() env.config = ecfg env.name = ecfg.Name() containerType := ecfg.container() managerConfig := container.ManagerConfig{ container.ConfigName: env.config.namespace(), container.ConfigLogDir: env.config.logDir(), } var imageURLGetter container.ImageURLGetter if containerType == instance.LXC { if useLxcClone, ok := cfg.LXCUseClone(); ok { managerConfig["use-clone"] = fmt.Sprint(useLxcClone) } if useLxcCloneAufs, ok := cfg.LXCUseCloneAUFS(); ok { managerConfig["use-aufs"] = fmt.Sprint(useLxcCloneAufs) } // For lxc containers, we cache image tarballs in the environment storage, so here // we construct a URL getter. if uuid, ok := ecfg.UUID(); ok { var caCert []byte = nil if cert, ok := cfg.CACert(); ok { caCert = []byte(cert) } baseUrl := ecfg.CloudImageBaseURL() imageURLGetter = container.NewImageURLGetter( // Explicitly call the non-named constructor so if anyone // adds additional fields, this fails. container.ImageURLGetterConfig{ ecfg.stateServerAddr(), uuid, caCert, baseUrl, container.ImageDownloadURL, }) } } env.containerManager, err = factory.NewContainerManager( containerType, managerConfig, imageURLGetter) if err != nil { return errors.Trace(err) } // When the localEnviron value is created on the client // side, the bootstrap-ip attribute will not exist, // because it is only set *within* the running // environment, not in the configuration created by // Prepare. if addr := ecfg.bootstrapIPAddress(); addr != "" { env.bridgeAddress = addr return nil } // If we get to here, it is because we haven't yet bootstrapped an // environment, and saved the config in it, or we are running a command // from the command line, so it is ok to work on the assumption that we // have direct access to the directories. if err := env.config.createDirs(); err != nil { return errors.Trace(err) } // Record the network bridge address and create a filestorage. if err := env.resolveBridgeAddress(cfg); err != nil { return errors.Trace(err) } return nil }