// 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 } 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)) } } // The environment has not been prepared, // so create it and set its state identifier accordingly. state := newState(name, p.ops, p.statePolicy) p.maxStateId++ state.id = p.maxStateId p.state[state.id] = state attrs := map[string]interface{}{"state-id": fmt.Sprint(state.id)} if ecfg.stateServer() { attrs["api-port"] = state.listenAPI() } return cfg.Apply(attrs) }
func (p manualProvider) PrepareForBootstrap(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 } cfg, err = cfg.Apply(envConfig.attrs) if err != nil { return nil, err } envConfig = newEnvironConfig(cfg, envConfig.attrs) if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil { return nil, err } return p.open(envConfig) }
// newValidConfig builds a new environConfig from the provided Config // and returns it. The resulting config values are validated. func newValidConfig(cfg *config.Config, defaults map[string]interface{}) (*environConfig, error) { // Ensure that the provided config is valid. if err := config.Validate(cfg, nil); err != nil { return nil, errors.Trace(err) } // Apply the defaults and coerce/validate the custom config attrs. validated, err := cfg.ValidateUnknownAttrs(configFields, defaults) if err != nil { return nil, errors.Trace(err) } validCfg, err := cfg.Apply(validated) if err != nil { return nil, errors.Trace(err) } // Build the config. ecfg := newConfig(validCfg) // Do final validation. if err := ecfg.validate(); err != nil { return nil, errors.Trace(err) } return ecfg, nil }
func (joyentProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { newEcfg, err := validateConfig(cfg, old) if err != nil { return nil, fmt.Errorf("invalid Joyent provider config: %v", err) } return cfg.Apply(newEcfg.attrs) }
// PrepareForBootstrap is specified in the EnvironProvider interface. func (prov *azureEnvironProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { // Ensure that internal configuration is not specified, and then set // what we can now. We only need to do this during bootstrap. Validate // will check for changes later. unknownAttrs := cfg.UnknownAttrs() for _, key := range internalConfigAttributes { if _, ok := unknownAttrs[key]; ok { return nil, errors.Errorf(`internal config %q must not be specified`, key) } } // Record the UUID that will be used for the controller environment. cfg, err := cfg.Apply(map[string]interface{}{ configAttrControllerResourceGroup: resourceGroupName(cfg), }) if err != nil { return nil, errors.Annotate(err, "recording controller-resource-group") } env, err := prov.Open(cfg) if err != nil { return nil, errors.Trace(err) } if ctx.ShouldVerifyCredentials() { if err := verifyCredentials(env.(*azureEnviron)); err != nil { return nil, errors.Trace(err) } } return env, nil }
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 (t configTest) check(c *gc.C) { credential := cloud.NewCredential( cloud.AccessKeyAuthType, map[string]string{ "access-key": "x", "secret-key": "y", }, ) cloudSpec := environs.CloudSpec{ Type: "ec2", Name: "ec2test", Region: "us-east-1", Credential: &credential, } attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "ec2", }).Merge(t.config) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, jc.ErrorIsNil) e, err := environs.New(environs.OpenParams{ Cloud: cloudSpec, Config: cfg, }) if t.change != nil { c.Assert(err, jc.ErrorIsNil) // Testing a change in configuration. var old, changed, valid *config.Config ec2env := e.(*environ) old = ec2env.ecfg().Config changed, err = old.Apply(t.change) c.Assert(err, jc.ErrorIsNil) // Keep err for validation below. valid, err = providerInstance.Validate(changed, old) if err == nil { err = ec2env.SetConfig(valid) } } if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) return } c.Assert(err, jc.ErrorIsNil) ecfg := e.(*environ).ecfg() c.Assert(ecfg.Name(), gc.Equals, "testenv") c.Assert(ecfg.vpcID(), gc.Equals, t.vpcID) c.Assert(ecfg.forceVPCID(), gc.Equals, t.forceVPCID) if t.firewallMode != "" { c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) } for name, expect := range t.expect { actual, found := ecfg.UnknownAttrs()[name] c.Check(found, jc.IsTrue) c.Check(actual, gc.Equals, expect) } }
// 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(), }) }
func (t configTest) check(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "ec2", }).Merge(t.config) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, jc.ErrorIsNil) e, err := environs.New(cfg) if t.change != nil { c.Assert(err, jc.ErrorIsNil) // Testing a change in configuration. var old, changed, valid *config.Config ec2env := e.(*environ) old = ec2env.ecfg().Config changed, err = old.Apply(t.change) c.Assert(err, jc.ErrorIsNil) // Keep err for validation below. valid, err = providerInstance.Validate(changed, old) if err == nil { err = ec2env.SetConfig(valid) } } if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) return } c.Assert(err, jc.ErrorIsNil) ecfg := e.(*environ).ecfg() c.Assert(ecfg.Name(), gc.Equals, "testenv") if t.region != "" { c.Assert(ecfg.region(), gc.Equals, t.region) } if t.accessKey != "" { c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey) c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey) expected := map[string]string{ "access-key": t.accessKey, "secret-key": t.secretKey, } c.Assert(err, jc.ErrorIsNil) actual, err := e.Provider().SecretAttrs(ecfg.Config) c.Assert(err, jc.ErrorIsNil) c.Assert(expected, gc.DeepEquals, actual) } else { c.Assert(ecfg.accessKey(), gc.DeepEquals, testAuth.AccessKey) c.Assert(ecfg.secretKey(), gc.DeepEquals, testAuth.SecretKey) } if t.firewallMode != "" { c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) } for name, expect := range t.expect { actual, found := ecfg.UnknownAttrs()[name] c.Check(found, jc.IsTrue) c.Check(actual, gc.Equals, expect) } }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (p maasEnvironProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { attrs := cfg.UnknownAttrs() oldName, found := attrs["maas-agent-name"] if found && oldName != "" { return nil, errAgentNameAlreadySet } attrs["maas-agent-name"] = cfg.UUID() return cfg.Apply(attrs) }
func rebootstrap(cfg *config.Config, ctx *cmd.Context, cons constraints.Value) (environs.Environ, error) { progress("re-bootstrapping environment") // Turn on safe mode so that the newly bootstrapped instance // will not destroy all the instances it does not know about. cfg, err := cfg.Apply(map[string]interface{}{ "provisioner-safe-mode": true, }) if err != nil { return nil, errors.Annotate(err, "cannot enable provisioner-safe-mode") } env, err := environs.New(cfg) if err != nil { return nil, err } instanceIds, err := env.StateServerInstances() switch errors.Cause(err) { case nil, environs.ErrNoInstances: // Some providers will return a nil error even // if there are no live state server instances. break case environs.ErrNotBootstrapped: return nil, errors.Trace(err) default: return nil, errors.Annotate(err, "cannot determine state server instances") } if len(instanceIds) > 0 { instances, err := env.Instances(instanceIds) switch errors.Cause(err) { case nil, environs.ErrPartialInstances: return nil, fmt.Errorf("old bootstrap instances %q still seems to exist; will not replace", instances) case environs.ErrNoInstances: // No state server instances, so keep running. break default: return nil, errors.Annotate(err, "cannot detect whether old instance is still running") } } // Remove the storage so that we can bootstrap without the provider complaining. if env, ok := env.(environs.EnvironStorage); ok { if err := env.Storage().Remove(common.StateFile); err != nil { return nil, errors.Annotate(err, fmt.Sprintf("cannot remove %q from storage", common.StateFile)) } } // TODO If we fail beyond here, then we won't have a state file and // we won't be able to re-run this script because it fails without it. // We could either try to recreate the file if we fail (which is itself // error-prone) or we could provide a --no-check flag to make // it go ahead anyway without the check. args := bootstrap.BootstrapParams{Constraints: cons} if err := bootstrap.Bootstrap(envcmd.BootstrapContextNoVerify(ctx), env, args); err != nil { return nil, errors.Annotate(err, "cannot bootstrap new instance") } return env, nil }
func validateConfig(cfg *config.Config, old *environConfig) (*environConfig, error) { // Check sanity of juju-level fields. var oldCfg *config.Config if old != nil { oldCfg = old.Config } if err := config.Validate(cfg, oldCfg); err != nil { return nil, errors.Trace(err) } // Extract validated provider-specific fields. All of configFields will be // present in validated, and defaults will be inserted if necessary. If the // schema you passed in doesn't quite express what you need, you can make // whatever checks you need here, before continuing. // In particular, if you want to extract (say) credentials from the user's // shell environment variables, you'll need to allow missing values to pass // through the schema by setting a value of schema.Omit in the configFields // map, and then to set and check them at this point. These values *must* be // stored in newAttrs: a Config will be generated on the user's machine only // to begin with, and will subsequently be used on a different machine that // will probably not have those variables set. newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaultFields) if err != nil { return nil, errors.Trace(err) } for field := range configFields { if newAttrs[field] == "" { return nil, errors.Errorf("%s: must not be empty", field) } } // If an old config was supplied, check any immutable fields have not changed. if old != nil { for _, field := range configImmutableFields { if old.attrs[field] != newAttrs[field] { return nil, errors.Errorf( "%s: cannot change from %v to %v", field, old.attrs[field], newAttrs[field], ) } } } // Merge the validated provider-specific fields into the original config, // to ensure the object we return is internally consistent. newCfg, err := cfg.Apply(newAttrs) if err != nil { return nil, errors.Trace(err) } ecfg := &environConfig{ Config: newCfg, attrs: newAttrs, } return ecfg, nil }
func (ts configTestSpec) fixCfg(c *gc.C, cfg *config.Config) *config.Config { fixes := make(map[string]interface{}) // Set changed values. fixes = updateAttrs(fixes, ts.insert) newCfg, err := cfg.Apply(fixes) c.Assert(err, jc.ErrorIsNil) return newCfg }
// Validate implements environs.EnvironProvider.Validate. func (provider 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, fmt.Errorf("failed to validate unknown attrs: %v", err) } localConfig := newEnvironConfig(cfg, validated) // Before potentially creating directories, make sure that the // root directory has not changed. containerType := localConfig.container() if old != nil { oldLocalConfig, err := provider.newConfig(old) if err != nil { return nil, fmt.Errorf("old config is not a valid local config: %v", old) } if containerType != oldLocalConfig.container() { return nil, fmt.Errorf("cannot change container from %q to %q", oldLocalConfig.container(), containerType) } if localConfig.rootDir() != oldLocalConfig.rootDir() { return nil, fmt.Errorf("cannot change root-dir from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.networkBridge() != oldLocalConfig.networkBridge() { return nil, fmt.Errorf("cannot change network-bridge from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.storagePort() != oldLocalConfig.storagePort() { return nil, fmt.Errorf("cannot change storage-port from %v to %v", oldLocalConfig.storagePort(), localConfig.storagePort()) } } // Currently only supported containers are "lxc" and "kvm". if containerType != instance.LXC && containerType != instance.KVM { return nil, fmt.Errorf("unsupported container type: %q", containerType) } dir, err := utils.NormalizePath(localConfig.rootDir()) if err != nil { return nil, err } if dir == "." { dir = osenv.JujuHomePath(cfg.Name()) } // Always assign the normalized path. localConfig.attrs["root-dir"] = dir // Apply the coerced unknown values back into the config. return cfg.Apply(localConfig.attrs) }
func configWithDefaults(cfg *config.Config) (*config.Config, error) { defaults := make(map[string]interface{}) if _, ok := cfg.StorageDefaultBlockSource(); !ok { // Set the default block source. defaults[config.StorageDefaultBlockSourceKey] = storageProviderType } if len(defaults) == 0 { return cfg, nil } return cfg.Apply(defaults) }
func (prov maasEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) { // Validate base configuration change before validating MAAS specifics. err := config.Validate(cfg, oldCfg) if err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } // Add MAAS specific defaults. providerDefaults := make(map[string]interface{}) // Storage. if _, ok := cfg.StorageDefaultBlockSource(); !ok { providerDefaults[config.StorageDefaultBlockSourceKey] = maasStorageProviderType } if len(providerDefaults) > 0 { if cfg, err = cfg.Apply(providerDefaults); err != nil { return nil, err } } if oldCfg != nil { oldAttrs := oldCfg.UnknownAttrs() validMaasAgentName := false if oldName, ok := oldAttrs["maas-agent-name"]; !ok || oldName == nil { // If maas-agent-name was nil (because the config was // generated pre-1.16.2 the only correct value for it is "" // See bug #1256179 validMaasAgentName = (validated["maas-agent-name"] == "") } else { validMaasAgentName = (validated["maas-agent-name"] == oldName) } if !validMaasAgentName { return nil, fmt.Errorf("cannot change maas-agent-name") } } envCfg := new(maasEnvironConfig) envCfg.Config = cfg envCfg.attrs = validated server := envCfg.maasServer() serverURL, err := url.Parse(server) if err != nil || serverURL.Scheme == "" || serverURL.Host == "" { return nil, fmt.Errorf("malformed maas-server URL '%v': %s", server, err) } oauth := envCfg.maasOAuth() if strings.Count(oauth, ":") != 2 { return nil, errMalformedMaasOAuth } return cfg.Apply(envCfg.attrs) }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (p environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { attrs := cfg.UnknownAttrs() if _, ok := attrs["control-bucket"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, errors.Trace(err) } attrs["control-bucket"] = fmt.Sprintf("%x", uuid.Raw()) } return cfg.Apply(attrs) }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (p azureEnvironProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { // Set availability-sets-enabled to true // by default, unless the user set a value. if _, ok := cfg.AllAttrs()["availability-sets-enabled"]; !ok { var err error cfg, err = cfg.Apply(map[string]interface{}{"availability-sets-enabled": true}) if err != nil { return nil, errors.Trace(err) } } return cfg, nil }
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 } // Apply the coerced unknown values back into the config. return cfg.Apply(validated) }
// Prepare is specified in the EnvironProvider interface. func (prov azureEnvironProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { // Set availability-sets-enabled to true // by default, unless the user set a value. if _, ok := cfg.AllAttrs()["availability-sets-enabled"]; !ok { var err error cfg, err = cfg.Apply(map[string]interface{}{"availability-sets-enabled": true}) if err != nil { return nil, err } } return prov.Open(cfg) }
func validateConfig(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 } // Add EC2 specific defaults. providerDefaults := make(map[string]interface{}) // Storage. if _, ok := cfg.StorageDefaultBlockSource(); !ok { providerDefaults[config.StorageDefaultBlockSourceKey] = EBS_ProviderType } if len(providerDefaults) > 0 { if cfg, err = cfg.Apply(providerDefaults); err != nil { return nil, err } } ecfg := &environConfig{cfg, validated} if ecfg.accessKey() == "" || ecfg.secretKey() == "" { auth, err := aws.EnvAuth() if err != nil || ecfg.accessKey() != "" || ecfg.secretKey() != "" { return nil, fmt.Errorf("environment has no access-key or secret-key") } ecfg.attrs["access-key"] = auth.AccessKey ecfg.attrs["secret-key"] = auth.SecretKey } if _, ok := aws.Regions[ecfg.region()]; !ok { return nil, fmt.Errorf("invalid region name %q", ecfg.region()) } if old != nil { attrs := old.UnknownAttrs() if region, _ := attrs["region"].(string); ecfg.region() != region { return nil, fmt.Errorf("cannot change region from %q to %q", region, ecfg.region()) } if bucket, _ := attrs["control-bucket"].(string); ecfg.controlBucket() != bucket { return nil, fmt.Errorf("cannot change control-bucket from %q to %q", bucket, ecfg.controlBucket()) } } // ssl-hostname-verification cannot be disabled if !ecfg.SSLHostnameVerification() { return nil, fmt.Errorf("disabling ssh-hostname-verification is not supported") } return ecfg, nil }
// ensureUUID generates a new uuid and attaches it to // the given environment configuration, unless the // configuration already has one. func ensureUUID(cfg *config.Config) (*config.Config, error) { _, hasUUID := cfg.UUID() if hasUUID { return cfg, nil } uuid, err := utils.NewUUID() if err != nil { return nil, errors.Trace(err) } return cfg.Apply(map[string]interface{}{ "uuid": uuid.String(), }) }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (joyentProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { // Turn an incomplete config into a valid one, if possible. attrs := cfg.UnknownAttrs() if _, ok := attrs["control-dir"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, errors.Trace(err) } attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw()) } return cfg.Apply(attrs) }
func prepareConfig(cfg *config.Config) (*config.Config, error) { // Turn an incomplete config into a valid one, if possible. attrs := cfg.UnknownAttrs() if _, ok := attrs["control-dir"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, err } attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw()) } return cfg.Apply(attrs) }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { // Make any necessary updates to the config. This needs to happen // before any defaults are applied. updates, err := parseOSEnv() if err != nil { return nil, errors.Trace(err) } cfg, err = cfg.Apply(updates) if err != nil { return nil, errors.Trace(err) } return cfg, nil }
func prepareConfig(cfg *config.Config) (*config.Config, error) { // Turn an incomplete config into a valid one, if possible. attrs := cfg.AllAttrs() if _, ok := attrs["uuid"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, errors.Trace(err) } attrs["uuid"] = uuid.String() } return cfg.Apply(attrs) }
// PrepareForCreateEnvironment is specified in the EnvironProvider interface. func (p *environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { // NOTE: this check might appear redundant, but it's not: some tests // (apiserver/modelmanager) inject a string value and determine that // the config is validated later; validating here would render that // test meaningless. if cfg.AllAttrs()["controller"] == true { // NOTE: cfg.Apply *does* validate, but we're only adding a // valid value so it doesn't matter. return cfg.Apply(map[string]interface{}{ "controller": false, }) } return cfg, nil }
// Validate ensures that config is a valid configuration for this // provider like specified in the EnvironProvider interface. func (prov azureEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) { // Validate base configuration change before validating Azure specifics. err := config.Validate(cfg, oldCfg) if err != nil { return nil, err } // User cannot change availability-sets-enabled after environment is prepared. if oldCfg != nil { if oldCfg.AllAttrs()["availability-sets-enabled"] != cfg.AllAttrs()["availability-sets-enabled"] { return nil, fmt.Errorf("cannot change availability-sets-enabled") } } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envCfg := new(azureEnvironConfig) envCfg.Config = cfg envCfg.attrs = validated if _, ok := cfg.StorageDefaultBlockSource(); !ok { // Default volume source not specified; set // it to the azure storage provider. envCfg.attrs[config.StorageDefaultBlockSourceKey] = storageProviderType } cert := envCfg.managementCertificate() if cert == "" { certPath := envCfg.attrs["management-certificate-path"].(string) pemData, err := readPEMFile(certPath) if err != nil { return nil, fmt.Errorf("invalid management-certificate-path: %s", err) } envCfg.attrs["management-certificate"] = string(pemData) } else { if block, _ := pem.Decode([]byte(cert)); block == nil { return nil, fmt.Errorf("invalid management-certificate: not a PEM encoded certificate") } } delete(envCfg.attrs, "management-certificate-path") if envCfg.location() == "" { return nil, fmt.Errorf("environment has no location; you need to set one. E.g. 'West US'") } return cfg.Apply(envCfg.attrs) }
func rebootstrap(cfg *config.Config, ctx *cmd.Context, cons constraints.Value) (environs.Environ, error) { progress("re-bootstrapping environment") // Turn on safe mode so that the newly bootstrapped instance // will not destroy all the instances it does not know about. cfg, err := cfg.Apply(map[string]interface{}{ "provisioner-safe-mode": true, }) if err != nil { return nil, fmt.Errorf("cannot enable provisioner-safe-mode: %v", err) } env, err := environs.New(cfg) if err != nil { return nil, err } st, err := bootstrap.LoadState(env.Storage()) if err != nil { return nil, fmt.Errorf("cannot retrieve environment storage; perhaps the environment was not bootstrapped: %v", err) } if len(st.StateInstances) == 0 { return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped") } if len(st.StateInstances) > 1 { return nil, fmt.Errorf("restore does not support HA juju configurations yet") } inst, err := env.Instances(st.StateInstances) if err == nil { return nil, fmt.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst) } if err != environs.ErrNoInstances { return nil, fmt.Errorf("cannot detect whether old instance is still running: %v", err) } // Remove the storage so that we can bootstrap without the provider complaining. if err := env.Storage().Remove(bootstrap.StateFile); err != nil { return nil, fmt.Errorf("cannot remove %q from storage: %v", bootstrap.StateFile, err) } // TODO If we fail beyond here, then we won't have a state file and // we won't be able to re-run this script because it fails without it. // We could either try to recreate the file if we fail (which is itself // error-prone) or we could provide a --no-check flag to make // it go ahead anyway without the check. args := environs.BootstrapParams{Constraints: cons} if err := bootstrap.Bootstrap(ctx, env, args); err != nil { return nil, fmt.Errorf("cannot bootstrap new instance: %v", err) } return env, nil }
func (prov maasEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) { // Validate base configuration change before validating MAAS specifics. err := config.Validate(cfg, oldCfg) if err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envCfg := &maasModelConfig{ Config: cfg, attrs: validated, } return cfg.Apply(envCfg.attrs) }