github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/openstack/config.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package openstack
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/schema"
    12  	"gopkg.in/goose.v1/identity"
    13  	"gopkg.in/juju/environschema.v1"
    14  
    15  	"github.com/juju/juju/environs/config"
    16  )
    17  
    18  var configSchema = environschema.Fields{
    19  	"username": {
    20  		Description: "The user name  to use when auth-mode is userpass.",
    21  		Type:        environschema.Tstring,
    22  		EnvVars:     identity.CredEnvUser,
    23  		Group:       environschema.AccountGroup,
    24  	},
    25  	"password": {
    26  		Description: "The password to use when auth-mode is userpass.",
    27  		Type:        environschema.Tstring,
    28  		EnvVars:     identity.CredEnvSecrets,
    29  		Group:       environschema.AccountGroup,
    30  		Secret:      true,
    31  	},
    32  	"tenant-name": {
    33  		Description: "The openstack tenant name.",
    34  		Type:        environschema.Tstring,
    35  		EnvVars:     identity.CredEnvTenantName,
    36  		Group:       environschema.AccountGroup,
    37  	},
    38  	"auth-url": {
    39  		Description: "The keystone URL for authentication.",
    40  		Type:        environschema.Tstring,
    41  		EnvVars:     identity.CredEnvAuthURL,
    42  		Example:     "https://yourkeystoneurl:443/v2.0/",
    43  		Group:       environschema.AccountGroup,
    44  	},
    45  	"auth-mode": {
    46  		Description: "The authentication mode to use. When set to keypair, the access-key and secret-key parameters should be set; when set to userpass or legacy, the username and password parameters should be set.",
    47  		Type:        environschema.Tstring,
    48  		Values:      []interface{}{AuthKeyPair, AuthLegacy, AuthUserPass},
    49  		Group:       environschema.AccountGroup,
    50  	},
    51  	"access-key": {
    52  		Description: "The access key to use when auth-mode is set to keypair.",
    53  		Type:        environschema.Tstring,
    54  		EnvVars:     identity.CredEnvUser,
    55  		Group:       environschema.AccountGroup,
    56  		Secret:      true,
    57  	},
    58  	"secret-key": {
    59  		Description: "The secret key to use when auth-mode is set to keypair.",
    60  		EnvVars:     identity.CredEnvSecrets,
    61  		Group:       environschema.AccountGroup,
    62  		Type:        environschema.Tstring,
    63  		Secret:      true,
    64  	},
    65  	"region": {
    66  		Description: "The openstack region.",
    67  		Type:        environschema.Tstring,
    68  		EnvVars:     identity.CredEnvRegion,
    69  	},
    70  	"use-floating-ip": {
    71  		Description: "Whether a floating IP address is required to give the nodes a public IP address. Some installations assign public IP addresses by default without requiring a floating IP address.",
    72  		Type:        environschema.Tbool,
    73  	},
    74  	"use-default-secgroup": {
    75  		Description: `Whether new machine instances should have the "default" Openstack security group assigned.`,
    76  		Type:        environschema.Tbool,
    77  	},
    78  	"network": {
    79  		Description: "The network label or UUID to bring machines up on when multiple networks exist.",
    80  		Type:        environschema.Tstring,
    81  	},
    82  }
    83  
    84  var configFields = func() schema.Fields {
    85  	fs, _, err := configSchema.ValidationSchema()
    86  	if err != nil {
    87  		panic(err)
    88  	}
    89  	return fs
    90  }()
    91  
    92  type environConfig struct {
    93  	*config.Config
    94  	attrs map[string]interface{}
    95  }
    96  
    97  func (c *environConfig) region() string {
    98  	return c.attrs["region"].(string)
    99  }
   100  
   101  func (c *environConfig) username() string {
   102  	return c.attrs["username"].(string)
   103  }
   104  
   105  func (c *environConfig) password() string {
   106  	return c.attrs["password"].(string)
   107  }
   108  
   109  func (c *environConfig) tenantName() string {
   110  	return c.attrs["tenant-name"].(string)
   111  }
   112  
   113  func (c *environConfig) domainName() string {
   114  	dname, ok := c.attrs["domain-name"]
   115  	if ok {
   116  		return dname.(string)
   117  	}
   118  	return ""
   119  }
   120  
   121  func (c *environConfig) authURL() string {
   122  	return c.attrs["auth-url"].(string)
   123  }
   124  
   125  func (c *environConfig) authMode() AuthMode {
   126  	return AuthMode(c.attrs["auth-mode"].(string))
   127  }
   128  
   129  func (c *environConfig) accessKey() string {
   130  	return c.attrs["access-key"].(string)
   131  }
   132  
   133  func (c *environConfig) secretKey() string {
   134  	return c.attrs["secret-key"].(string)
   135  }
   136  
   137  func (c *environConfig) useFloatingIP() bool {
   138  	return c.attrs["use-floating-ip"].(bool)
   139  }
   140  
   141  func (c *environConfig) useDefaultSecurityGroup() bool {
   142  	return c.attrs["use-default-secgroup"].(bool)
   143  }
   144  
   145  func (c *environConfig) network() string {
   146  	return c.attrs["network"].(string)
   147  }
   148  
   149  type AuthMode string
   150  
   151  const (
   152  	AuthKeyPair  AuthMode = "keypair"
   153  	AuthLegacy   AuthMode = "legacy"
   154  	AuthUserPass AuthMode = "userpass"
   155  )
   156  
   157  // Schema returns the configuration schema for an environment.
   158  func (EnvironProvider) Schema() environschema.Fields {
   159  	fields, err := config.Schema(configSchema)
   160  	if err != nil {
   161  		panic(err)
   162  	}
   163  	return fields
   164  }
   165  
   166  func (p EnvironProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   167  	// Check for valid changes for the base config values.
   168  	if err := config.Validate(cfg, old); err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	validated, err := cfg.ValidateUnknownAttrs(configFields, p.Configurator.GetConfigDefaults())
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	ecfg := &environConfig{cfg, validated}
   177  
   178  	switch ecfg.authMode() {
   179  	case AuthUserPass, AuthLegacy:
   180  		if ecfg.username() == "" {
   181  			return nil, errors.NotValidf("missing username")
   182  		}
   183  		if ecfg.password() == "" {
   184  			return nil, errors.NotValidf("missing password")
   185  		}
   186  	case AuthKeyPair:
   187  		if ecfg.accessKey() == "" {
   188  			return nil, errors.NotValidf("missing access-key")
   189  		}
   190  		if ecfg.secretKey() == "" {
   191  			return nil, errors.NotValidf("missing secret-key")
   192  		}
   193  	default:
   194  		return nil, fmt.Errorf("unexpected authentication mode %q", ecfg.authMode())
   195  	}
   196  
   197  	if ecfg.authURL() == "" {
   198  		return nil, errors.NotValidf("missing auth-url")
   199  	}
   200  	if ecfg.tenantName() == "" {
   201  		return nil, errors.NotValidf("missing tenant-name")
   202  	}
   203  	if ecfg.region() == "" {
   204  		return nil, errors.NotValidf("missing region")
   205  	}
   206  
   207  	parts, err := url.Parse(ecfg.authURL())
   208  	if err != nil || parts.Host == "" || parts.Scheme == "" {
   209  		return nil, fmt.Errorf("invalid auth-url value %q", ecfg.authURL())
   210  	}
   211  
   212  	if old != nil {
   213  		attrs := old.UnknownAttrs()
   214  		if region, _ := attrs["region"].(string); ecfg.region() != region {
   215  			return nil, fmt.Errorf("cannot change region from %q to %q", region, ecfg.region())
   216  		}
   217  	}
   218  
   219  	// Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message
   220  	// even if they are not running with --debug.
   221  	cfgAttrs := cfg.AllAttrs()
   222  	if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" {
   223  		msg := fmt.Sprintf(
   224  			"Config attribute %q (%v) is deprecated and ignored.\n"+
   225  				"Your cloud provider should have set up image metadata to provide the correct image id\n"+
   226  				"for your chosen series and archietcure. If this is a private Openstack deployment without\n"+
   227  				"existing image metadata, please run 'juju-metadata help' to see how suitable image"+
   228  				"metadata can be generated.",
   229  			"default-image-id", defaultImageId)
   230  		logger.Warningf(msg)
   231  	}
   232  	if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" {
   233  		msg := fmt.Sprintf(
   234  			"Config attribute %q (%v) is deprecated and ignored.\n"+
   235  				"The correct instance flavor is determined using constraints, globally specified\n"+
   236  				"when an model is bootstrapped, or individually when a charm is deployed.\n"+
   237  				"See 'juju help bootstrap' or 'juju help deploy'.",
   238  			"default-instance-type", defaultInstanceType)
   239  		logger.Warningf(msg)
   240  	}
   241  	// Construct a new config with the deprecated attributes removed.
   242  	for _, attr := range []string{"default-image-id", "default-instance-type"} {
   243  		delete(cfgAttrs, attr)
   244  		delete(ecfg.attrs, attr)
   245  	}
   246  	for k, v := range ecfg.attrs {
   247  		cfgAttrs[k] = v
   248  	}
   249  	return config.New(config.NoDefaults, cfgAttrs)
   250  }