github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 }