launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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 "time" 9 10 "github.com/loggo/loggo" 11 gc "launchpad.net/gocheck" 12 13 "launchpad.net/juju-core/environs" 14 "launchpad.net/juju-core/environs/config" 15 "launchpad.net/juju-core/testing" 16 jc "launchpad.net/juju-core/testing/checkers" 17 "launchpad.net/juju-core/testing/testbase" 18 ) 19 20 type ConfigSuite struct { 21 testbase.LoggingSuite 22 savedVars map[string]string 23 oldJujuHome *testing.FakeHome 24 } 25 26 // Ensure any environment variables a user may have set locally are reset. 27 var envVars = map[string]string{ 28 "AWS_SECRET_ACCESS_KEY": "", 29 "EC2_SECRET_KEYS": "", 30 "NOVA_API_KEY": "", 31 "NOVA_PASSWORD": "", 32 "NOVA_PROJECT_ID": "", 33 "NOVA_REGION": "", 34 "NOVA_USERNAME": "", 35 "OS_ACCESS_KEY": "", 36 "OS_AUTH_URL": "", 37 "OS_PASSWORD": "", 38 "OS_REGION_NAME": "", 39 "OS_SECRET_KEY": "", 40 "OS_TENANT_NAME": "", 41 "OS_USERNAME": "", 42 } 43 44 var _ = gc.Suite(&ConfigSuite{}) 45 46 // configTest specifies a config parsing test, checking that env when 47 // parsed as the openstack section of a config file matches 48 // baseConfigResult when mutated by the mutate function, or that the 49 // parse matches the given error. 50 type configTest struct { 51 summary string 52 config map[string]interface{} 53 change map[string]interface{} 54 expect map[string]interface{} 55 envVars map[string]string 56 region string 57 controlBucket string 58 toolsURL string 59 useFloatingIP bool 60 useDefaultSecurityGroup bool 61 network string 62 username string 63 password string 64 tenantName string 65 authMode string 66 authURL string 67 accessKey string 68 secretKey string 69 firewallMode string 70 err string 71 sslHostnameVerification bool 72 sslHostnameSet bool 73 } 74 75 type attrs map[string]interface{} 76 77 func restoreEnvVars(envVars map[string]string) { 78 for k, v := range envVars { 79 os.Setenv(k, v) 80 } 81 } 82 83 func (t configTest) check(c *gc.C) { 84 attrs := testing.FakeConfig().Merge(testing.Attrs{ 85 "type": "openstack", 86 "control-bucket": "x", 87 }).Merge(t.config) 88 89 cfg, err := config.New(config.NoDefaults, attrs) 90 c.Assert(err, gc.IsNil) 91 92 // Set environment variables if any. 93 savedVars := make(map[string]string) 94 if t.envVars != nil { 95 for k, v := range t.envVars { 96 savedVars[k] = os.Getenv(k) 97 os.Setenv(k, v) 98 } 99 } 100 defer restoreEnvVars(savedVars) 101 102 e, err := environs.New(cfg) 103 if t.change != nil { 104 c.Assert(err, gc.IsNil) 105 106 // Testing a change in configuration. 107 var old, changed, valid *config.Config 108 osenv := e.(*environ) 109 old = osenv.ecfg().Config 110 changed, err = old.Apply(t.change) 111 c.Assert(err, gc.IsNil) 112 113 // Keep err for validation below. 114 valid, err = providerInstance.Validate(changed, old) 115 if err == nil { 116 err = osenv.SetConfig(valid) 117 } 118 } 119 if t.err != "" { 120 c.Check(err, gc.ErrorMatches, t.err) 121 return 122 } 123 c.Assert(err, gc.IsNil) 124 125 ecfg := e.(*environ).ecfg() 126 c.Assert(ecfg.Name(), gc.Equals, "testenv") 127 c.Assert(ecfg.controlBucket(), gc.Equals, "x") 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, gc.IsNil) 151 actual, err := e.Provider().SecretAttrs(ecfg.Config) 152 c.Assert(err, gc.IsNil) 153 c.Assert(expected, gc.DeepEquals, actual) 154 } 155 if t.toolsURL != "" { 156 toolsURL, ok := ecfg.ToolsURL() 157 c.Assert(ok, jc.IsTrue) 158 c.Assert(toolsURL, gc.Equals, t.toolsURL) 159 } 160 if t.firewallMode != "" { 161 c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) 162 } 163 c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP) 164 c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup) 165 c.Assert(ecfg.network(), gc.Equals, t.network) 166 // Default should be true 167 expectedHostnameVerification := true 168 if t.sslHostnameSet { 169 expectedHostnameVerification = t.sslHostnameVerification 170 } 171 c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification) 172 for name, expect := range t.expect { 173 actual, found := ecfg.UnknownAttrs()[name] 174 c.Check(found, gc.Equals, true) 175 c.Check(actual, gc.Equals, expect) 176 } 177 } 178 179 func (s *ConfigSuite) SetUpTest(c *gc.C) { 180 s.LoggingSuite.SetUpTest(c) 181 s.oldJujuHome = testing.MakeEmptyFakeHome(c) 182 s.savedVars = make(map[string]string) 183 for v, val := range envVars { 184 s.savedVars[v] = os.Getenv(v) 185 os.Setenv(v, val) 186 } 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.oldJujuHome.Restore() 194 s.LoggingSuite.TearDownTest(c) 195 } 196 197 var configTests = []configTest{ 198 { 199 summary: "setting region", 200 config: attrs{ 201 "region": "testreg", 202 }, 203 region: "testreg", 204 }, { 205 summary: "setting region (2)", 206 config: attrs{ 207 "region": "configtest", 208 }, 209 region: "configtest", 210 }, { 211 summary: "changing region", 212 config: attrs{ 213 "region": "configtest", 214 }, 215 change: attrs{ 216 "region": "somereg", 217 }, 218 err: `cannot change region from "configtest" to "somereg"`, 219 }, { 220 summary: "invalid region", 221 config: attrs{ 222 "region": 666, 223 }, 224 err: `.*expected string, got int\(666\)`, 225 }, { 226 summary: "missing region in environment", 227 envVars: map[string]string{ 228 "OS_REGION_NAME": "", 229 "NOVA_REGION": "", 230 }, 231 err: "required environment variable not set for credentials attribute: Region", 232 }, { 233 summary: "invalid username", 234 config: attrs{ 235 "username": 666, 236 }, 237 err: `.*expected string, got int\(666\)`, 238 }, { 239 summary: "missing username in environment", 240 err: "required environment variable not set for credentials attribute: User", 241 envVars: map[string]string{ 242 "OS_USERNAME": "", 243 "NOVA_USERNAME": "", 244 }, 245 }, { 246 summary: "invalid password", 247 config: attrs{ 248 "password": 666, 249 }, 250 err: `.*expected string, got int\(666\)`, 251 }, { 252 summary: "missing password in environment", 253 err: "required environment variable not set for credentials attribute: Secrets", 254 envVars: map[string]string{ 255 "OS_PASSWORD": "", 256 "NOVA_PASSWORD": "", 257 }, 258 }, { 259 summary: "invalid tenant-name", 260 config: attrs{ 261 "tenant-name": 666, 262 }, 263 err: `.*expected string, got int\(666\)`, 264 }, { 265 summary: "missing tenant in environment", 266 err: "required environment variable not set for credentials attribute: TenantName", 267 envVars: map[string]string{ 268 "OS_TENANT_NAME": "", 269 "NOVA_PROJECT_ID": "", 270 }, 271 }, { 272 summary: "invalid auth-url type", 273 config: attrs{ 274 "auth-url": 666, 275 }, 276 err: `.*expected string, got int\(666\)`, 277 }, { 278 summary: "missing auth-url in environment", 279 err: "required environment variable not set for credentials attribute: URL", 280 envVars: map[string]string{ 281 "OS_AUTH_URL": "", 282 }, 283 }, { 284 summary: "invalid authorization mode", 285 config: attrs{ 286 "auth-mode": "invalid-mode", 287 }, 288 err: ".*invalid authorization mode.*", 289 }, { 290 summary: "keypair authorization mode", 291 config: attrs{ 292 "auth-mode": "keypair", 293 "access-key": "MyAccessKey", 294 "secret-key": "MySecretKey", 295 }, 296 authMode: "keypair", 297 accessKey: "MyAccessKey", 298 secretKey: "MySecretKey", 299 }, { 300 summary: "keypair authorization mode without access key", 301 config: attrs{ 302 "auth-mode": "keypair", 303 "secret-key": "MySecretKey", 304 }, 305 envVars: map[string]string{ 306 "OS_USERNAME": "", 307 }, 308 err: "required environment variable not set for credentials attribute: User", 309 }, { 310 summary: "keypair authorization mode without secret key", 311 config: attrs{ 312 "auth-mode": "keypair", 313 "access-key": "MyAccessKey", 314 }, 315 envVars: map[string]string{ 316 "OS_PASSWORD": "", 317 }, 318 err: "required environment variable not set for credentials attribute: Secrets", 319 }, { 320 summary: "invalid auth-url format", 321 config: attrs{ 322 "auth-url": "invalid", 323 }, 324 err: `invalid auth-url value "invalid"`, 325 }, { 326 summary: "invalid control-bucket", 327 config: attrs{ 328 "control-bucket": 666, 329 }, 330 err: `.*expected string, got int\(666\)`, 331 }, { 332 summary: "changing control-bucket", 333 change: attrs{ 334 "control-bucket": "new-x", 335 }, 336 err: `cannot change control-bucket from "x" to "new-x"`, 337 }, { 338 summary: "valid auth args", 339 config: attrs{ 340 "username": "jujuer", 341 "password": "open sesame", 342 "tenant-name": "juju tenant", 343 "auth-mode": "legacy", 344 "auth-url": "http://some/url", 345 }, 346 username: "jujuer", 347 password: "open sesame", 348 tenantName: "juju tenant", 349 authURL: "http://some/url", 350 authMode: string(AuthLegacy), 351 }, { 352 summary: "valid auth args in environment", 353 envVars: map[string]string{ 354 "OS_USERNAME": "jujuer", 355 "OS_PASSWORD": "open sesame", 356 "OS_AUTH_URL": "http://some/url", 357 "OS_TENANT_NAME": "juju tenant", 358 "OS_REGION_NAME": "region", 359 }, 360 username: "jujuer", 361 password: "open sesame", 362 tenantName: "juju tenant", 363 authURL: "http://some/url", 364 region: "region", 365 }, { 366 summary: "default auth mode based on environment", 367 authMode: string(AuthUserPass), 368 }, { 369 summary: "default use floating ip", 370 // Do not use floating IP's by default. 371 useFloatingIP: false, 372 }, { 373 summary: "use floating ip", 374 config: attrs{ 375 "use-floating-ip": true, 376 }, 377 useFloatingIP: true, 378 }, { 379 summary: "default use default security group", 380 // Do not use default security group by default. 381 useDefaultSecurityGroup: false, 382 }, { 383 summary: "use default security group", 384 config: attrs{ 385 "use-default-secgroup": true, 386 }, 387 useDefaultSecurityGroup: true, 388 }, { 389 summary: "admin-secret given", 390 config: attrs{ 391 "admin-secret": "Futumpsh", 392 }, 393 }, { 394 summary: "default firewall-mode", 395 config: attrs{}, 396 firewallMode: config.FwInstance, 397 }, { 398 summary: "instance firewall-mode", 399 config: attrs{ 400 "firewall-mode": "instance", 401 }, 402 firewallMode: config.FwInstance, 403 }, { 404 summary: "global firewall-mode", 405 config: attrs{ 406 "firewall-mode": "global", 407 }, 408 firewallMode: config.FwGlobal, 409 }, { 410 config: attrs{ 411 "future": "hammerstein", 412 }, 413 expect: attrs{ 414 "future": "hammerstein", 415 }, 416 }, { 417 change: attrs{ 418 "future": "hammerstein", 419 }, 420 expect: attrs{ 421 "future": "hammerstein", 422 }, 423 }, { 424 change: attrs{ 425 "ssl-hostname-verification": false, 426 }, 427 sslHostnameVerification: false, 428 sslHostnameSet: true, 429 }, { 430 change: attrs{ 431 "ssl-hostname-verification": true, 432 }, 433 sslHostnameVerification: true, 434 sslHostnameSet: true, 435 }, { 436 summary: "default network", 437 network: "", 438 }, { 439 summary: "network", 440 config: attrs{ 441 "network": "a-network-label", 442 }, 443 network: "a-network-label", 444 }, 445 } 446 447 func (s *ConfigSuite) TestConfig(c *gc.C) { 448 s.setupEnvCredentials() 449 for i, t := range configTests { 450 c.Logf("test %d: %s (%v)", i, t.summary, t.config) 451 t.check(c) 452 } 453 } 454 455 func (s *ConfigSuite) TestPrepareInsertsUniqueControlBucket(c *gc.C) { 456 s.setupEnvCredentials() 457 attrs := testing.FakeConfig().Merge(testing.Attrs{ 458 "type": "openstack", 459 }) 460 cfg, err := config.New(config.NoDefaults, attrs) 461 c.Assert(err, gc.IsNil) 462 463 env0, err := providerInstance.Prepare(cfg) 464 c.Assert(err, gc.IsNil) 465 bucket0 := env0.(*environ).ecfg().controlBucket() 466 c.Assert(bucket0, gc.Matches, "[a-f0-9]{32}") 467 468 env1, err := providerInstance.Prepare(cfg) 469 c.Assert(err, gc.IsNil) 470 bucket1 := env1.(*environ).ecfg().controlBucket() 471 c.Assert(bucket1, gc.Matches, "[a-f0-9]{32}") 472 473 c.Assert(bucket1, gc.Not(gc.Equals), bucket0) 474 } 475 476 func (s *ConfigSuite) TestPrepareDoesNotTouchExistingControlBucket(c *gc.C) { 477 s.setupEnvCredentials() 478 attrs := testing.FakeConfig().Merge(testing.Attrs{ 479 "type": "openstack", 480 "control-bucket": "burblefoo", 481 }) 482 cfg, err := config.New(config.NoDefaults, attrs) 483 c.Assert(err, gc.IsNil) 484 485 env, err := providerInstance.Prepare(cfg) 486 c.Assert(err, gc.IsNil) 487 bucket := env.(*environ).ecfg().controlBucket() 488 c.Assert(bucket, gc.Equals, "burblefoo") 489 } 490 491 func (s *ConfigSuite) setupEnvCredentials() { 492 os.Setenv("OS_USERNAME", "user") 493 os.Setenv("OS_PASSWORD", "secret") 494 os.Setenv("OS_AUTH_URL", "http://auth") 495 os.Setenv("OS_TENANT_NAME", "sometenant") 496 os.Setenv("OS_REGION_NAME", "region") 497 } 498 499 type ConfigDeprecationSuite struct { 500 ConfigSuite 501 writer *testWriter 502 } 503 504 var _ = gc.Suite(&ConfigDeprecationSuite{}) 505 506 func (s *ConfigDeprecationSuite) SetUpTest(c *gc.C) { 507 s.ConfigSuite.SetUpTest(c) 508 } 509 510 func (s *ConfigDeprecationSuite) TearDownTest(c *gc.C) { 511 s.ConfigSuite.TearDownTest(c) 512 } 513 514 func (s *ConfigDeprecationSuite) setupLogger(c *gc.C) { 515 var err error 516 s.writer = &testWriter{} 517 err = loggo.RegisterWriter("test", s.writer, loggo.WARNING) 518 c.Assert(err, gc.IsNil) 519 } 520 521 func (s *ConfigDeprecationSuite) resetLogger(c *gc.C) { 522 _, _, err := loggo.RemoveWriter("test") 523 c.Assert(err, gc.IsNil) 524 } 525 526 type testWriter struct { 527 messages []string 528 } 529 530 func (t *testWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { 531 t.messages = append(t.messages, message) 532 } 533 534 func (s *ConfigDeprecationSuite) setupEnv(c *gc.C, deprecatedKey, value string) { 535 s.setupEnvCredentials() 536 attrs := testing.FakeConfig().Merge(testing.Attrs{ 537 "name": "testenv", 538 "type": "openstack", 539 "control-bucket": "x", 540 deprecatedKey: value, 541 }) 542 _, err := environs.NewFromAttrs(attrs) 543 c.Assert(err, gc.IsNil) 544 }