func (s *ModelConfigSourceSuite) assertModelConfigValues(c *gc.C, modelCfg *config.Config, modelAttributes, controllerAttributes set.Strings) { expectedValues := make(config.ConfigValues) defaultAttributes := set.NewStrings() for defaultAttr := range config.ConfigDefaults() { defaultAttributes.Add(defaultAttr) } for attr, val := range modelCfg.AllAttrs() { source := "model" if defaultAttributes.Contains(attr) { source = "default" } if modelAttributes.Contains(attr) { source = "model" } if controllerAttributes.Contains(attr) { source = "controller" } expectedValues[attr] = config.ConfigValue{ Value: val, Source: source, } } sources, err := s.State.ModelConfigValues() c.Assert(err, jc.ErrorIsNil) c.Assert(sources, jc.DeepEquals, expectedValues) }
// finalizeConfig creates the config object from attributes, calls // PrepareForCreateEnvironment, and then finally validates the config // before returning it. func finalizeConfig(isAdmin bool, controllerCfg *config.Config, attrs map[string]interface{}) (*config.Config, error) { provider, err := environs.Provider(controllerCfg.Type()) if err != nil { return nil, errors.Trace(err) } // Controller admins creating models do not have to re-supply new secrets. // These may be copied from the controller model if not supplied. if isAdmin { maybeCopyControllerSecrets(provider, controllerCfg.AllAttrs(), attrs) } cfg, err := config.New(config.UseDefaults, attrs) if err != nil { return nil, errors.Annotate(err, "creating config from values failed") } cfg, err = provider.PrepareForCreateEnvironment(cfg) if err != nil { return nil, errors.Trace(err) } cfg, err = provider.Validate(cfg, nil) if err != nil { return nil, errors.Annotate(err, "provider validation failed") } return cfg, 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 }
// 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 }
// NewModelConfig returns a new model config given a base (controller) config // and a set of attributes that will be specific to the new model, overriding // any non-restricted attributes in the base configuration. The resulting // config will be suitable for creating a new model in state. // // If "attrs" does not include a UUID, a new, random one will be generated // and added to the config. // // The config will be validated with the provider before being returned. func (c ModelConfigCreator) NewModelConfig( isAdmin bool, base *config.Config, attrs map[string]interface{}, ) (*config.Config, error) { if err := c.checkVersion(base, attrs); err != nil { return nil, errors.Trace(err) } // Before comparing any values, we need to push the config through // the provider validation code. One of the reasons for this is that // numbers being serialized through JSON get turned into float64. The // schema code used in config will convert these back into integers. // However, before we can create a valid config, we need to make sure // we copy across fields from the main config that aren't there. baseAttrs := base.AllAttrs() restrictedFields, err := RestrictedProviderFields(base.Type()) if err != nil { return nil, errors.Trace(err) } for _, field := range restrictedFields { if _, ok := attrs[field]; !ok { if baseValue, ok := baseAttrs[field]; ok { attrs[field] = baseValue } } } // Generate a new UUID for the model as necessary, // and finalize the new config. if _, ok := attrs[config.UUIDKey]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, errors.Trace(err) } attrs[config.UUIDKey] = uuid.String() } cfg, err := finalizeConfig(isAdmin, base, attrs) if err != nil { return nil, errors.Trace(err) } attrs = cfg.AllAttrs() // Any values that would normally be copied from the controller // config can also be defined, but if they differ from the controller // values, an error is returned. for _, field := range restrictedFields { if value, ok := attrs[field]; ok { if serverValue := baseAttrs[field]; value != serverValue { return nil, errors.Errorf( "specified %s \"%v\" does not match controller \"%v\"", field, value, serverValue) } } } return cfg, nil }
func base64yaml(m *config.Config) string { data, err := goyaml.Marshal(m.AllAttrs()) if err != nil { // can't happen, these values have been validated a number of times panic(err) } return base64.StdEncoding.EncodeToString(data) }
// 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 *mongo.MongoInfo, cfg *config.Config, opts mongo.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, ok := cfg.UUID() if !ok { return nil, errors.Errorf("environment uuid was not supplied") } st.environTag = names.NewEnvironTag(uuid) ops := []txn.Op{ createConstraintsOp(st, environGlobalKey, constraints.Value{}), createSettingsOp(st, environGlobalKey, cfg.AllAttrs()), createEnvironmentOp(st, cfg.Name(), uuid), { C: stateServersC, Id: environGlobalKey, Assert: txn.DocMissing, Insert: &stateServersDoc{ EnvUUID: uuid, }, }, { C: stateServersC, Id: apiHostPortsKey, Assert: txn.DocMissing, Insert: &apiHostPortsDoc{}, }, { C: stateServersC, Id: stateServingInfoKey, Assert: txn.DocMissing, Insert: ¶ms.StateServingInfo{}, }, } 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 }
// Open opens an instance of the testing environment. func (t *Tests) Open(c *gc.C, cfg *config.Config) environs.Environ { e, err := environs.New(environs.OpenParams{ Cloud: t.CloudSpec(), Config: cfg, }) c.Assert(err, gc.IsNil, gc.Commentf("opening environ %#v", cfg.AllAttrs())) c.Assert(e, gc.NotNil) return e }
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) } } // If the user hasn't already specified a value, set it to the // given value. defineIfNot := func(keyName string, value interface{}) { if _, defined := cfg.AllAttrs()[keyName]; !defined { logger.Infof("%s was not defined. Defaulting to %v.", keyName, value) envConfig.attrs[keyName] = value } } // If the user hasn't specified a value, refresh the // available updates, but don't upgrade. defineIfNot("enable-os-refresh-update", true) defineIfNot("enable-os-upgrade", false) return envConfig, nil }
// 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 }
// 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) }
// 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 }
// bootstrapConfig returns a copy of the supplied configuration with the // admin-secret and ca-private-key attributes removed. If the resulting // config is not suitable for bootstrapping an environment, an error is // returned. // This function is copied from environs in here so we can avoid an import loop func bootstrapConfig(cfg *config.Config) (*config.Config, error) { m := cfg.AllAttrs() // We never want to push admin-secret or the root CA private key to the cloud. delete(m, "admin-secret") delete(m, "ca-private-key") cfg, err := config.New(config.NoDefaults, m) if err != nil { return nil, err } if _, ok := cfg.AgentVersion(); !ok { return nil, fmt.Errorf("model configuration has no agent-version") } 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) }
// 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 mongo.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 }
// 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) }
// checkModelConfig returns an error if the config is definitely invalid. func checkModelConfig(cfg *config.Config) error { allAttrs := cfg.AllAttrs() for _, attr := range disallowedModelConfigAttrs { if _, ok := allAttrs[attr]; ok { return errors.Errorf(attr + " should never be written to the state") } } if _, ok := cfg.AgentVersion(); !ok { return errors.Errorf("agent-version must always be set in state") } for attr := range allAttrs { if controller.ControllerOnlyAttribute(attr) { return errors.Errorf("cannot set controller attribute %q on a model", attr) } } return nil }
// check that any --env-config $base64 is valid and matches t.cfg.Config func checkEnvConfig(c *gc.C, cfg *config.Config, x map[interface{}]interface{}, scripts []string) { re := regexp.MustCompile(`--env-config '([^']+)'`) found := false for _, s := range scripts { m := re.FindStringSubmatch(s) if m == nil { continue } found = true buf, err := base64.StdEncoding.DecodeString(m[1]) c.Assert(err, gc.IsNil) var actual map[string]interface{} err = goyaml.Unmarshal(buf, &actual) c.Assert(err, gc.IsNil) c.Assert(cfg.AllAttrs(), jc.DeepEquals, actual) } c.Assert(found, gc.Equals, true) }
func (ts configTestSpec) checkSuccess(c *gc.C, value interface{}, err error) { if !c.Check(err, jc.ErrorIsNil) { return } var cfg *config.Config switch typed := value.(type) { case *config.Config: cfg = typed case environs.Environ: cfg = typed.Config() } attrs := cfg.AllAttrs() for field, value := range ts.expect { c.Check(attrs[field], gc.Equals, value) } }
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, p.Configurator.GetConfigDefaults()) if err != nil { return nil, err } ecfg := &environConfig{cfg, validated} // Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message // even if they are not running with --debug. cfgAttrs := cfg.AllAttrs() if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "Your cloud provider should have set up image metadata to provide the correct image id\n"+ "for your chosen series and architecture. If this is a private Openstack deployment without\n"+ "existing image metadata, please run 'juju-metadata help' to see how suitable image"+ "metadata can be generated.", "default-image-id", defaultImageId) logger.Warningf(msg) } if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "The correct instance flavor is determined using constraints, globally specified\n"+ "when an model is bootstrapped, or individually when a charm is deployed.\n"+ "See 'juju help bootstrap' or 'juju help deploy'.", "default-instance-type", defaultInstanceType) logger.Warningf(msg) } // Construct a new config with the deprecated attributes removed. for _, attr := range []string{"default-image-id", "default-instance-type"} { delete(cfgAttrs, attr) delete(ecfg.attrs, attr) } for k, v := range ecfg.attrs { cfgAttrs[k] = v } return config.New(config.NoDefaults, cfgAttrs) }
func (st *State) envSetupOps(cfg *config.Config, envUUID, serverUUID string, owner names.UserTag) ([]txn.Op, error) { if err := checkEnvironConfig(cfg); err != nil { return nil, errors.Trace(err) } // When creating the state server environment, the new environment // UUID is also used as the state server UUID. if serverUUID == "" { serverUUID = envUUID } envUserOp := createEnvUserOp(envUUID, owner, owner, owner.Name(), false) ops := []txn.Op{ createConstraintsOp(st, environGlobalKey, constraints.Value{}), createSettingsOp(environGlobalKey, cfg.AllAttrs()), incHostedEnvironCountOp(), createEnvironmentOp(st, owner, cfg.Name(), envUUID, serverUUID), createUniqueOwnerEnvNameOp(owner, cfg.Name()), envUserOp, } return ops, nil }
func (st *State) envSetupOps(cfg *config.Config, modelUUID, serverUUID string, owner names.UserTag) ([]txn.Op, error) { if err := checkModelConfig(cfg); err != nil { return nil, errors.Trace(err) } // When creating the state server model, the new model // UUID is also used as the state server UUID. if serverUUID == "" { serverUUID = modelUUID } modelUserOp := createModelUserOp(modelUUID, owner, owner, owner.Name(), false) ops := []txn.Op{ createConstraintsOp(st, modelGlobalKey, constraints.Value{}), createSettingsOp(modelGlobalKey, cfg.AllAttrs()), incHostedModelCountOp(), createModelOp(st, owner, cfg.Name(), modelUUID, serverUUID), createUniqueOwnerModelNameOp(owner, cfg.Name()), modelUserOp, } return ops, nil }
func (ts configTestSpec) checkSuccess(c *gc.C, value interface{}, err error) { if !c.Check(err, jc.ErrorIsNil) { return } var cfg *config.Config switch typed := value.(type) { case *config.Config: cfg = typed case environs.Environ: cfg = typed.Config() } attrs := cfg.AllAttrs() for field, value := range ts.expect { if field == "auth-file" && value != nil && value.(string) != "" { value = filepath.Join(ts.rootDir, value.(string)) } c.Check(attrs[field], gc.Equals, value) } }
// correctLocalhostURLs exams proxy attributes and changes URL values pointing to localhost to use bridge IP. func (p environProvider) correctLocalhostURLs(cfg *config.Config, providerCfg *environConfig) (*config.Config, error) { attrs := cfg.AllAttrs() updatedAttrs := make(map[string]interface{}) for _, key := range config.ProxyAttributes { anAttr := attrs[key] if anAttr == nil { continue } var attrStr string var isString bool if attrStr, isString = anAttr.(string); !isString || attrStr == "" { continue } newValue, err := p.swapLocalhostForBridgeIP(attrStr, providerCfg) if err != nil { return nil, errors.Trace(err) } updatedAttrs[key] = newValue logger.Infof("\nAttribute %q is set to (%v)\n", key, newValue) } // Update desired attributes on current configuration return cfg.Apply(updatedAttrs) }
func (st *State) modelSetupOps(cfg *config.Config, modelUUID, serverUUID string, owner names.UserTag, mode MigrationMode) ([]txn.Op, error) { if err := checkModelConfig(cfg); err != nil { return nil, errors.Trace(err) } modelStatusDoc := statusDoc{ ModelUUID: modelUUID, // TODO(fwereade): 2016-03-17 lp:1558657 Updated: time.Now().UnixNano(), // TODO(axw) 2016-04-13 lp:1569632 // We need to decide how we will // represent migration in model status. Status: status.StatusAvailable, } // When creating the controller model, the new model // UUID is also used as the controller UUID. if serverUUID == "" { serverUUID = modelUUID } modelUserOp := createModelUserOp(modelUUID, owner, owner, owner.Name(), nowToTheSecond(), ModelAdminAccess) ops := []txn.Op{ createStatusOp(st, modelGlobalKey, modelStatusDoc), createConstraintsOp(st, modelGlobalKey, constraints.Value{}), createSettingsOp(modelGlobalKey, cfg.AllAttrs()), } if modelUUID != serverUUID { ops = append(ops, incHostedModelCountOp()) } ops = append(ops, createModelEntityRefsOp(st, modelUUID), createModelOp(st, owner, cfg.Name(), modelUUID, serverUUID, mode), createUniqueOwnerModelNameOp(owner, cfg.Name()), modelUserOp, ) return ops, 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 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, errors.Annotatef(err, "failed to validate unknown attrs") } localConfig := newEnvironConfig(cfg, validated) // Set correct default network bridge if needed // fix for http://pad.lv/1394450 localConfig.setDefaultNetworkBridge() // Before potentially creating directories, make sure that the // root directory has not changed. if localConfig.namespace() == "" { return nil, errors.New("missing namespace, config not prepared") } containerType := localConfig.container() if old != nil { oldLocalConfig, err := provider.newConfig(old) if err != nil { return nil, errors.Annotatef(err, "old config is not a valid local config: %v", old) } if containerType != oldLocalConfig.container() { return nil, errors.Errorf("cannot change container from %q to %q", oldLocalConfig.container(), containerType) } if localConfig.rootDir() != oldLocalConfig.rootDir() { return nil, errors.Errorf("cannot change root-dir from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.networkBridge() != oldLocalConfig.networkBridge() { return nil, errors.Errorf("cannot change network-bridge from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.storagePort() != oldLocalConfig.storagePort() { return nil, errors.Errorf("cannot change storage-port from %v to %v", oldLocalConfig.storagePort(), localConfig.storagePort()) } if localConfig.namespace() != oldLocalConfig.namespace() { return nil, errors.Errorf("cannot change namespace from %v to %v", oldLocalConfig.namespace(), localConfig.namespace()) } } // Currently only supported containers are "lxc" and "kvm". if containerType != instance.LXC && containerType != instance.KVM { return nil, errors.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 the user hasn't already specified a value, set it to the // given value. defineIfNot := func(keyName string, value interface{}) { if _, defined := cfg.AllAttrs()[keyName]; !defined { logger.Infof("lxc-clone is enabled. Switching %s to %v", keyName, value) localConfig.attrs[keyName] = value } } // If we're cloning, and the user hasn't specified otherwise, // prefer to skip update logic. if useClone, _ := localConfig.LXCUseClone(); useClone && containerType == instance.LXC { defineIfNot("enable-os-refresh-update", true) defineIfNot("enable-os-upgrade", false) } // Apply the coerced unknown values back into the config. return cfg.Apply(localConfig.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, p.Configurator.GetConfigDefaults()) if err != nil { return nil, err } // Add Openstack specific defaults. providerDefaults := map[string]interface{}{} // Storage. if _, ok := cfg.StorageDefaultBlockSource(); !ok { providerDefaults[config.StorageDefaultBlockSourceKey] = CinderProviderType } if len(providerDefaults) > 0 { if cfg, err = cfg.Apply(providerDefaults); err != nil { return nil, err } } ecfg := &environConfig{cfg, validated} if ecfg.authURL() != "" { parts, err := url.Parse(ecfg.authURL()) if err != nil || parts.Host == "" || parts.Scheme == "" { return nil, fmt.Errorf("invalid auth-url value %q", ecfg.authURL()) } } cred := identity.CredentialsFromEnv() format := "required model variable not set for credentials attribute: %s" switch ecfg.authMode() { case AuthUserPass, AuthLegacy: if ecfg.username() == "" { if cred.User == "" { return nil, fmt.Errorf(format, "User") } ecfg.attrs["username"] = cred.User } if ecfg.password() == "" { if cred.Secrets == "" { return nil, fmt.Errorf(format, "Secrets") } ecfg.attrs["password"] = cred.Secrets } case AuthKeyPair: if ecfg.accessKey() == "" { if cred.User == "" { return nil, fmt.Errorf(format, "User") } ecfg.attrs["access-key"] = cred.User } if ecfg.secretKey() == "" { if cred.Secrets == "" { return nil, fmt.Errorf(format, "Secrets") } ecfg.attrs["secret-key"] = cred.Secrets } default: return nil, fmt.Errorf("unexpected authentication mode %q", ecfg.authMode()) } if ecfg.authURL() == "" { if cred.URL == "" { return nil, fmt.Errorf(format, "URL") } ecfg.attrs["auth-url"] = cred.URL } if ecfg.tenantName() == "" { if cred.TenantName == "" { return nil, fmt.Errorf(format, "TenantName") } ecfg.attrs["tenant-name"] = cred.TenantName } if ecfg.region() == "" { if cred.Region == "" { return nil, fmt.Errorf(format, "Region") } ecfg.attrs["region"] = cred.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()) } } // Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message // even if they are not running with --debug. cfgAttrs := cfg.AllAttrs() if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "Your cloud provider should have set up image metadata to provide the correct image id\n"+ "for your chosen series and archietcure. If this is a private Openstack deployment without\n"+ "existing image metadata, please run 'juju-metadata help' to see how suitable image"+ "metadata can be generated.", "default-image-id", defaultImageId) logger.Warningf(msg) } if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "The correct instance flavor is determined using constraints, globally specified\n"+ "when an model is bootstrapped, or individually when a charm is deployed.\n"+ "See 'juju help bootstrap' or 'juju help deploy'.", "default-instance-type", defaultInstanceType) logger.Warningf(msg) } // Construct a new config with the deprecated attributes removed. for _, attr := range []string{"default-image-id", "default-instance-type"} { delete(cfgAttrs, attr) delete(ecfg.attrs, attr) } for k, v := range ecfg.attrs { cfgAttrs[k] = v } return config.New(config.NoDefaults, cfgAttrs) }
// Open opens an instance of the testing environment. func (t *Tests) Open(c *gc.C, cfg *config.Config) environs.Environ { e, err := environs.New(cfg) c.Assert(err, gc.IsNil, gc.Commentf("opening environ %#v", cfg.AllAttrs())) c.Assert(e, gc.NotNil) return e }
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, p.Configurator.GetConfigDefaults()) if err != nil { return nil, err } ecfg := &environConfig{cfg, validated} switch ecfg.authMode() { case AuthUserPass, AuthLegacy: if ecfg.username() == "" { return nil, errors.NotValidf("missing username") } if ecfg.password() == "" { return nil, errors.NotValidf("missing password") } case AuthKeyPair: if ecfg.accessKey() == "" { return nil, errors.NotValidf("missing access-key") } if ecfg.secretKey() == "" { return nil, errors.NotValidf("missing secret-key") } default: return nil, fmt.Errorf("unexpected authentication mode %q", ecfg.authMode()) } if ecfg.authURL() == "" { return nil, errors.NotValidf("missing auth-url") } if ecfg.tenantName() == "" { return nil, errors.NotValidf("missing tenant-name") } if ecfg.region() == "" { return nil, errors.NotValidf("missing region") } parts, err := url.Parse(ecfg.authURL()) if err != nil || parts.Host == "" || parts.Scheme == "" { return nil, fmt.Errorf("invalid auth-url value %q", ecfg.authURL()) } 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()) } } // Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message // even if they are not running with --debug. cfgAttrs := cfg.AllAttrs() if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "Your cloud provider should have set up image metadata to provide the correct image id\n"+ "for your chosen series and archietcure. If this is a private Openstack deployment without\n"+ "existing image metadata, please run 'juju-metadata help' to see how suitable image"+ "metadata can be generated.", "default-image-id", defaultImageId) logger.Warningf(msg) } if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "The correct instance flavor is determined using constraints, globally specified\n"+ "when an model is bootstrapped, or individually when a charm is deployed.\n"+ "See 'juju help bootstrap' or 'juju help deploy'.", "default-instance-type", defaultInstanceType) logger.Warningf(msg) } // Construct a new config with the deprecated attributes removed. for _, attr := range []string{"default-image-id", "default-instance-type"} { delete(cfgAttrs, attr) delete(ecfg.attrs, attr) } for k, v := range ecfg.attrs { cfgAttrs[k] = v } return config.New(config.NoDefaults, cfgAttrs) }