Skip to content

Commit

Permalink
Take account of provider specific config when creating models and boo…
Browse files Browse the repository at this point in the history
…tstrapping
  • Loading branch information
wallyworld committed Aug 24, 2016
1 parent 54890d6 commit 0d95cef
Show file tree
Hide file tree
Showing 21 changed files with 262 additions and 26 deletions.
33 changes: 29 additions & 4 deletions cmd/juju/commands/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/juju/cmd"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"github.com/juju/schema"
"github.com/juju/utils"
"github.com/juju/utils/featureflag"
"github.com/juju/version"
Expand Down Expand Up @@ -372,14 +373,14 @@ func (c *bootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
return errors.Trace(err)
}

provider, err := environs.Provider(cloud.Type)
if err != nil {
return errors.Trace(err)
}
// Custom clouds may not have explicitly declared support for any auth-
// types, in which case we'll assume that they support everything that
// the provider supports.
if len(cloud.AuthTypes) == 0 {
provider, err := environs.Provider(cloud.Type)
if err != nil {
return errors.Trace(err)
}
for authType := range provider.CredentialSchemas() {
cloud.AuthTypes = append(cloud.AuthTypes, authType)
}
Expand Down Expand Up @@ -452,9 +453,33 @@ func (c *bootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
if err != nil {
return errors.Trace(err)
}

// The provider may define some custom attributes specific
// to the provider. These will be added to the model config.
providerAttrs := make(map[string]interface{})
if ps, ok := provider.(config.ConfigSource); ok {
for attr := range ps.ProviderConfig() {
if v, ok := userConfigAttrs[attr]; ok {
providerAttrs[attr] = v
}
}
fields := schema.FieldMap(ps.ProviderConfig(), ps.ProviderConfigDefaults())
if coercedAttrs, err := fields.Coerce(providerAttrs, nil); err != nil {
return errors.Annotatef(err, "invalid attribute value(s) for %v cloud", cloud.Type)
} else {
providerAttrs = coercedAttrs.(map[string]interface{})
}
}
logger.Debugf("provider attrs: %v", providerAttrs)
for k, v := range userConfigAttrs {
modelConfigAttrs[k] = v
}
// Provider specific attributes are either already specified in model
// config (but may have been coerced), or were not present. Either way,
// copy them in.
for k, v := range providerAttrs {
modelConfigAttrs[k] = v
}
bootstrapConfigAttrs := make(map[string]interface{})
controllerConfigAttrs := make(map[string]interface{})
// Based on the attribute names in clouds.yaml, create
Expand Down
10 changes: 7 additions & 3 deletions cmd/juju/commands/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ func (s *BootstrapSuite) run(c *gc.C, test bootstrapTest) testing.Restorer {
"type": "dummy",
"default-series": "raring",
"authorized-keys": "public auth key\n",
// Dummy provider defaults
"broken": "",
"secret": "pork",
"controller": false,
}
for k, v := range config.ConfigDefaults() {
if _, ok := expected[k]; !ok {
Expand Down Expand Up @@ -1157,7 +1161,7 @@ func (s *BootstrapSuite) TestBootstrapConfigFile(c *gc.C) {
c, s.newBootstrapCommand(), "ctrl", "dummy",
"--config", configFile,
)
c.Assert(err, gc.ErrorMatches, `controller: expected bool, got string.*`)
c.Assert(err, gc.ErrorMatches, `invalid attribute value\(s\) for dummy cloud: controller: expected bool, got string.*`)
}

func (s *BootstrapSuite) TestBootstrapMultipleConfigFiles(c *gc.C) {
Expand Down Expand Up @@ -1210,9 +1214,9 @@ func (s *BootstrapSuite) TestBootstrapCloudConfigAndAdHoc(c *gc.C) {
"--auto-upgrade",
// Configuration specified on the command line overrides
// anything specified in files, no matter what the order.
"--config", "controller=false",
"--config", "controller=not-a-bool",
)
c.Assert(err, gc.ErrorMatches, "failed to bootstrap model: dummy.Bootstrap is broken")
c.Assert(err, gc.ErrorMatches, `invalid attribute value\(s\) for dummy cloud: controller: expected bool, got .*`)
}

func (s *BootstrapSuite) TestBootstrapPrintClouds(c *gc.C) {
Expand Down
2 changes: 1 addition & 1 deletion environs/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ func (cfg *Config) ValidateUnknownAttrs(fields schema.Fields, defaults schema.De
if fields[name] == nil {
if val, isString := value.(string); isString && val != "" {
// only warn about attributes with non-empty string values
logger.Errorf("unknown config field %q", name)
logger.Warningf("unknown config field %q", name)
}
result[name] = value
}
Expand Down
16 changes: 16 additions & 0 deletions environs/config/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

package config

import (
"github.com/juju/schema"
)

// These constants define named sources of model config attributes.
// After a call to UpdateModelConfig, any attributes added/removed
// will have a source of JujuModelConfigSource.
Expand Down Expand Up @@ -42,3 +46,15 @@ func (c ConfigValues) AllAttrs() map[string]interface{} {
}
return result
}

// ConfigSource instances provide information on config attributes
// and the default attribute values.
type ConfigSource interface {
// ProviderConfig returns extra config attributes specific
// to this provider only.
ProviderConfig() schema.Fields

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
ProviderConfigDefaults() schema.Defaults
}
14 changes: 14 additions & 0 deletions provider/dummy/environs.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,20 @@ func (p *environProvider) Schema() environschema.Fields {
return fields
}

var _ config.ConfigSource = (*environProvider)(nil)

// ProviderConfig returns extra config attributes specific
// to this provider only.
func (p environProvider) ProviderConfig() schema.Fields {
return configFields
}

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
func (p environProvider) ProviderConfigDefaults() schema.Defaults {
return configDefaults
}

func (p *environProvider) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema {
return map[cloud.AuthType]cloud.CredentialSchema{cloud.EmptyAuthType: {}}
}
Expand Down
12 changes: 12 additions & 0 deletions provider/ec2/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ func (environProvider) Schema() environschema.Fields {
return fields
}

// ProviderConfig returns extra config attributes specific
// to this provider only.
func (p environProvider) ProviderConfig() schema.Fields {
return configFields
}

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
func (p environProvider) ProviderConfigDefaults() schema.Defaults {
return configDefaults
}

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 {
Expand Down
2 changes: 2 additions & 0 deletions provider/ec2/environ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/config"
"github.com/juju/juju/environs/simplestreams"
"github.com/juju/juju/instance"
"github.com/juju/juju/network"
Expand All @@ -18,6 +19,7 @@ import (
// Ensure EC2 provider supports the expected interfaces,
var (
_ environs.NetworkingEnviron = (*environ)(nil)
_ config.ConfigSource = (*environProvider)(nil)
_ simplestreams.HasRegion = (*environ)(nil)
_ state.Prechecker = (*environ)(nil)
_ instance.Distributor = (*environ)(nil)
Expand Down
13 changes: 13 additions & 0 deletions provider/gce/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package gce

import (
"github.com/juju/errors"
"github.com/juju/schema"
"gopkg.in/juju/environschema.v1"

"github.com/juju/juju/cloud"
Expand Down Expand Up @@ -59,6 +60,18 @@ func (environProvider) Schema() environschema.Fields {
return fields
}

// ProviderConfig returns extra config attributes specific
// to this provider only.
func (p environProvider) ProviderConfig() schema.Fields {
return configFields
}

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
func (p environProvider) ProviderConfigDefaults() schema.Defaults {
return configDefaults
}

// UpgradeModelConfig is specified in the ModelConfigUpgrader interface.
func (environProvider) UpgradeConfig(cfg *config.Config) (*config.Config, error) {
return configWithDefaults(cfg)
Expand Down
5 changes: 5 additions & 0 deletions provider/gce/testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ import (
coretools "github.com/juju/juju/tools"
)

// Ensure GCE provider supports the expected interfaces.
var (
_ config.ConfigSource = (*environProvider)(nil)
)

// These values are fake GCE auth credentials for use in tests.
const (
ClientName = "ba9876543210-0123456789abcdefghijklmnopqrstuv"
Expand Down
13 changes: 13 additions & 0 deletions provider/lxd/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package lxd

import (
"github.com/juju/errors"
"github.com/juju/schema"
"gopkg.in/juju/environschema.v1"

"github.com/juju/juju/cloud"
Expand Down Expand Up @@ -95,3 +96,15 @@ func (environProvider) Schema() environschema.Fields {
}
return fields
}

// ProviderConfig returns extra config attributes specific
// to this provider only.
func (p environProvider) ProviderConfig() schema.Fields {
return configFields
}

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
func (p environProvider) ProviderConfigDefaults() schema.Defaults {
return configDefaults
}
5 changes: 5 additions & 0 deletions provider/lxd/testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import (
"github.com/juju/version"
)

// Ensure LXD provider supports the expected interfaces.
var (
_ config.ConfigSource = (*environProvider)(nil)
)

// These values are stub LXD client credentials for use in tests.
const (
PublicKey = `-----BEGIN CERTIFICATE-----
Expand Down
12 changes: 12 additions & 0 deletions provider/maas/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ func (maasEnvironProvider) Schema() environschema.Fields {
return fields
}

// ProviderConfig returns extra config attributes specific
// to this provider only.
func (p maasEnvironProvider) ProviderConfig() schema.Fields {
return configFields
}

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
func (p maasEnvironProvider) ProviderConfigDefaults() schema.Defaults {
return configDefaults
}

func (prov maasEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) {
// Validate base configuration change before validating MAAS specifics.
err := config.Validate(cfg, oldCfg)
Expand Down
5 changes: 5 additions & 0 deletions provider/maas/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
"github.com/juju/juju/testing"
)

// Ensure MAAS provider supports the expected interfaces.
var (
_ config.ConfigSource = (*maasEnvironProvider)(nil)
)

type configSuite struct {
testing.BaseSuite
}
Expand Down
14 changes: 14 additions & 0 deletions provider/openstack/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var configSchema = environschema.Fields{
},
}

var configDefaults = schema.Defaults{}

var configFields = func() schema.Fields {
fs, _, err := configSchema.ValidationSchema()
if err != nil {
Expand Down Expand Up @@ -69,6 +71,18 @@ func (EnvironProvider) Schema() environschema.Fields {
return fields
}

// ProviderConfig returns extra config attributes specific
// to this provider only.
func (p EnvironProvider) ProviderConfig() schema.Fields {
return configFields
}

// ProviderConfigDefaults returns the default values for the
// provider specific config attributes.
func (p EnvironProvider) ProviderConfigDefaults() schema.Defaults {
return configDefaults
}

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 {
Expand Down
5 changes: 4 additions & 1 deletion provider/openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ type EnvironProvider struct {
FirewallerFactory FirewallerFactory
}

var _ environs.EnvironProvider = (*EnvironProvider)(nil)
var (
_ environs.EnvironProvider = (*EnvironProvider)(nil)
_ environs.ProviderSchema = (*EnvironProvider)(nil)
)

var providerInstance *EnvironProvider = &EnvironProvider{
OpenstackCredentials{},
Expand Down
4 changes: 4 additions & 0 deletions state/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,7 @@ func (internalStatePolicy) InstanceDistributor() (instance.Distributor, error) {
func (internalStatePolicy) StorageProviderRegistry() (storage.ProviderRegistry, error) {
return provider.CommonStorageProviders(), nil
}

func (internalStatePolicy) ProviderConfigSource() (config.ConfigSource, error) {
return nil, errors.NotImplementedf("ConfigSource")
}
27 changes: 26 additions & 1 deletion state/modelconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package state

import (
"github.com/juju/errors"
"github.com/juju/schema"

"github.com/juju/juju/controller"
"github.com/juju/juju/environs/config"
Expand Down Expand Up @@ -283,7 +284,7 @@ type modelConfigSource struct {
// overall config values, later values override earlier ones.
func modelConfigSources(st *State) []modelConfigSource {
return []modelConfigSource{
{config.JujuDefaultSource, func() (attrValues, error) { return config.ConfigDefaults(), nil }},
{config.JujuDefaultSource, st.defaultInheritedConfig},
{config.JujuControllerSource, st.controllerInheritedConfig},
{config.JujuControllerSource, st.regionInheritedConfig},
}
Expand All @@ -295,6 +296,30 @@ const (
controllerInheritedSettingsGlobalKey = "controller"
)

// defaultInheritedConfig returns config values which are defined
// as defaults in either Juju or the state's environ provider.
func (st *State) defaultInheritedConfig() (attrValues, error) {
var defaults = make(map[string]interface{})
for k, v := range config.ConfigDefaults() {
defaults[k] = v
}
providerDefaults, err := st.environsProviderConfigSource()
if errors.IsNotImplemented(err) {
return defaults, nil
} else if err != nil {
return nil, errors.Trace(err)
}
fields := schema.FieldMap(providerDefaults.ProviderConfig(), providerDefaults.ProviderConfigDefaults())
if coercedAtts, err := fields.Coerce(defaults, nil); err != nil {
return nil, errors.Trace(err)
} else {
for k, v := range coercedAtts.(map[string]interface{}) {
defaults[k] = v
}
}
return defaults, nil
}

// controllerInheritedConfig returns the inherited config values
// sourced from the local cloud config.
func (st *State) controllerInheritedConfig() (attrValues, error) {
Expand Down

0 comments on commit 0d95cef

Please sign in to comment.