github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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/environs" 13 "github.com/juju/juju/environs/config" 14 envtesting "github.com/juju/juju/environs/testing" 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 map[string]interface{} 50 change map[string]interface{} 51 expect map[string]interface{} 52 envVars map[string]string 53 region string 54 controlBucket string 55 useFloatingIP bool 56 useDefaultSecurityGroup bool 57 network string 58 username string 59 password string 60 tenantName string 61 authMode AuthMode 62 authURL string 63 accessKey string 64 secretKey string 65 firewallMode string 66 err string 67 sslHostnameVerification bool 68 sslHostnameSet bool 69 blockStorageSource string 70 } 71 72 type attrs map[string]interface{} 73 74 func restoreEnvVars(envVars map[string]string) { 75 for k, v := range envVars { 76 os.Setenv(k, v) 77 } 78 } 79 80 func (t configTest) check(c *gc.C) { 81 attrs := testing.FakeConfig().Merge(testing.Attrs{ 82 "type": "openstack", 83 "control-bucket": "x", 84 }).Merge(t.config) 85 86 cfg, err := config.New(config.NoDefaults, attrs) 87 c.Assert(err, jc.ErrorIsNil) 88 89 // Set environment variables if any. 90 savedVars := make(map[string]string) 91 if t.envVars != nil { 92 for k, v := range t.envVars { 93 savedVars[k] = os.Getenv(k) 94 os.Setenv(k, v) 95 } 96 } 97 defer restoreEnvVars(savedVars) 98 99 e, err := environs.New(cfg) 100 if t.change != nil { 101 c.Assert(err, jc.ErrorIsNil) 102 103 // Testing a change in configuration. 104 var old, changed, valid *config.Config 105 osenv := e.(*environ) 106 old = osenv.ecfg().Config 107 changed, err = old.Apply(t.change) 108 c.Assert(err, jc.ErrorIsNil) 109 110 // Keep err for validation below. 111 valid, err = providerInstance.Validate(changed, old) 112 if err == nil { 113 err = osenv.SetConfig(valid) 114 } 115 } 116 if t.err != "" { 117 c.Check(err, gc.ErrorMatches, t.err) 118 return 119 } 120 c.Assert(err, jc.ErrorIsNil) 121 122 ecfg := e.(*environ).ecfg() 123 c.Assert(ecfg.Name(), gc.Equals, "testenv") 124 c.Assert(ecfg.controlBucket(), gc.Equals, "x") 125 if t.region != "" { 126 c.Assert(ecfg.region(), gc.Equals, t.region) 127 } 128 if t.authMode != "" { 129 c.Assert(ecfg.authMode(), gc.Equals, t.authMode) 130 } 131 if t.accessKey != "" { 132 c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey) 133 } 134 if t.secretKey != "" { 135 c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey) 136 } 137 if t.username != "" { 138 c.Assert(ecfg.username(), gc.Equals, t.username) 139 c.Assert(ecfg.password(), gc.Equals, t.password) 140 c.Assert(ecfg.tenantName(), gc.Equals, t.tenantName) 141 c.Assert(ecfg.authURL(), gc.Equals, t.authURL) 142 expected := map[string]string{ 143 "username": t.username, 144 "password": t.password, 145 "tenant-name": t.tenantName, 146 } 147 c.Assert(err, jc.ErrorIsNil) 148 actual, err := e.Provider().SecretAttrs(ecfg.Config) 149 c.Assert(err, jc.ErrorIsNil) 150 c.Assert(expected, gc.DeepEquals, actual) 151 } 152 if t.firewallMode != "" { 153 c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) 154 } 155 c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP) 156 c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup) 157 c.Assert(ecfg.network(), gc.Equals, t.network) 158 // Default should be true 159 expectedHostnameVerification := true 160 if t.sslHostnameSet { 161 expectedHostnameVerification = t.sslHostnameVerification 162 } 163 c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification) 164 for name, expect := range t.expect { 165 actual, found := ecfg.UnknownAttrs()[name] 166 c.Check(found, jc.IsTrue) 167 c.Check(actual, gc.Equals, expect) 168 } 169 expectedStorage := "cinder" 170 if t.blockStorageSource != "" { 171 expectedStorage = t.blockStorageSource 172 } 173 storage, ok := ecfg.StorageDefaultBlockSource() 174 c.Assert(ok, jc.IsTrue) 175 c.Assert(storage, gc.Equals, expectedStorage) 176 } 177 178 func (s *ConfigSuite) SetUpTest(c *gc.C) { 179 s.BaseSuite.SetUpTest(c) 180 s.savedVars = make(map[string]string) 181 for v, val := range envVars { 182 s.savedVars[v] = os.Getenv(v) 183 os.Setenv(v, val) 184 } 185 s.PatchValue(&authenticateClient, func(*environ) error { return nil }) 186 } 187 188 func (s *ConfigSuite) TearDownTest(c *gc.C) { 189 for k, v := range s.savedVars { 190 os.Setenv(k, v) 191 } 192 s.BaseSuite.TearDownTest(c) 193 } 194 195 var configTests = []configTest{ 196 { 197 summary: "setting region", 198 config: attrs{ 199 "region": "testreg", 200 }, 201 region: "testreg", 202 }, { 203 summary: "setting region (2)", 204 config: attrs{ 205 "region": "configtest", 206 }, 207 region: "configtest", 208 }, { 209 summary: "changing region", 210 config: attrs{ 211 "region": "configtest", 212 }, 213 change: attrs{ 214 "region": "somereg", 215 }, 216 err: `cannot change region from "configtest" to "somereg"`, 217 }, { 218 summary: "invalid region", 219 config: attrs{ 220 "region": 666, 221 }, 222 err: `.*expected string, got int\(666\)`, 223 }, { 224 summary: "missing region in environment", 225 envVars: map[string]string{ 226 "OS_REGION_NAME": "", 227 "NOVA_REGION": "", 228 }, 229 err: "required environment variable not set for credentials attribute: Region", 230 }, { 231 summary: "invalid username", 232 config: attrs{ 233 "username": 666, 234 }, 235 err: `.*expected string, got int\(666\)`, 236 }, { 237 summary: "missing username in environment", 238 err: "required environment variable not set for credentials attribute: User", 239 envVars: map[string]string{ 240 "OS_USERNAME": "", 241 "NOVA_USERNAME": "", 242 }, 243 }, { 244 summary: "invalid password", 245 config: attrs{ 246 "password": 666, 247 }, 248 err: `.*expected string, got int\(666\)`, 249 }, { 250 summary: "missing password in environment", 251 err: "required environment variable not set for credentials attribute: Secrets", 252 envVars: map[string]string{ 253 "OS_PASSWORD": "", 254 "NOVA_PASSWORD": "", 255 }, 256 }, { 257 summary: "invalid tenant-name", 258 config: attrs{ 259 "tenant-name": 666, 260 }, 261 err: `.*expected string, got int\(666\)`, 262 }, { 263 summary: "missing tenant in environment", 264 err: "required environment variable not set for credentials attribute: TenantName", 265 envVars: map[string]string{ 266 "OS_TENANT_NAME": "", 267 "NOVA_PROJECT_ID": "", 268 }, 269 }, { 270 summary: "invalid auth-url type", 271 config: attrs{ 272 "auth-url": 666, 273 }, 274 err: `.*expected string, got int\(666\)`, 275 }, { 276 summary: "missing auth-url in environment", 277 err: "required environment variable not set for credentials attribute: URL", 278 envVars: map[string]string{ 279 "OS_AUTH_URL": "", 280 }, 281 }, { 282 summary: "invalid authorization mode", 283 config: attrs{ 284 "auth-mode": "invalid-mode", 285 }, 286 err: `auth-mode: expected one of \[keypair legacy userpass\], got "invalid-mode"`, 287 }, { 288 summary: "keypair authorization mode", 289 config: attrs{ 290 "auth-mode": "keypair", 291 "access-key": "MyAccessKey", 292 "secret-key": "MySecretKey", 293 }, 294 authMode: "keypair", 295 accessKey: "MyAccessKey", 296 secretKey: "MySecretKey", 297 }, { 298 summary: "keypair authorization mode without access key", 299 config: attrs{ 300 "auth-mode": "keypair", 301 "secret-key": "MySecretKey", 302 }, 303 envVars: map[string]string{ 304 "OS_USERNAME": "", 305 }, 306 err: "required environment variable not set for credentials attribute: User", 307 }, { 308 summary: "keypair authorization mode without secret key", 309 config: attrs{ 310 "auth-mode": "keypair", 311 "access-key": "MyAccessKey", 312 }, 313 envVars: map[string]string{ 314 "OS_PASSWORD": "", 315 }, 316 err: "required environment variable not set for credentials attribute: Secrets", 317 }, { 318 summary: "invalid auth-url format", 319 config: attrs{ 320 "auth-url": "invalid", 321 }, 322 err: `invalid auth-url value "invalid"`, 323 }, { 324 summary: "invalid control-bucket", 325 config: attrs{ 326 "control-bucket": 666, 327 }, 328 err: `.*expected string, got int\(666\)`, 329 }, { 330 summary: "changing control-bucket", 331 change: attrs{ 332 "control-bucket": "new-x", 333 }, 334 err: `cannot change control-bucket from "x" to "new-x"`, 335 }, { 336 summary: "valid auth args", 337 config: attrs{ 338 "username": "jujuer", 339 "password": "open sesame", 340 "tenant-name": "juju tenant", 341 "auth-mode": "legacy", 342 "auth-url": "http://some/url", 343 }, 344 username: "jujuer", 345 password: "open sesame", 346 tenantName: "juju tenant", 347 authURL: "http://some/url", 348 authMode: AuthLegacy, 349 }, { 350 summary: "valid auth args in environment", 351 envVars: map[string]string{ 352 "OS_USERNAME": "jujuer", 353 "OS_PASSWORD": "open sesame", 354 "OS_AUTH_URL": "http://some/url", 355 "OS_TENANT_NAME": "juju tenant", 356 "OS_REGION_NAME": "region", 357 }, 358 username: "jujuer", 359 password: "open sesame", 360 tenantName: "juju tenant", 361 authURL: "http://some/url", 362 region: "region", 363 }, { 364 summary: "default auth mode based on environment", 365 authMode: AuthUserPass, 366 }, { 367 summary: "default use floating ip", 368 // Do not use floating IP's by default. 369 useFloatingIP: false, 370 }, { 371 summary: "use floating ip", 372 config: attrs{ 373 "use-floating-ip": true, 374 }, 375 useFloatingIP: true, 376 }, { 377 summary: "default use default security group", 378 // Do not use default security group by default. 379 useDefaultSecurityGroup: false, 380 }, { 381 summary: "use default security group", 382 config: attrs{ 383 "use-default-secgroup": true, 384 }, 385 useDefaultSecurityGroup: true, 386 }, { 387 summary: "admin-secret given", 388 config: attrs{ 389 "admin-secret": "Futumpsh", 390 }, 391 }, { 392 summary: "default firewall-mode", 393 config: attrs{}, 394 firewallMode: config.FwInstance, 395 }, { 396 summary: "instance firewall-mode", 397 config: attrs{ 398 "firewall-mode": "instance", 399 }, 400 firewallMode: config.FwInstance, 401 }, { 402 summary: "global firewall-mode", 403 config: attrs{ 404 "firewall-mode": "global", 405 }, 406 firewallMode: config.FwGlobal, 407 }, { 408 summary: "none firewall-mode", 409 config: attrs{ 410 "firewall-mode": "none", 411 }, 412 firewallMode: config.FwNone, 413 }, { 414 config: attrs{ 415 "future": "hammerstein", 416 }, 417 expect: attrs{ 418 "future": "hammerstein", 419 }, 420 }, { 421 change: attrs{ 422 "future": "hammerstein", 423 }, 424 expect: attrs{ 425 "future": "hammerstein", 426 }, 427 }, { 428 change: attrs{ 429 "ssl-hostname-verification": false, 430 }, 431 sslHostnameVerification: false, 432 sslHostnameSet: true, 433 }, { 434 change: attrs{ 435 "ssl-hostname-verification": true, 436 }, 437 sslHostnameVerification: true, 438 sslHostnameSet: true, 439 }, { 440 summary: "default network", 441 network: "", 442 }, { 443 summary: "network", 444 config: attrs{ 445 "network": "a-network-label", 446 }, 447 network: "a-network-label", 448 }, { 449 summary: "no default block storage specified", 450 config: attrs{}, 451 blockStorageSource: "cinder", 452 }, { 453 summary: "block storage specified", 454 config: attrs{ 455 "storage-default-block-source": "my-cinder", 456 }, 457 blockStorageSource: "my-cinder", 458 }, 459 } 460 461 func (s *ConfigSuite) TestConfig(c *gc.C) { 462 s.setupEnvCredentials() 463 for i, t := range configTests { 464 c.Logf("test %d: %s (%v)", i, t.summary, t.config) 465 t.check(c) 466 } 467 } 468 469 func (s *ConfigSuite) TestDeprecatedAttributesRemoved(c *gc.C) { 470 s.setupEnvCredentials() 471 attrs := testing.FakeConfig().Merge(testing.Attrs{ 472 "type": "openstack", 473 "control-bucket": "x", 474 "default-image-id": "id-1234", 475 "default-instance-type": "big", 476 }) 477 478 cfg, err := config.New(config.NoDefaults, attrs) 479 c.Assert(err, jc.ErrorIsNil) 480 // Keep err for validation below. 481 valid, err := providerInstance.Validate(cfg, nil) 482 c.Assert(err, jc.ErrorIsNil) 483 // Check deprecated attributes removed. 484 allAttrs := valid.AllAttrs() 485 for _, attr := range []string{"default-image-id", "default-instance-type"} { 486 _, ok := allAttrs[attr] 487 c.Assert(ok, jc.IsFalse) 488 } 489 } 490 491 func (s *ConfigSuite) TestPrepareInsertsUniqueControlBucket(c *gc.C) { 492 s.setupEnvCredentials() 493 attrs := testing.FakeConfig().Merge(testing.Attrs{ 494 "type": "openstack", 495 }) 496 cfg, err := config.New(config.NoDefaults, attrs) 497 c.Assert(err, jc.ErrorIsNil) 498 499 ctx := envtesting.BootstrapContext(c) 500 env0, err := providerInstance.PrepareForBootstrap(ctx, cfg) 501 c.Assert(err, jc.ErrorIsNil) 502 bucket0 := env0.(*environ).ecfg().controlBucket() 503 c.Assert(bucket0, gc.Matches, "[a-f0-9]{32}") 504 505 env1, err := providerInstance.PrepareForBootstrap(ctx, cfg) 506 c.Assert(err, jc.ErrorIsNil) 507 bucket1 := env1.(*environ).ecfg().controlBucket() 508 c.Assert(bucket1, gc.Matches, "[a-f0-9]{32}") 509 510 c.Assert(bucket1, gc.Not(gc.Equals), bucket0) 511 } 512 513 func (s *ConfigSuite) TestPrepareDoesNotTouchExistingControlBucket(c *gc.C) { 514 s.setupEnvCredentials() 515 attrs := testing.FakeConfig().Merge(testing.Attrs{ 516 "type": "openstack", 517 "control-bucket": "burblefoo", 518 }) 519 cfg, err := config.New(config.NoDefaults, attrs) 520 c.Assert(err, jc.ErrorIsNil) 521 522 env, err := providerInstance.PrepareForBootstrap(envtesting.BootstrapContext(c), cfg) 523 c.Assert(err, jc.ErrorIsNil) 524 bucket := env.(*environ).ecfg().controlBucket() 525 c.Assert(bucket, gc.Equals, "burblefoo") 526 } 527 528 func (s *ConfigSuite) TestPrepareSetsDefaultBlockSource(c *gc.C) { 529 s.setupEnvCredentials() 530 attrs := testing.FakeConfig().Merge(testing.Attrs{ 531 "type": "openstack", 532 }) 533 cfg, err := config.New(config.NoDefaults, attrs) 534 c.Assert(err, jc.ErrorIsNil) 535 536 env, err := providerInstance.PrepareForBootstrap(envtesting.BootstrapContext(c), cfg) 537 c.Assert(err, jc.ErrorIsNil) 538 source, ok := env.(*environ).ecfg().StorageDefaultBlockSource() 539 c.Assert(ok, jc.IsTrue) 540 c.Assert(source, gc.Equals, "cinder") 541 } 542 543 func (s *ConfigSuite) setupEnvCredentials() { 544 os.Setenv("OS_USERNAME", "user") 545 os.Setenv("OS_PASSWORD", "secret") 546 os.Setenv("OS_AUTH_URL", "http://auth") 547 os.Setenv("OS_TENANT_NAME", "sometenant") 548 os.Setenv("OS_REGION_NAME", "region") 549 } 550 551 func (*ConfigSuite) TestSchema(c *gc.C) { 552 fields := providerInstance.Schema() 553 // Check that all the fields defined in environs/config 554 // are in the returned schema. 555 globalFields, err := config.Schema(nil) 556 c.Assert(err, gc.IsNil) 557 for name, field := range globalFields { 558 c.Check(fields[name], jc.DeepEquals, field) 559 } 560 }