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