github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "gopkg.in/goose.v1/identity" 12 "gopkg.in/juju/environschema.v1" 13 14 "github.com/juju/juju/environs/config" 15 ) 16 17 var configSchema = environschema.Fields{ 18 "username": { 19 Description: "The user name to use when auth-mode is userpass.", 20 Type: environschema.Tstring, 21 EnvVars: identity.CredEnvUser, 22 Group: environschema.AccountGroup, 23 }, 24 "password": { 25 Description: "The password to use when auth-mode is userpass.", 26 Type: environschema.Tstring, 27 EnvVars: identity.CredEnvSecrets, 28 Group: environschema.AccountGroup, 29 Secret: true, 30 }, 31 "tenant-name": { 32 Description: "The openstack tenant name.", 33 Type: environschema.Tstring, 34 EnvVars: identity.CredEnvTenantName, 35 Group: environschema.AccountGroup, 36 }, 37 "auth-url": { 38 Description: "The keystone URL for authentication.", 39 Type: environschema.Tstring, 40 EnvVars: identity.CredEnvAuthURL, 41 Example: "https://yourkeystoneurl:443/v2.0/", 42 Group: environschema.AccountGroup, 43 }, 44 "auth-mode": { 45 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.", 46 Type: environschema.Tstring, 47 Values: []interface{}{AuthKeyPair, AuthLegacy, AuthUserPass}, 48 Group: environschema.AccountGroup, 49 }, 50 "access-key": { 51 Description: "The access key to use when auth-mode is set to keypair.", 52 Type: environschema.Tstring, 53 EnvVars: identity.CredEnvUser, 54 Group: environschema.AccountGroup, 55 Secret: true, 56 }, 57 "secret-key": { 58 Description: "The secret key to use when auth-mode is set to keypair.", 59 EnvVars: identity.CredEnvSecrets, 60 Group: environschema.AccountGroup, 61 Type: environschema.Tstring, 62 Secret: true, 63 }, 64 "region": { 65 Description: "The openstack region.", 66 Type: environschema.Tstring, 67 EnvVars: identity.CredEnvRegion, 68 }, 69 "control-bucket": { 70 Description: "The name to use for the control bucket (do not set unless you know what you are doing!).", 71 Type: environschema.Tstring, 72 }, 73 "use-floating-ip": { 74 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.", 75 Type: environschema.Tbool, 76 }, 77 "use-default-secgroup": { 78 Description: `Whether new machine instances should have the "default" Openstack security group assigned.`, 79 Type: environschema.Tbool, 80 }, 81 "network": { 82 Description: "The network label or UUID to bring machines up on when multiple networks exist.", 83 Type: environschema.Tstring, 84 }, 85 } 86 87 var configFields = func() schema.Fields { 88 fs, _, err := configSchema.ValidationSchema() 89 if err != nil { 90 panic(err) 91 } 92 return fs 93 }() 94 95 var configDefaults = schema.Defaults{ 96 "username": "", 97 "password": "", 98 "tenant-name": "", 99 "auth-url": "", 100 "auth-mode": string(AuthUserPass), 101 "access-key": "", 102 "secret-key": "", 103 "region": "", 104 "control-bucket": "", 105 "use-floating-ip": false, 106 "use-default-secgroup": false, 107 "network": "", 108 } 109 110 type environConfig struct { 111 *config.Config 112 attrs map[string]interface{} 113 } 114 115 func (c *environConfig) region() string { 116 return c.attrs["region"].(string) 117 } 118 119 func (c *environConfig) username() string { 120 return c.attrs["username"].(string) 121 } 122 123 func (c *environConfig) password() string { 124 return c.attrs["password"].(string) 125 } 126 127 func (c *environConfig) tenantName() string { 128 return c.attrs["tenant-name"].(string) 129 } 130 131 func (c *environConfig) authURL() string { 132 return c.attrs["auth-url"].(string) 133 } 134 135 func (c *environConfig) authMode() AuthMode { 136 return AuthMode(c.attrs["auth-mode"].(string)) 137 } 138 139 func (c *environConfig) accessKey() string { 140 return c.attrs["access-key"].(string) 141 } 142 143 func (c *environConfig) secretKey() string { 144 return c.attrs["secret-key"].(string) 145 } 146 147 func (c *environConfig) controlBucket() string { 148 return c.attrs["control-bucket"].(string) 149 } 150 151 func (c *environConfig) useFloatingIP() bool { 152 return c.attrs["use-floating-ip"].(bool) 153 } 154 155 func (c *environConfig) useDefaultSecurityGroup() bool { 156 return c.attrs["use-default-secgroup"].(bool) 157 } 158 159 func (c *environConfig) network() string { 160 return c.attrs["network"].(string) 161 } 162 163 func (p environProvider) newConfig(cfg *config.Config) (*environConfig, error) { 164 valid, err := p.Validate(cfg, nil) 165 if err != nil { 166 return nil, err 167 } 168 return &environConfig{valid, valid.UnknownAttrs()}, nil 169 } 170 171 type AuthMode string 172 173 const ( 174 AuthKeyPair AuthMode = "keypair" 175 AuthLegacy AuthMode = "legacy" 176 AuthUserPass AuthMode = "userpass" 177 ) 178 179 // Schema returns the configuration schema for an environment. 180 func (environProvider) Schema() environschema.Fields { 181 fields, err := config.Schema(configSchema) 182 if err != nil { 183 panic(err) 184 } 185 return fields 186 } 187 188 func (p environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 189 // Check for valid changes for the base config values. 190 if err := config.Validate(cfg, old); err != nil { 191 return nil, err 192 } 193 194 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 195 if err != nil { 196 return nil, err 197 } 198 199 // Add Openstack specific defaults. 200 providerDefaults := make(map[string]interface{}) 201 202 // Storage. 203 if _, ok := cfg.StorageDefaultBlockSource(); !ok { 204 providerDefaults[config.StorageDefaultBlockSourceKey] = CinderProviderType 205 } 206 if len(providerDefaults) > 0 { 207 if cfg, err = cfg.Apply(providerDefaults); err != nil { 208 return nil, err 209 } 210 } 211 212 ecfg := &environConfig{cfg, validated} 213 214 if ecfg.authURL() != "" { 215 parts, err := url.Parse(ecfg.authURL()) 216 if err != nil || parts.Host == "" || parts.Scheme == "" { 217 return nil, fmt.Errorf("invalid auth-url value %q", ecfg.authURL()) 218 } 219 } 220 cred := identity.CredentialsFromEnv() 221 format := "required environment variable not set for credentials attribute: %s" 222 switch ecfg.authMode() { 223 case AuthUserPass, AuthLegacy: 224 if ecfg.username() == "" { 225 if cred.User == "" { 226 return nil, fmt.Errorf(format, "User") 227 } 228 ecfg.attrs["username"] = cred.User 229 } 230 if ecfg.password() == "" { 231 if cred.Secrets == "" { 232 return nil, fmt.Errorf(format, "Secrets") 233 } 234 ecfg.attrs["password"] = cred.Secrets 235 } 236 case AuthKeyPair: 237 if ecfg.accessKey() == "" { 238 if cred.User == "" { 239 return nil, fmt.Errorf(format, "User") 240 } 241 ecfg.attrs["access-key"] = cred.User 242 } 243 if ecfg.secretKey() == "" { 244 if cred.Secrets == "" { 245 return nil, fmt.Errorf(format, "Secrets") 246 } 247 ecfg.attrs["secret-key"] = cred.Secrets 248 } 249 default: 250 return nil, fmt.Errorf("unexpected authentication mode %q", ecfg.authMode()) 251 } 252 if ecfg.authURL() == "" { 253 if cred.URL == "" { 254 return nil, fmt.Errorf(format, "URL") 255 } 256 ecfg.attrs["auth-url"] = cred.URL 257 } 258 if ecfg.tenantName() == "" { 259 if cred.TenantName == "" { 260 return nil, fmt.Errorf(format, "TenantName") 261 } 262 ecfg.attrs["tenant-name"] = cred.TenantName 263 } 264 if ecfg.region() == "" { 265 if cred.Region == "" { 266 return nil, fmt.Errorf(format, "Region") 267 } 268 ecfg.attrs["region"] = cred.Region 269 } 270 271 if old != nil { 272 attrs := old.UnknownAttrs() 273 if region, _ := attrs["region"].(string); ecfg.region() != region { 274 return nil, fmt.Errorf("cannot change region from %q to %q", region, ecfg.region()) 275 } 276 if controlBucket, _ := attrs["control-bucket"].(string); ecfg.controlBucket() != controlBucket { 277 return nil, fmt.Errorf("cannot change control-bucket from %q to %q", controlBucket, ecfg.controlBucket()) 278 } 279 } 280 281 // Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message 282 // even if they are not running with --debug. 283 cfgAttrs := cfg.AllAttrs() 284 if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" { 285 msg := fmt.Sprintf( 286 "Config attribute %q (%v) is deprecated and ignored.\n"+ 287 "Your cloud provider should have set up image metadata to provide the correct image id\n"+ 288 "for your chosen series and archietcure. If this is a private Openstack deployment without\n"+ 289 "existing image metadata, please run 'juju-metadata help' to see how suitable image"+ 290 "metadata can be generated.", 291 "default-image-id", defaultImageId) 292 logger.Warningf(msg) 293 } 294 if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" { 295 msg := fmt.Sprintf( 296 "Config attribute %q (%v) is deprecated and ignored.\n"+ 297 "The correct instance flavor is determined using constraints, globally specified\n"+ 298 "when an environment is bootstrapped, or individually when a charm is deployed.\n"+ 299 "See 'juju help bootstrap' or 'juju help deploy'.", 300 "default-instance-type", defaultInstanceType) 301 logger.Warningf(msg) 302 } 303 // Construct a new config with the deprecated attributes removed. 304 for _, attr := range []string{"default-image-id", "default-instance-type"} { 305 delete(cfgAttrs, attr) 306 delete(ecfg.attrs, attr) 307 } 308 for k, v := range ecfg.attrs { 309 cfgAttrs[k] = v 310 } 311 return config.New(config.NoDefaults, cfgAttrs) 312 }