github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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/schema"
    11  	"launchpad.net/goose/identity"
    12  
    13  	"github.com/juju/juju/environs/config"
    14  )
    15  
    16  var configFields = schema.Fields{
    17  	"username":             schema.String(),
    18  	"password":             schema.String(),
    19  	"tenant-name":          schema.String(),
    20  	"auth-url":             schema.String(),
    21  	"auth-mode":            schema.String(),
    22  	"access-key":           schema.String(),
    23  	"secret-key":           schema.String(),
    24  	"region":               schema.String(),
    25  	"control-bucket":       schema.String(),
    26  	"use-floating-ip":      schema.Bool(),
    27  	"use-default-secgroup": schema.Bool(),
    28  	"network":              schema.String(),
    29  }
    30  var configDefaults = schema.Defaults{
    31  	"username":             "",
    32  	"password":             "",
    33  	"tenant-name":          "",
    34  	"auth-url":             "",
    35  	"auth-mode":            string(AuthUserPass),
    36  	"access-key":           "",
    37  	"secret-key":           "",
    38  	"region":               "",
    39  	"control-bucket":       "",
    40  	"use-floating-ip":      false,
    41  	"use-default-secgroup": false,
    42  	"network":              "",
    43  }
    44  
    45  type environConfig struct {
    46  	*config.Config
    47  	attrs map[string]interface{}
    48  }
    49  
    50  func (c *environConfig) region() string {
    51  	return c.attrs["region"].(string)
    52  }
    53  
    54  func (c *environConfig) username() string {
    55  	return c.attrs["username"].(string)
    56  }
    57  
    58  func (c *environConfig) password() string {
    59  	return c.attrs["password"].(string)
    60  }
    61  
    62  func (c *environConfig) tenantName() string {
    63  	return c.attrs["tenant-name"].(string)
    64  }
    65  
    66  func (c *environConfig) authURL() string {
    67  	return c.attrs["auth-url"].(string)
    68  }
    69  
    70  func (c *environConfig) authMode() string {
    71  	return c.attrs["auth-mode"].(string)
    72  }
    73  
    74  func (c *environConfig) accessKey() string {
    75  	return c.attrs["access-key"].(string)
    76  }
    77  
    78  func (c *environConfig) secretKey() string {
    79  	return c.attrs["secret-key"].(string)
    80  }
    81  
    82  func (c *environConfig) controlBucket() string {
    83  	return c.attrs["control-bucket"].(string)
    84  }
    85  
    86  func (c *environConfig) useFloatingIP() bool {
    87  	return c.attrs["use-floating-ip"].(bool)
    88  }
    89  
    90  func (c *environConfig) useDefaultSecurityGroup() bool {
    91  	return c.attrs["use-default-secgroup"].(bool)
    92  }
    93  
    94  func (c *environConfig) network() string {
    95  	return c.attrs["network"].(string)
    96  }
    97  
    98  func (p environProvider) newConfig(cfg *config.Config) (*environConfig, error) {
    99  	valid, err := p.Validate(cfg, nil)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	return &environConfig{valid, valid.UnknownAttrs()}, nil
   104  }
   105  
   106  type AuthMode string
   107  
   108  const (
   109  	AuthKeyPair  AuthMode = "keypair"
   110  	AuthLegacy   AuthMode = "legacy"
   111  	AuthUserPass AuthMode = "userpass"
   112  )
   113  
   114  func (p environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   115  	// Check for valid changes for the base config values.
   116  	if err := config.Validate(cfg, old); err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	ecfg := &environConfig{cfg, validated}
   125  
   126  	authMode := AuthMode(ecfg.authMode())
   127  	switch authMode {
   128  	case AuthKeyPair:
   129  	case AuthLegacy:
   130  	case AuthUserPass:
   131  	default:
   132  		return nil, fmt.Errorf("invalid authorization mode: %q", authMode)
   133  	}
   134  
   135  	if ecfg.authURL() != "" {
   136  		parts, err := url.Parse(ecfg.authURL())
   137  		if err != nil || parts.Host == "" || parts.Scheme == "" {
   138  			return nil, fmt.Errorf("invalid auth-url value %q", ecfg.authURL())
   139  		}
   140  	}
   141  	cred := identity.CredentialsFromEnv()
   142  	format := "required environment variable not set for credentials attribute: %s"
   143  	if authMode == AuthUserPass || authMode == AuthLegacy {
   144  		if ecfg.username() == "" {
   145  			if cred.User == "" {
   146  				return nil, fmt.Errorf(format, "User")
   147  			}
   148  			ecfg.attrs["username"] = cred.User
   149  		}
   150  		if ecfg.password() == "" {
   151  			if cred.Secrets == "" {
   152  				return nil, fmt.Errorf(format, "Secrets")
   153  			}
   154  			ecfg.attrs["password"] = cred.Secrets
   155  		}
   156  	} else if authMode == AuthKeyPair {
   157  		if ecfg.accessKey() == "" {
   158  			if cred.User == "" {
   159  				return nil, fmt.Errorf(format, "User")
   160  			}
   161  			ecfg.attrs["access-key"] = cred.User
   162  		}
   163  		if ecfg.secretKey() == "" {
   164  			if cred.Secrets == "" {
   165  				return nil, fmt.Errorf(format, "Secrets")
   166  			}
   167  			ecfg.attrs["secret-key"] = cred.Secrets
   168  		}
   169  	}
   170  	if ecfg.authURL() == "" {
   171  		if cred.URL == "" {
   172  			return nil, fmt.Errorf(format, "URL")
   173  		}
   174  		ecfg.attrs["auth-url"] = cred.URL
   175  	}
   176  	if ecfg.tenantName() == "" {
   177  		if cred.TenantName == "" {
   178  			return nil, fmt.Errorf(format, "TenantName")
   179  		}
   180  		ecfg.attrs["tenant-name"] = cred.TenantName
   181  	}
   182  	if ecfg.region() == "" {
   183  		if cred.Region == "" {
   184  			return nil, fmt.Errorf(format, "Region")
   185  		}
   186  		ecfg.attrs["region"] = cred.Region
   187  	}
   188  
   189  	if old != nil {
   190  		attrs := old.UnknownAttrs()
   191  		if region, _ := attrs["region"].(string); ecfg.region() != region {
   192  			return nil, fmt.Errorf("cannot change region from %q to %q", region, ecfg.region())
   193  		}
   194  		if controlBucket, _ := attrs["control-bucket"].(string); ecfg.controlBucket() != controlBucket {
   195  			return nil, fmt.Errorf("cannot change control-bucket from %q to %q", controlBucket, ecfg.controlBucket())
   196  		}
   197  	}
   198  
   199  	// Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message
   200  	// even if they are not running with --debug.
   201  	cfgAttrs := cfg.AllAttrs()
   202  	if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" {
   203  		msg := fmt.Sprintf(
   204  			"Config attribute %q (%v) is deprecated and ignored.\n"+
   205  				"Your cloud provider should have set up image metadata to provide the correct image id\n"+
   206  				"for your chosen series and archietcure. If this is a private Openstack deployment without\n"+
   207  				"existing image metadata, please run 'juju-metadata help' to see how suitable image"+
   208  				"metadata can be generated.",
   209  			"default-image-id", defaultImageId)
   210  		logger.Warningf(msg)
   211  	}
   212  	if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" {
   213  		msg := fmt.Sprintf(
   214  			"Config attribute %q (%v) is deprecated and ignored.\n"+
   215  				"The correct instance flavor is determined using constraints, globally specified\n"+
   216  				"when an environment is bootstrapped, or individually when a charm is deployed.\n"+
   217  				"See 'juju help bootstrap' or 'juju help deploy'.",
   218  			"default-instance-type", defaultInstanceType)
   219  		logger.Warningf(msg)
   220  	}
   221  	// Construct a new config with the deprecated attributes removed.
   222  	for _, attr := range []string{"default-image-id", "default-instance-type"} {
   223  		delete(cfgAttrs, attr)
   224  		delete(ecfg.attrs, attr)
   225  	}
   226  	for k, v := range ecfg.attrs {
   227  		cfgAttrs[k] = v
   228  	}
   229  	return config.New(config.NoDefaults, cfgAttrs)
   230  }