// newConfig builds a new environConfig from the provided Config // filling in default values, if any. It returns an error if the // resulting configuration is not valid. func newConfig(cfg, old *config.Config) (*environConfig, error) { // Ensure that the provided config is valid. if err := config.Validate(cfg, old); err != nil { return nil, errors.Trace(err) } attrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, errors.Trace(err) } if old != nil { // There's an old configuration. Validate it so that any // default values are correctly coerced for when we check // the old values later. oldEcfg, err := newConfig(old, nil) if err != nil { return nil, errors.Annotatef(err, "invalid base config") } for _, attr := range configImmutableFields { oldv, newv := oldEcfg.attrs[attr], attrs[attr] if oldv != newv { return nil, errors.Errorf( "%s: cannot change from %v to %v", attr, oldv, newv, ) } } } ecfg := &environConfig{ config: cfg, attrs: attrs, } return ecfg, nil }
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 } newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envConfig := &environConfig{cfg, newAttrs} // If an old config was supplied, check any immutable fields have not changed. if old != nil { oldEnvConfig, err := validateConfig(old, nil) if err != nil { return nil, err } for _, field := range configImmutableFields { if oldEnvConfig.attrs[field] != envConfig.attrs[field] { return nil, fmt.Errorf( "%s: cannot change from %v to %v", field, oldEnvConfig.attrs[field], envConfig.attrs[field], ) } } } // Check for missing fields. for _, field := range requiredFields { if nilOrEmptyString(envConfig.attrs[field]) { return nil, fmt.Errorf("%s: must not be empty", field) } } return envConfig, nil }
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 } ecfg := &environConfig{cfg, validated} if vpcID := ecfg.vpcID(); isVPCIDSetButInvalid(vpcID) { return nil, fmt.Errorf("vpc-id: %q is not a valid AWS VPC ID", vpcID) } else if !isVPCIDSet(vpcID) && ecfg.forceVPCID() { return nil, fmt.Errorf("cannot use vpc-id-force without specifying vpc-id as well") } if old != nil { attrs := old.UnknownAttrs() if vpcID, _ := attrs["vpc-id"].(string); vpcID != ecfg.vpcID() { return nil, fmt.Errorf("cannot change vpc-id from %q to %q", vpcID, ecfg.vpcID()) } if forceVPCID, _ := attrs["vpc-id-force"].(bool); forceVPCID != ecfg.forceVPCID() { return nil, fmt.Errorf("cannot change vpc-id-force from %v to %v", forceVPCID, ecfg.forceVPCID()) } } // ssl-hostname-verification cannot be disabled if !ecfg.SSLHostnameVerification() { return nil, fmt.Errorf("disabling ssh-hostname-verification is not supported") } return ecfg, nil }
// 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 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 }
// 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 (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 }
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) }
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) }
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 } newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envConfig := &environConfig{cfg, newAttrs} // If an old config was supplied, check any immutable fields have not changed. if old != nil { oldEnvConfig, err := validateConfig(old, nil) if err != nil { return nil, err } for _, field := range configImmutableFields { if oldEnvConfig.attrs[field] != envConfig.attrs[field] { return nil, fmt.Errorf( "%s: cannot change from %v to %v", field, oldEnvConfig.attrs[field], envConfig.attrs[field], ) } } } // Read env variables to fill in any missing fields. for field, envVar := range environmentVariables { // If field is not set, get it from env variables if nilOrEmptyString(envConfig.attrs[field]) { localEnvVariable := os.Getenv(envVar) if localEnvVariable != "" { envConfig.attrs[field] = localEnvVariable } else { return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar) } } } if err := ensurePrivateKey(envConfig); err != nil { return nil, err } // Check for missing fields. for _, field := range requiredFields { if nilOrEmptyString(envConfig.attrs[field]) { return nil, fmt.Errorf("%s: must not be empty", field) } } return envConfig, nil }
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 }
// 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 (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) }
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 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) }
// newValidConfig builds a new environConfig from the provided Config // and returns it. This includes applying the provided defaults // values, if any. The resulting config values are validated. func newValidConfig(cfg *config.Config, defaults map[string]interface{}) (*environConfig, error) { // Any auth credentials handling should happen first... // 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. fixedDefaults, unset := adjustDefaults(cfg, defaults) cfg, err := cfg.Remove(unset) if err != nil { return nil, errors.Trace(err) } validated, err := cfg.ValidateUnknownAttrs(configFields, fixedDefaults) 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) // Update to defaults set via client config. clientCfg, err := ecfg.clientConfig() if err != nil { return nil, errors.Trace(err) } ecfg, err = ecfg.updateForClientConfig(clientCfg) if err != nil { return nil, errors.Trace(err) } // Do final (more complex, provider-specific) validation. if err := ecfg.validate(); err != nil { return nil, errors.Trace(err) } return ecfg, 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 } 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) }
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 }
// 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) }
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 } ecfg := &environConfig{cfg, validated} if ecfg.accessKey() == "" || ecfg.secretKey() == "" { auth, err := aws.EnvAuth() if err != nil || ecfg.accessKey() != "" || ecfg.secretKey() != "" { return nil, fmt.Errorf("model 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()) } } // ssl-hostname-verification cannot be disabled if !ecfg.SSLHostnameVerification() { return nil, fmt.Errorf("disabling ssh-hostname-verification is not supported") } return ecfg, nil }
// 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 validateConfig(newCfg, oldCfg *config.Config) (*azureModelConfig, error) { err := config.Validate(newCfg, oldCfg) if err != nil { return nil, err } validated, err := newCfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } if oldCfg != nil { // Ensure immutable configuration isn't changed. oldUnknownAttrs := oldCfg.UnknownAttrs() for _, key := range immutableConfigAttributes { oldValue, hadValue := oldUnknownAttrs[key].(string) if hadValue { newValue, haveValue := validated[key].(string) if !haveValue { return nil, errors.Errorf( "cannot remove immutable %q config", key, ) } if newValue != oldValue { return nil, errors.Errorf( "cannot change immutable %q config (%v -> %v)", key, oldValue, newValue, ) } } // It's valid to go from not having to having. } } // Resource group names must not exceed 80 characters. Resource group // names are based on the model UUID and model name, the latter of // which the model creator controls. modelTag := names.NewModelTag(newCfg.UUID()) resourceGroup := resourceGroupName(modelTag, newCfg.Name()) if n := len(resourceGroup); n > resourceNameLengthMax { smallestResourceGroup := resourceGroupName(modelTag, "") return nil, errors.Errorf(`resource group name %q is too long Please choose a model name of no more than %d characters.`, resourceGroup, resourceNameLengthMax-len(smallestResourceGroup), ) } if newCfg.FirewallMode() == config.FwGlobal { // We do not currently support the "global" firewall mode. return nil, errNoFwGlobal } storageAccountType := validated[configAttrStorageAccountType].(string) if !isKnownStorageAccountType(storageAccountType) { return nil, errors.Errorf( "invalid storage account type %q, expected one of: %q", storageAccountType, knownStorageAccountTypes, ) } azureConfig := &azureModelConfig{ newCfg, storageAccountType, } return azureConfig, nil }
func validateConfig(newCfg, oldCfg *config.Config) (*azureModelConfig, error) { err := config.Validate(newCfg, oldCfg) if err != nil { return nil, err } validated, err := newCfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } // Ensure required configuration is provided. for _, key := range requiredConfigAttributes { if value, ok := validated[key].(string); !ok || value == "" { return nil, errors.Errorf("%q config not specified", key) } } if oldCfg != nil { // Ensure immutable configuration isn't changed. oldUnknownAttrs := oldCfg.UnknownAttrs() for _, key := range immutableConfigAttributes { oldValue, hadValue := oldUnknownAttrs[key].(string) if hadValue { newValue, haveValue := validated[key].(string) if !haveValue { return nil, errors.Errorf( "cannot remove immutable %q config", key, ) } if newValue != oldValue { return nil, errors.Errorf( "cannot change immutable %q config (%v -> %v)", key, oldValue, newValue, ) } } // It's valid to go from not having to having. } // TODO(axw) figure out how we intend to handle changing // secrets, such as application key } // Resource group names must not exceed 80 characters. Resource group // names are based on the model UUID and model name, the latter of // which the model creator controls. modelTag := names.NewModelTag(newCfg.UUID()) resourceGroup := resourceGroupName(modelTag, newCfg.Name()) if n := len(resourceGroup); n > resourceNameLengthMax { smallestResourceGroup := resourceGroupName(modelTag, "") return nil, errors.Errorf(`resource group name %q is too long Please choose a model name of no more than %d characters.`, resourceGroup, resourceNameLengthMax-len(smallestResourceGroup), ) } location := canonicalLocation(validated[configAttrLocation].(string)) endpoint := validated[configAttrEndpoint].(string) storageEndpoint := validated[configAttrStorageEndpoint].(string) appId := validated[configAttrAppId].(string) subscriptionId := validated[configAttrSubscriptionId].(string) tenantId := validated[configAttrTenantId].(string) appPassword := validated[configAttrAppPassword].(string) storageAccount, _ := validated[configAttrStorageAccount].(string) storageAccountKey, _ := validated[configAttrStorageAccountKey].(string) storageAccountType := validated[configAttrStorageAccountType].(string) controllerResourceGroup := validated[configAttrControllerResourceGroup].(string) if newCfg.FirewallMode() == config.FwGlobal { // We do not currently support the "global" firewall mode. return nil, errNoFwGlobal } if !isKnownStorageAccountType(storageAccountType) { return nil, errors.Errorf( "invalid storage account type %q, expected one of: %q", storageAccountType, knownStorageAccountTypes, ) } // The Azure storage code wants the endpoint host only, not the URL. storageEndpointURL, err := url.Parse(storageEndpoint) if err != nil { return nil, errors.Annotate(err, "parsing storage endpoint URL") } token, err := azure.NewServicePrincipalToken( appId, appPassword, tenantId, azure.AzureResourceManagerScope, ) if err != nil { return nil, errors.Annotate(err, "constructing service principal token") } azureConfig := &azureModelConfig{ newCfg, token, subscriptionId, location, endpoint, storageEndpointURL.Host, storageAccount, storageAccountKey, storage.AccountType(storageAccountType), controllerResourceGroup, } return azureConfig, nil }
func validateConfig(newCfg, oldCfg *config.Config) (*azureModelConfig, error) { err := config.Validate(newCfg, oldCfg) if err != nil { return nil, err } validated, err := newCfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } // Ensure required configuration is provided. for _, key := range requiredConfigAttributes { if value, ok := validated[key].(string); !ok || value == "" { return nil, errors.Errorf("%q config not specified", key) } } if oldCfg != nil { // Ensure immutable configuration isn't changed. oldUnknownAttrs := oldCfg.UnknownAttrs() for _, key := range immutableConfigAttributes { oldValue, hadValue := oldUnknownAttrs[key].(string) if hadValue { newValue, haveValue := validated[key].(string) if !haveValue { return nil, errors.Errorf( "cannot remove immutable %q config", key, ) } if newValue != oldValue { return nil, errors.Errorf( "cannot change immutable %q config (%v -> %v)", key, oldValue, newValue, ) } } // It's valid to go from not having to having. } // TODO(axw) figure out how we intend to handle changing // secrets, such as application key } location := canonicalLocation(validated[configAttrLocation].(string)) appId := validated[configAttrAppId].(string) subscriptionId := validated[configAttrSubscriptionId].(string) tenantId := validated[configAttrTenantId].(string) appPassword := validated[configAttrAppPassword].(string) storageAccount, _ := validated[configAttrStorageAccount].(string) storageAccountKey, _ := validated[configAttrStorageAccountKey].(string) storageAccountType := validated[configAttrStorageAccountType].(string) controllerResourceGroup := validated[configAttrControllerResourceGroup].(string) if newCfg.FirewallMode() == config.FwGlobal { // We do not currently support the "global" firewall mode. return nil, errNoFwGlobal } if !isKnownStorageAccountType(storageAccountType) { return nil, errors.Errorf( "invalid storage account type %q, expected one of: %q", storageAccountType, knownStorageAccountTypes, ) } token, err := azure.NewServicePrincipalToken( appId, appPassword, tenantId, azure.AzureResourceManagerScope, ) if err != nil { return nil, errors.Annotate(err, "constructing service principal token") } azureConfig := &azureModelConfig{ newCfg, token, subscriptionId, location, storageAccount, storageAccountKey, storage.AccountType(storageAccountType), controllerResourceGroup, } return azureConfig, nil }
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 } newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envConfig := &environConfig{cfg, newAttrs} // If an old config was supplied, check any immutable fields have not changed. if old != nil { oldEnvConfig, err := validateConfig(old, nil) if err != nil { return nil, err } for _, field := range configImmutableFields { if oldEnvConfig.attrs[field] != envConfig.attrs[field] { return nil, fmt.Errorf( "%s: cannot change from %v to %v", field, oldEnvConfig.attrs[field], envConfig.attrs[field], ) } } } // Read env variables to fill in any missing fields. for field, envVar := range environmentVariables { // If field is not set, get it from env variables if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" { localEnvVariable := os.Getenv(envVar) if localEnvVariable != "" { envConfig.attrs[field] = localEnvVariable } else { if field != "private-key-path" { return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar) } } } } // Ensure private-key-path is set - if it's not in config or an env var, use a default value. if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" { v = os.Getenv(environmentVariables["private-key-path"]) if v == "" { v = DefaultPrivateKey } envConfig.attrs["private-key-path"] = v } // Now that we've ensured private-key-path is properly set, we go back and set // up the private key - this is used to sign requests. if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" { keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string)) if err != nil { return nil, err } privateKey, err := ioutil.ReadFile(keyFile) if err != nil { return nil, err } envConfig.attrs["private-key"] = string(privateKey) } // Check for missing fields. for field := range configFields { if envConfig.attrs[field] == "" { return nil, fmt.Errorf("%s: must not be empty", field) } } return envConfig, 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, 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) }
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) }