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 }