// prepare is the internal version of Prepare - it prepares the // environment but does not open it. func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) { ecfg, err := p.newConfig(cfg) if err != nil { return nil, err } p.mu.Lock() defer p.mu.Unlock() name := cfg.Name() if ecfg.stateId() != noStateId { return cfg, nil } // The environment has not been prepared, // so create it and set its state identifier accordingly. if ecfg.stateServer() && len(p.state) != 0 { for _, old := range p.state { panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name)) } } state := newState(name, p.ops, p.statePolicy) p.maxStateId++ state.id = p.maxStateId p.state[state.id] = state // Add the state id to the configuration we use to // in the returned environment. return cfg.Apply(map[string]interface{}{ "state-id": fmt.Sprint(state.id), }) }
func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { envConfig, err := p.validate(cfg, old) if err != nil { return nil, err } return cfg.Apply(envConfig.attrs) }
func (maasEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Debugf("opening environment %q.", cfg.Name()) env, err := NewEnviron(cfg) if err != nil { return nil, err } return env, nil }
// ensureAdminSecret returns a config with a non-empty admin-secret. func ensureAdminSecret(cfg *config.Config) (*config.Config, error) { if cfg.AdminSecret() != "" { return cfg, nil } return cfg.Apply(map[string]interface{}{ "admin-secret": randomKey(), }) }
// 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 }
// SeriesToUpload returns the supplied series with duplicates removed if // non-empty; otherwise it returns a default list of series we should // probably upload, based on cfg. func SeriesToUpload(cfg *config.Config, series []string) []string { unique := set.NewStrings(series...) if unique.IsEmpty() { unique.Add(version.Current.Series) for _, toolsSeries := range ToolsLtsSeries { unique.Add(toolsSeries) } if series, ok := cfg.DefaultSeries(); ok { unique.Add(series) } } return unique.Values() }
// 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{ Addrs: composeAddresses(hostnames, config.StatePort()), CACert: cert, }, &api.Info{ Addrs: composeAddresses(hostnames, config.APIPort()), CACert: cert, } }
// validate calls the state's assigned policy, if non-nil, to obtain // a ConfigValidator, and calls Validate if a non-nil ConfigValidator is // returned. func (st *State) validate(cfg, old *config.Config) (valid *config.Config, err error) { if st.policy == nil { return cfg, nil } configValidator, err := st.policy.ConfigValidator(cfg.Type()) if errors.IsNotImplemented(err) { return cfg, nil } else if err != nil { return nil, err } if configValidator == nil { return nil, fmt.Errorf("policy returned nil configValidator without an error") } return configValidator.Validate(cfg, old) }
// Initialize sets up an initial empty state and returns it. // This needs to be performed only once for a given environment. // It returns unauthorizedError if access is unauthorized. func Initialize(info *Info, cfg *config.Config, opts DialOpts, policy Policy) (rst *State, err error) { st, err := Open(info, opts, policy) if err != nil { return nil, err } defer func() { if err != nil { st.Close() } }() // A valid environment is used as a signal that the // state has already been initalized. If this is the case // do nothing. if _, err := st.Environment(); err == nil { return st, nil } else if !errors.IsNotFound(err) { return nil, err } logger.Infof("initializing environment") if err := checkEnvironConfig(cfg); err != nil { return nil, err } uuid, err := utils.NewUUID() if err != nil { return nil, fmt.Errorf("environment UUID cannot be created: %v", err) } ops := []txn.Op{ createConstraintsOp(st, environGlobalKey, constraints.Value{}), createSettingsOp(st, environGlobalKey, cfg.AllAttrs()), createEnvironmentOp(st, cfg.Name(), uuid.String()), { C: st.stateServers.Name, Id: environGlobalKey, Insert: &stateServersDoc{}, }, { C: st.stateServers.Name, Id: apiHostPortsKey, Insert: &apiHostPortsDoc{}, }, } if err := st.runTransaction(ops); err == txn.ErrAborted { // The config was created in the meantime. return st, nil } else if err != nil { return nil, err } return st, nil }
// New returns a new environment based on the provided configuration. func New(config *config.Config) (Environ, error) { p, err := Provider(config.Type()) if err != nil { return nil, err } return p.Open(config) }
func (st *State) buildAndValidateEnvironConfig(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) (validCfg *config.Config, err error) { newConfig, err := oldConfig.Apply(updateAttrs) if err != nil { return nil, err } if len(removeAttrs) != 0 { newConfig, err = newConfig.Remove(removeAttrs) if err != nil { return nil, err } } if err := checkEnvironConfig(newConfig); err != nil { return nil, err } return st.validate(newConfig, oldConfig) }
func (p maasEnvironProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { attrs := cfg.UnknownAttrs() oldName, found := attrs["maas-agent-name"] if found && oldName != "" { return nil, errAgentNameAlreadySet } uuid, err := utils.NewUUID() if err != nil { return nil, err } attrs["maas-agent-name"] = uuid.String() cfg, err = cfg.Apply(attrs) if err != nil { return nil, err } return p.Open(cfg) }
func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } if idStr, ok := validated["state-id"].(string); ok { if _, err := strconv.Atoi(idStr); err != nil { return nil, fmt.Errorf("invalid state-id %q", idStr) } } // Apply the coerced unknown values back into the config. return cfg.Apply(validated) }
// newEnviron create a new Joyent environ instance from config. func newEnviron(cfg *config.Config) (*joyentEnviron, error) { env := new(joyentEnviron) if err := env.SetConfig(cfg); err != nil { return nil, err } env.name = cfg.Name() var err error env.storage, err = newStorage(env.ecfg, "") if err != nil { return nil, err } env.compute, err = newCompute(env.ecfg) if err != nil { return nil, err } return env, nil }
func (p manualProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { if _, ok := cfg.UnknownAttrs()["storage-auth-key"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, err } cfg, err = cfg.Apply(map[string]interface{}{ "storage-auth-key": uuid.String(), }) if err != nil { return nil, err } } if use, ok := cfg.UnknownAttrs()["use-sshstorage"].(bool); ok && !use { return nil, fmt.Errorf("use-sshstorage must not be specified") } envConfig, err := p.validate(cfg, nil) if err != nil { return nil, err } if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil { return nil, err } return p.open(envConfig) }
// 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.CreateInfo(cfg.Name()) if err == configstore.ErrEnvironInfoAlreadyExists { logger.Infof("environment info already exists; using New not Prepare") info, err := store.ReadInfo(cfg.Name()) if err != nil { return nil, fmt.Errorf("error reading environment info %q: %v", cfg.Name(), err) } if !info.Initialized() { return nil, fmt.Errorf("found uninitialized environment info for %q; environment preparation probably in progress or interrupted", cfg.Name()) } if len(info.BootstrapConfig()) == 0 { return nil, fmt.Errorf("found environment info but no bootstrap config") } cfg, err = config.New(config.NoDefaults, info.BootstrapConfig()) if err != nil { return nil, fmt.Errorf("cannot parse bootstrap config: %v", err) } return New(cfg) } if err != nil { return nil, fmt.Errorf("cannot create new info for environment %q: %v", cfg.Name(), err) } 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 } info.SetBootstrapConfig(env.Config().AllAttrs()) if err := info.Write(); err != nil { return nil, fmt.Errorf("cannot create environment info %q: %v", env.Config().Name(), err) } return env, nil }
func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envConfig := newEnvironConfig(cfg, validated) if envConfig.bootstrapHost() == "" { return nil, errNoBootstrapHost } // Check various immutable attributes. if old != nil { oldEnvConfig, err := p.validate(old, nil) if err != nil { return nil, err } for _, key := range [...]string{ "bootstrap-user", "bootstrap-host", "storage-listen-ip", } { if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil { return nil, err } } oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort() if oldPort != newPort { return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort) } oldUseSSHStorage, newUseSSHStorage := oldEnvConfig.useSSHStorage(), envConfig.useSSHStorage() if oldUseSSHStorage != newUseSSHStorage && newUseSSHStorage == true { return nil, fmt.Errorf("cannot change use-sshstorage from %v to %v", oldUseSSHStorage, newUseSSHStorage) } } return envConfig, nil }
// newRsyslogConfig creates a new instance of the RsyslogConfig. func newRsyslogConfig(envCfg *config.Config, api *RsyslogAPI) (*apirsyslog.RsyslogConfig, error) { stateAddrsResult, err := api.StateAddresser.StateAddresses() if err != nil { return nil, err } port := envCfg.SyslogPort() var bareAddrs []string for _, addr := range stateAddrsResult.Result { hostOnly, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } bareAddrs = append(bareAddrs, hostOnly) } apiAddresses := instance.NewAddresses(bareAddrs...) return &apirsyslog.RsyslogConfig{ CACert: envCfg.RsyslogCACert(), Port: port, HostPorts: instance.AddressesWithPort(apiAddresses, port), }, nil }
// 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), }) }