github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/openstack/config_test.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 "os" 8 9 jc "github.com/juju/testing/checkers" 10 gc "gopkg.in/check.v1" 11 12 "github.com/juju/juju/cloud" 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/testing" 16 ) 17 18 type ConfigSuite struct { 19 testing.BaseSuite 20 savedVars map[string]string 21 } 22 23 // Ensure any environment variables a user may have set locally are reset. 24 var envVars = map[string]string{ 25 "AWS_SECRET_ACCESS_KEY": "", 26 "EC2_SECRET_KEYS": "", 27 "NOVA_API_KEY": "", 28 "NOVA_PASSWORD": "", 29 "NOVA_PROJECT_ID": "", 30 "NOVA_REGION": "", 31 "NOVA_USERNAME": "", 32 "OS_ACCESS_KEY": "", 33 "OS_AUTH_URL": "", 34 "OS_PASSWORD": "", 35 "OS_REGION_NAME": "", 36 "OS_SECRET_KEY": "", 37 "OS_TENANT_NAME": "", 38 "OS_USERNAME": "", 39 } 40 41 var _ = gc.Suite(&ConfigSuite{}) 42 43 // configTest specifies a config parsing test, checking that env when 44 // parsed as the openstack section of a config file matches 45 // baseConfigResult when mutated by the mutate function, or that the 46 // parse matches the given error. 47 type configTest struct { 48 summary string 49 config testing.Attrs 50 change map[string]interface{} 51 expect map[string]interface{} 52 envVars map[string]string 53 region string 54 useFloatingIP bool 55 useDefaultSecurityGroup bool 56 network string 57 username string 58 password string 59 tenantName string 60 authMode AuthMode 61 authURL string 62 accessKey string 63 secretKey string 64 firewallMode string 65 err string 66 sslHostnameVerification bool 67 sslHostnameSet bool 68 blockStorageSource string 69 } 70 71 var requiredConfig = testing.Attrs{ 72 "region": "configtest", 73 "auth-url": "http://auth", 74 "username": "user", 75 "password": "pass", 76 "tenant-name": "tenant", 77 } 78 79 func restoreEnvVars(envVars map[string]string) { 80 for k, v := range envVars { 81 os.Setenv(k, v) 82 } 83 } 84 85 func (t configTest) check(c *gc.C) { 86 attrs := testing.FakeConfig().Merge(testing.Attrs{ 87 "type": "openstack", 88 }).Merge(t.config) 89 90 cfg, err := config.New(config.NoDefaults, attrs) 91 c.Assert(err, jc.ErrorIsNil) 92 93 // Set environment variables if any. 94 savedVars := make(map[string]string) 95 if t.envVars != nil { 96 for k, v := range t.envVars { 97 savedVars[k] = os.Getenv(k) 98 os.Setenv(k, v) 99 } 100 } 101 defer restoreEnvVars(savedVars) 102 103 e, err := environs.New(cfg) 104 if t.change != nil { 105 c.Assert(err, jc.ErrorIsNil) 106 107 // Testing a change in configuration. 108 var old, changed, valid *config.Config 109 osenv := e.(*Environ) 110 old = osenv.ecfg().Config 111 changed, err = old.Apply(t.change) 112 c.Assert(err, jc.ErrorIsNil) 113 114 // Keep err for validation below. 115 valid, err = providerInstance.Validate(changed, old) 116 if err == nil { 117 err = osenv.SetConfig(valid) 118 } 119 } 120 if t.err != "" { 121 c.Check(err, gc.ErrorMatches, t.err) 122 return 123 } 124 c.Assert(err, jc.ErrorIsNil) 125 126 ecfg := e.(*Environ).ecfg() 127 c.Assert(ecfg.Name(), gc.Equals, "testenv") 128 if t.region != "" { 129 c.Assert(ecfg.region(), gc.Equals, t.region) 130 } 131 if t.authMode != "" { 132 c.Assert(ecfg.authMode(), gc.Equals, t.authMode) 133 } 134 if t.accessKey != "" { 135 c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey) 136 } 137 if t.secretKey != "" { 138 c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey) 139 } 140 if t.username != "" { 141 c.Assert(ecfg.username(), gc.Equals, t.username) 142 c.Assert(ecfg.password(), gc.Equals, t.password) 143 c.Assert(ecfg.tenantName(), gc.Equals, t.tenantName) 144 c.Assert(ecfg.authURL(), gc.Equals, t.authURL) 145 expected := map[string]string{ 146 "username": t.username, 147 "password": t.password, 148 "tenant-name": t.tenantName, 149 } 150 c.Assert(err, jc.ErrorIsNil) 151 actual, err := e.Provider().SecretAttrs(ecfg.Config) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(expected, gc.DeepEquals, actual) 154 } 155 if t.firewallMode != "" { 156 c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) 157 } 158 c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP) 159 c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup) 160 c.Assert(ecfg.network(), gc.Equals, t.network) 161 // Default should be true 162 expectedHostnameVerification := true 163 if t.sslHostnameSet { 164 expectedHostnameVerification = t.sslHostnameVerification 165 } 166 c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification) 167 for name, expect := range t.expect { 168 actual, found := ecfg.UnknownAttrs()[name] 169 c.Check(found, jc.IsTrue) 170 c.Check(actual, gc.Equals, expect) 171 } 172 if t.blockStorageSource != "" { 173 storage, ok := ecfg.StorageDefaultBlockSource() 174 c.Assert(ok, jc.IsTrue) 175 c.Assert(storage, gc.Equals, t.blockStorageSource) 176 } 177 } 178 179 func (s *ConfigSuite) SetUpTest(c *gc.C) { 180 s.BaseSuite.SetUpTest(c) 181 s.savedVars = make(map[string]string) 182 for v, val := range envVars { 183 s.savedVars[v] = os.Getenv(v) 184 os.Setenv(v, val) 185 } 186 s.PatchValue(&authenticateClient, func(*Environ) error { return nil }) 187 } 188 189 func (s *ConfigSuite) TearDownTest(c *gc.C) { 190 for k, v := range s.savedVars { 191 os.Setenv(k, v) 192 } 193 s.BaseSuite.TearDownTest(c) 194 } 195 196 var configTests = []configTest{ 197 { 198 summary: "setting region", 199 config: requiredConfig.Merge(testing.Attrs{ 200 "region": "testreg", 201 }), 202 region: "testreg", 203 }, { 204 summary: "setting region (2)", 205 config: requiredConfig.Merge(testing.Attrs{ 206 "region": "configtest", 207 }), 208 region: "configtest", 209 }, { 210 summary: "changing region", 211 config: requiredConfig, 212 change: testing.Attrs{ 213 "region": "otherregion", 214 }, 215 err: `cannot change region from "configtest" to "otherregion"`, 216 }, { 217 summary: "invalid region", 218 config: requiredConfig.Merge(testing.Attrs{ 219 "region": 666, 220 }), 221 err: `.*expected string, got int\(666\)`, 222 }, { 223 summary: "missing region in model", 224 config: requiredConfig.Delete("region"), 225 err: "missing region not valid", 226 }, { 227 summary: "invalid username", 228 config: requiredConfig.Merge(testing.Attrs{ 229 "username": 666, 230 }), 231 err: `.*expected string, got int\(666\)`, 232 }, { 233 summary: "missing username in model", 234 config: requiredConfig.Delete("username"), 235 err: "missing username not valid", 236 }, { 237 summary: "invalid password", 238 config: requiredConfig.Merge(testing.Attrs{ 239 "password": 666, 240 }), 241 err: `.*expected string, got int\(666\)`, 242 }, { 243 summary: "missing password in model", 244 config: requiredConfig.Delete("password"), 245 err: "missing password not valid", 246 }, { 247 summary: "invalid tenant-name", 248 config: requiredConfig.Merge(testing.Attrs{ 249 "tenant-name": 666, 250 }), 251 err: `.*expected string, got int\(666\)`, 252 }, { 253 summary: "missing tenant in model", 254 config: requiredConfig.Delete("tenant-name"), 255 err: "missing tenant-name not valid", 256 }, { 257 summary: "invalid auth-url type", 258 config: requiredConfig.Merge(testing.Attrs{ 259 "auth-url": 666, 260 }), 261 err: `.*expected string, got int\(666\)`, 262 }, { 263 summary: "missing auth-url in model", 264 config: requiredConfig.Delete("auth-url"), 265 err: "missing auth-url not valid", 266 }, { 267 summary: "invalid authorization mode", 268 config: requiredConfig.Merge(testing.Attrs{ 269 "auth-mode": "invalid-mode", 270 }), 271 err: `auth-mode: expected one of \[keypair legacy userpass\], got "invalid-mode"`, 272 }, { 273 summary: "keypair authorization mode", 274 config: requiredConfig.Merge(testing.Attrs{ 275 "auth-mode": "keypair", 276 "access-key": "MyAccessKey", 277 "secret-key": "MySecretKey", 278 }), 279 authMode: "keypair", 280 accessKey: "MyAccessKey", 281 secretKey: "MySecretKey", 282 }, { 283 summary: "keypair authorization mode without access key", 284 config: requiredConfig.Merge(testing.Attrs{ 285 "auth-mode": "keypair", 286 "secret-key": "MySecretKey", 287 }), 288 err: "missing access-key not valid", 289 }, { 290 summary: "keypair authorization mode without secret key", 291 config: requiredConfig.Merge(testing.Attrs{ 292 "auth-mode": "keypair", 293 "access-key": "MyAccessKey", 294 }), 295 err: "missing secret-key not valid", 296 }, { 297 summary: "invalid auth-url format", 298 config: requiredConfig.Merge(testing.Attrs{ 299 "auth-url": "invalid", 300 }), 301 err: `invalid auth-url value "invalid"`, 302 }, { 303 summary: "valid auth args", 304 config: requiredConfig.Merge(testing.Attrs{ 305 "username": "jujuer", 306 "password": "open sesame", 307 "tenant-name": "juju tenant", 308 "auth-mode": "legacy", 309 "auth-url": "http://some.url/v2", 310 }), 311 username: "jujuer", 312 password: "open sesame", 313 tenantName: "juju tenant", 314 authURL: "http://some.url/v2", 315 authMode: AuthLegacy, 316 }, { 317 summary: "default use floating ip", 318 config: requiredConfig, 319 // Do not use floating IP's by default. 320 useFloatingIP: false, 321 }, { 322 summary: "use floating ip", 323 config: requiredConfig.Merge(testing.Attrs{ 324 "use-floating-ip": true, 325 }), 326 useFloatingIP: true, 327 }, { 328 summary: "default use default security group", 329 config: requiredConfig, 330 // Do not use default security group by default. 331 useDefaultSecurityGroup: false, 332 }, { 333 summary: "use default security group", 334 config: requiredConfig.Merge(testing.Attrs{ 335 "use-default-secgroup": true, 336 }), 337 useDefaultSecurityGroup: true, 338 }, { 339 summary: "admin-secret given", 340 config: requiredConfig.Merge(testing.Attrs{ 341 "admin-secret": "Futumpsh", 342 }), 343 }, { 344 summary: "default firewall-mode", 345 config: requiredConfig, 346 firewallMode: config.FwInstance, 347 }, { 348 summary: "instance firewall-mode", 349 config: requiredConfig.Merge(testing.Attrs{ 350 "firewall-mode": "instance", 351 }), 352 firewallMode: config.FwInstance, 353 }, { 354 summary: "global firewall-mode", 355 config: requiredConfig.Merge(testing.Attrs{ 356 "firewall-mode": "global", 357 }), 358 firewallMode: config.FwGlobal, 359 }, { 360 summary: "none firewall-mode", 361 config: requiredConfig.Merge(testing.Attrs{ 362 "firewall-mode": "none", 363 }), 364 firewallMode: config.FwNone, 365 }, { 366 config: requiredConfig.Merge(testing.Attrs{ 367 "future": "hammerstein", 368 }), 369 expect: testing.Attrs{ 370 "future": "hammerstein", 371 }, 372 }, { 373 config: requiredConfig, 374 change: testing.Attrs{ 375 "future": "hammerstein", 376 }, 377 expect: testing.Attrs{ 378 "future": "hammerstein", 379 }, 380 }, { 381 config: requiredConfig, 382 change: testing.Attrs{ 383 "ssl-hostname-verification": false, 384 }, 385 sslHostnameVerification: false, 386 sslHostnameSet: true, 387 }, { 388 config: requiredConfig, 389 change: testing.Attrs{ 390 "ssl-hostname-verification": true, 391 }, 392 sslHostnameVerification: true, 393 sslHostnameSet: true, 394 }, { 395 summary: "default network", 396 config: requiredConfig, 397 network: "", 398 }, { 399 summary: "network", 400 config: requiredConfig.Merge(testing.Attrs{ 401 "network": "a-network-label", 402 }), 403 network: "a-network-label", 404 }, { 405 summary: "block storage specified", 406 config: requiredConfig.Merge(testing.Attrs{ 407 "storage-default-block-source": "my-cinder", 408 }), 409 blockStorageSource: "my-cinder", 410 }, 411 } 412 413 func (s *ConfigSuite) TestConfig(c *gc.C) { 414 for i, t := range configTests { 415 c.Logf("test %d: %s (%v)", i, t.summary, t.config) 416 t.check(c) 417 } 418 } 419 420 func (s *ConfigSuite) TestDeprecatedAttributesRemoved(c *gc.C) { 421 attrs := testing.FakeConfig().Merge(testing.Attrs{ 422 "type": "openstack", 423 "default-image-id": "id-1234", 424 "default-instance-type": "big", 425 "username": "u", 426 "password": "p", 427 "tenant-name": "t", 428 "region": "r", 429 "auth-url": "http://auth", 430 }) 431 432 cfg, err := config.New(config.NoDefaults, attrs) 433 c.Assert(err, jc.ErrorIsNil) 434 // Keep err for validation below. 435 valid, err := providerInstance.Validate(cfg, nil) 436 c.Assert(err, jc.ErrorIsNil) 437 // Check deprecated attributes removed. 438 allAttrs := valid.AllAttrs() 439 for _, attr := range []string{"default-image-id", "default-instance-type"} { 440 _, ok := allAttrs[attr] 441 c.Assert(ok, jc.IsFalse) 442 } 443 } 444 445 func (s *ConfigSuite) TestBootstrapConfigSetsDefaultBlockSource(c *gc.C) { 446 attrs := testing.FakeConfig().Merge(testing.Attrs{ 447 "type": "openstack", 448 }) 449 cfg, err := config.New(config.NoDefaults, attrs) 450 c.Assert(err, jc.ErrorIsNil) 451 _, ok := cfg.StorageDefaultBlockSource() 452 c.Assert(ok, jc.IsFalse) 453 454 cfg, err = providerInstance.BootstrapConfig(bootstrapConfigParams(cfg)) 455 c.Assert(err, jc.ErrorIsNil) 456 source, ok := cfg.StorageDefaultBlockSource() 457 c.Assert(ok, jc.IsTrue) 458 c.Assert(source, gc.Equals, "cinder") 459 } 460 461 func bootstrapConfigParams(cfg *config.Config) environs.BootstrapConfigParams { 462 return environs.BootstrapConfigParams{ 463 Config: cfg, 464 Credentials: cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 465 "username": "user", 466 "password": "secret", 467 "tenant-name": "sometenant", 468 }), 469 CloudRegion: "region", 470 CloudEndpoint: "http://auth", 471 } 472 } 473 474 func (*ConfigSuite) TestSchema(c *gc.C) { 475 fields := providerInstance.Schema() 476 // Check that all the fields defined in environs/config 477 // are in the returned schema. 478 globalFields, err := config.Schema(nil) 479 c.Assert(err, gc.IsNil) 480 for name, field := range globalFields { 481 c.Check(fields[name], jc.DeepEquals, field) 482 } 483 }