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 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 (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) }
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) }
// 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(), }) }
// 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 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) }
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 } state, 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(state.StateInstances) == 0 { return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped") } if len(state.StateInstances) > 1 { return nil, fmt.Errorf("restore does not support HA juju configurations yet") } inst, err := env.Instances(state.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 (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 *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) }
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) }
// 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 }
// 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 (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 } 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) }
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 } 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") } // Apply the coerced unknown values back into the config. return cfg.Apply(ecfg.attrs) }
// 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 cert := envCfg.managementCertificate() if cert == "" { certPath := envCfg.attrs["management-certificate-path"].(string) pemData, err := ioutil.ReadFile(certPath) if err != nil { return nil, fmt.Errorf("invalid management-certificate-path: %s", err) } envCfg.attrs["management-certificate"] = string(pemData) } 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) }
// 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 if containerType != instance.KVM { fastOptionAvailable := useFastLXC(containerType) if _, found := localConfig.attrs["lxc-clone"]; !found { localConfig.attrs["lxc-clone"] = fastOptionAvailable } } // Apply the coerced unknown values back into the config. return cfg.Apply(localConfig.attrs) }
func (t configTest) check(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "ec2", "control-bucket": "x", }).Merge(t.config) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) e, err := environs.New(cfg) if t.change != nil { c.Assert(err, gc.IsNil) // 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, gc.IsNil) // 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, gc.IsNil) ecfg := e.(*environ).ecfg() c.Assert(ecfg.Name(), gc.Equals, "testenv") c.Assert(ecfg.controlBucket(), gc.Equals, "x") 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, gc.IsNil) actual, err := e.Provider().SecretAttrs(ecfg.Config) c.Assert(err, gc.IsNil) 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, gc.Equals, true) c.Check(actual, gc.Equals, expect) } // check storage bucket is configured correctly env := e.(*environ) c.Assert(env.Storage().(*ec2storage).bucket.Region.Name, gc.Equals, ecfg.region()) }
// 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() == "" { proxy := osenv.DetectProxies() logger.Tracef("Proxies detected %#v", proxy) setIfNotBlank("http-proxy", proxy.Http) setIfNotBlank("https-proxy", proxy.Https) setIfNotBlank("ftp-proxy", proxy.Ftp) setIfNotBlank("no-proxy", proxy.NoProxy) } if cfg.AptHttpProxy() == "" && cfg.AptHttpsProxy() == "" && cfg.AptFtpProxy() == "" { proxy, err := detectAptProxies() if err != nil { return nil, err } setIfNotBlank("apt-http-proxy", proxy.Http) setIfNotBlank("apt-https-proxy", proxy.Https) setIfNotBlank("apt-ftp-proxy", proxy.Ftp) } if len(attrs) > 0 { cfg, err = cfg.Apply(attrs) if err != nil { return nil, err } } return p.Open(cfg) }
func (t configTest) check(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "openstack", "control-bucket": "x", }).Merge(t.config) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) // Set environment variables if any. savedVars := make(map[string]string) if t.envVars != nil { for k, v := range t.envVars { savedVars[k] = os.Getenv(k) os.Setenv(k, v) } } defer restoreEnvVars(savedVars) e, err := environs.New(cfg) if t.change != nil { c.Assert(err, gc.IsNil) // Testing a change in configuration. var old, changed, valid *config.Config osenv := e.(*environ) old = osenv.ecfg().Config changed, err = old.Apply(t.change) c.Assert(err, gc.IsNil) // Keep err for validation below. valid, err = providerInstance.Validate(changed, old) if err == nil { err = osenv.SetConfig(valid) } } if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) return } c.Assert(err, gc.IsNil) ecfg := e.(*environ).ecfg() c.Assert(ecfg.Name(), gc.Equals, "testenv") c.Assert(ecfg.controlBucket(), gc.Equals, "x") if t.region != "" { c.Assert(ecfg.region(), gc.Equals, t.region) } if t.authMode != "" { c.Assert(ecfg.authMode(), gc.Equals, t.authMode) } if t.accessKey != "" { c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey) } if t.secretKey != "" { c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey) } if t.username != "" { c.Assert(ecfg.username(), gc.Equals, t.username) c.Assert(ecfg.password(), gc.Equals, t.password) c.Assert(ecfg.tenantName(), gc.Equals, t.tenantName) c.Assert(ecfg.authURL(), gc.Equals, t.authURL) expected := map[string]string{ "username": t.username, "password": t.password, "tenant-name": t.tenantName, } c.Assert(err, gc.IsNil) actual, err := e.Provider().SecretAttrs(ecfg.Config) c.Assert(err, gc.IsNil) c.Assert(expected, gc.DeepEquals, actual) } if t.firewallMode != "" { c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) } c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP) c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup) c.Assert(ecfg.network(), gc.Equals, t.network) // Default should be true expectedHostnameVerification := true if t.sslHostnameSet { expectedHostnameVerification = t.sslHostnameVerification } c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification) for name, expect := range t.expect { actual, found := ecfg.UnknownAttrs()[name] c.Check(found, gc.Equals, true) c.Check(actual, gc.Equals, expect) } }