github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/environs/config_test.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package environs_test 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "runtime" 11 "sort" 12 "strings" 13 14 "github.com/juju/loggo" 15 gitjujutesting "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/environs/config" 21 "github.com/juju/juju/provider/dummy" 22 _ "github.com/juju/juju/provider/manual" 23 "github.com/juju/juju/testing" 24 ) 25 26 type suite struct { 27 testing.FakeJujuHomeSuite 28 } 29 30 var _ = gc.Suite(&suite{}) 31 32 func (s *suite) TearDownTest(c *gc.C) { 33 dummy.Reset() 34 s.FakeJujuHomeSuite.TearDownTest(c) 35 } 36 37 // dummySampleConfig returns the dummy sample config without 38 // the state server configured. 39 // This function also exists in cloudconfig/userdata_test 40 // Maybe place it in dummy and export it? 41 func dummySampleConfig() testing.Attrs { 42 return dummy.SampleConfig().Merge(testing.Attrs{ 43 "state-server": false, 44 }) 45 } 46 47 var invalidConfigTests = []struct { 48 env string 49 err string 50 }{ 51 {"'", "YAML error:.*"}, 52 {` 53 default: unknown 54 environments: 55 only: 56 type: unknown 57 `, `default environment .* does not exist`, 58 }, 59 } 60 61 func (*suite) TestInvalidConfig(c *gc.C) { 62 for i, t := range invalidConfigTests { 63 c.Logf("running test %v", i) 64 _, err := environs.ReadEnvironsBytes([]byte(t.env)) 65 c.Check(err, gc.ErrorMatches, t.err) 66 } 67 } 68 69 var invalidEnvTests = []struct { 70 env string 71 name string 72 err string 73 }{ 74 {` 75 environments: 76 only: 77 foo: bar 78 `, "", `environment "only" has no type`, 79 }, {` 80 environments: 81 only: 82 foo: bar 83 `, "only", `environment "only" has no type`, 84 }, {` 85 environments: 86 only: 87 foo: bar 88 type: crazy 89 `, "only", `environment "only" has an unknown provider type "crazy"`, 90 }, 91 } 92 93 func (*suite) TestInvalidEnv(c *gc.C) { 94 for i, t := range invalidEnvTests { 95 c.Logf("running test %v", i) 96 es, err := environs.ReadEnvironsBytes([]byte(t.env)) 97 c.Check(err, jc.ErrorIsNil) 98 cfg, err := es.Config(t.name) 99 c.Check(err, gc.ErrorMatches, t.err) 100 c.Check(cfg, gc.IsNil) 101 } 102 } 103 104 func (*suite) TestNoWarningForDeprecatedButUnusedEnv(c *gc.C) { 105 // This tests that a config that has a deprecated field doesn't 106 // generate a Warning if we don't actually ask for that environment. 107 // However, we can only really trigger that when we have a deprecated 108 // field. If support for the field is removed entirely, another 109 // mechanism will need to be used 110 content := ` 111 environments: 112 valid: 113 type: dummy 114 state-server: false 115 deprecated: 116 type: dummy 117 state-server: false 118 tools-metadata-url: aknowndeprecatedfield 119 lxc-use-clone: true 120 ` 121 var tw loggo.TestWriter 122 // we only capture Warning or above 123 c.Assert(loggo.RegisterWriter("invalid-env-tester", &tw, loggo.WARNING), gc.IsNil) 124 defer loggo.RemoveWriter("invalid-env-tester") 125 126 envs, err := environs.ReadEnvironsBytes([]byte(content)) 127 c.Check(err, jc.ErrorIsNil) 128 names := envs.Names() 129 sort.Strings(names) 130 c.Check(names, gc.DeepEquals, []string{"deprecated", "valid"}) 131 // There should be no warning in the log 132 c.Check(tw.Log(), gc.HasLen, 0) 133 // Now we actually grab the 'valid' entry 134 _, err = envs.Config("valid") 135 c.Check(err, jc.ErrorIsNil) 136 // And still we have no warnings 137 c.Check(tw.Log(), gc.HasLen, 0) 138 // Only once we grab the deprecated one do we see any warnings 139 _, err = envs.Config("deprecated") 140 c.Check(err, jc.ErrorIsNil) 141 c.Check(tw.Log(), gc.HasLen, 2) 142 } 143 144 func (*suite) TestNoHomeBeforeConfig(c *gc.C) { 145 // Test that we don't actually need HOME set until we call envs.Config() 146 os.Setenv("HOME", "") 147 content := ` 148 environments: 149 valid: 150 type: dummy 151 amazon: 152 type: ec2 153 ` 154 _, err := environs.ReadEnvironsBytes([]byte(content)) 155 c.Check(err, jc.ErrorIsNil) 156 } 157 158 func (*suite) TestNoEnv(c *gc.C) { 159 envPath := gitjujutesting.HomePath(".juju", "environments.yaml") 160 err := os.Remove(envPath) 161 c.Assert(err, jc.ErrorIsNil) 162 es, err := environs.ReadEnvirons("") 163 c.Assert(es, gc.IsNil) 164 c.Assert(err, jc.Satisfies, environs.IsNoEnv) 165 } 166 167 var configTests = []struct { 168 env string 169 check func(c *gc.C, envs *environs.Environs) 170 }{ 171 {` 172 environments: 173 only: 174 type: dummy 175 state-server: false 176 `, func(c *gc.C, envs *environs.Environs) { 177 cfg, err := envs.Config("") 178 c.Assert(err, jc.ErrorIsNil) 179 c.Assert(cfg.Name(), gc.Equals, "only") 180 }}, {` 181 default: 182 invalid 183 environments: 184 valid: 185 type: dummy 186 state-server: false 187 invalid: 188 type: crazy 189 `, func(c *gc.C, envs *environs.Environs) { 190 cfg, err := envs.Config("") 191 c.Assert(err, gc.ErrorMatches, `environment "invalid" has an unknown provider type "crazy"`) 192 c.Assert(cfg, gc.IsNil) 193 cfg, err = envs.Config("valid") 194 c.Assert(err, jc.ErrorIsNil) 195 c.Assert(cfg.Name(), gc.Equals, "valid") 196 }}, {` 197 environments: 198 one: 199 type: dummy 200 state-server: false 201 two: 202 type: dummy 203 state-server: false 204 `, func(c *gc.C, envs *environs.Environs) { 205 cfg, err := envs.Config("") 206 c.Assert(err, gc.ErrorMatches, `no default environment found`) 207 c.Assert(cfg, gc.IsNil) 208 }}, 209 } 210 211 func (*suite) TestConfig(c *gc.C) { 212 for i, t := range configTests { 213 c.Logf("running test %v", i) 214 envs, err := environs.ReadEnvironsBytes([]byte(t.env)) 215 c.Assert(err, jc.ErrorIsNil) 216 t.check(c, envs) 217 } 218 } 219 220 func (*suite) TestDefaultConfigFile(c *gc.C) { 221 env := ` 222 environments: 223 only: 224 type: dummy 225 state-server: false 226 authorized-keys: i-am-a-key 227 ` 228 outfile, err := environs.WriteEnvirons("", env) 229 c.Assert(err, jc.ErrorIsNil) 230 path := gitjujutesting.HomePath(".juju", "environments.yaml") 231 c.Assert(path, gc.Equals, outfile) 232 233 envs, err := environs.ReadEnvirons("") 234 c.Assert(err, jc.ErrorIsNil) 235 cfg, err := envs.Config("") 236 c.Assert(err, jc.ErrorIsNil) 237 c.Assert(cfg.Name(), gc.Equals, "only") 238 } 239 240 func (s *suite) TestConfigPerm(c *gc.C) { 241 testing.MakeSampleJujuHome(c) 242 243 path := gitjujutesting.HomePath(".juju") 244 info, err := os.Lstat(path) 245 c.Assert(err, jc.ErrorIsNil) 246 oldPerm := info.Mode().Perm() 247 env := ` 248 environments: 249 only: 250 type: dummy 251 state-server: false 252 authorized-keys: i-am-a-key 253 ` 254 outfile, err := environs.WriteEnvirons("", env) 255 c.Assert(err, jc.ErrorIsNil) 256 257 info, err = os.Lstat(outfile) 258 c.Assert(err, jc.ErrorIsNil) 259 // Windows is not fully POSIX compliant. Normal permission 260 // checking will yield unexpected results 261 if runtime.GOOS != "windows" { 262 c.Assert(info.Mode().Perm(), gc.Equals, os.FileMode(0600)) 263 } 264 265 info, err = os.Lstat(filepath.Dir(outfile)) 266 c.Assert(err, jc.ErrorIsNil) 267 if runtime.GOOS != "windows" { 268 c.Assert(info.Mode().Perm(), gc.Equals, oldPerm) 269 } 270 271 } 272 273 func (*suite) TestNamedConfigFile(c *gc.C) { 274 275 env := ` 276 environments: 277 only: 278 type: dummy 279 state-server: false 280 authorized-keys: i-am-a-key 281 ` 282 path := filepath.Join(c.MkDir(), "a-file") 283 outfile, err := environs.WriteEnvirons(path, env) 284 c.Assert(err, jc.ErrorIsNil) 285 c.Assert(path, gc.Equals, outfile) 286 287 envs, err := environs.ReadEnvirons(path) 288 c.Assert(err, jc.ErrorIsNil) 289 cfg, err := envs.Config("") 290 c.Assert(err, jc.ErrorIsNil) 291 c.Assert(cfg.Name(), gc.Equals, "only") 292 } 293 294 func inMap(attrs testing.Attrs, attr string) bool { 295 _, ok := attrs[attr] 296 return ok 297 } 298 299 func (*suite) TestBootstrapConfig(c *gc.C) { 300 attrs := dummySampleConfig().Merge(testing.Attrs{ 301 "agent-version": "1.2.3", 302 }) 303 c.Assert(inMap(attrs, "secret"), jc.IsTrue) 304 c.Assert(inMap(attrs, "ca-private-key"), jc.IsTrue) 305 c.Assert(inMap(attrs, "admin-secret"), jc.IsTrue) 306 307 cfg, err := config.New(config.NoDefaults, attrs) 308 c.Assert(err, jc.ErrorIsNil) 309 c.Assert(err, jc.ErrorIsNil) 310 311 cfg1, err := environs.BootstrapConfig(cfg) 312 c.Assert(err, jc.ErrorIsNil) 313 314 expect := cfg.AllAttrs() 315 expect["admin-secret"] = "" 316 expect["ca-private-key"] = "" 317 c.Assert(cfg1.AllAttrs(), gc.DeepEquals, expect) 318 } 319 320 func (s *suite) TestDisallowedInBootstrap(c *gc.C) { 321 content := ` 322 environments: 323 dummy: 324 type: dummy 325 state-server: false 326 ` 327 for key, value := range map[string]interface{}{ 328 "storage-default-block-source": "loop", 329 } { 330 envContent := fmt.Sprintf("%s\n %s: %s", content, key, value) 331 envs, err := environs.ReadEnvironsBytes([]byte(envContent)) 332 c.Check(err, jc.ErrorIsNil) 333 _, err = envs.Config("dummy") 334 c.Assert(err, gc.ErrorMatches, "attribute .* is not allowed in bootstrap configurations") 335 } 336 } 337 338 type dummyProvider struct { 339 environs.EnvironProvider 340 } 341 342 func (s *suite) TestRegisterProvider(c *gc.C) { 343 s.PatchValue(environs.Providers, make(map[string]environs.EnvironProvider)) 344 s.PatchValue(environs.ProviderAliases, make(map[string]string)) 345 type step struct { 346 name string 347 aliases []string 348 err string 349 } 350 type test []step 351 352 tests := []test{ 353 []step{{ 354 name: "providerName", 355 }}, 356 []step{{ 357 name: "providerName", 358 aliases: []string{"providerName"}, 359 err: "juju: duplicate provider alias \"providerName\"", 360 }}, 361 []step{{ 362 name: "providerName", 363 aliases: []string{"providerAlias", "providerAlias"}, 364 err: "juju: duplicate provider alias \"providerAlias\"", 365 }}, 366 []step{{ 367 name: "providerName", 368 aliases: []string{"providerAlias1", "providerAlias2"}, 369 }}, 370 []step{{ 371 name: "providerName", 372 }, { 373 name: "providerName", 374 err: "juju: duplicate provider name \"providerName\"", 375 }}, 376 []step{{ 377 name: "providerName1", 378 }, { 379 name: "providerName2", 380 aliases: []string{"providerName"}, 381 }}, 382 []step{{ 383 name: "providerName1", 384 }, { 385 name: "providerName2", 386 aliases: []string{"providerName1"}, 387 err: "juju: duplicate provider alias \"providerName1\"", 388 }}, 389 } 390 391 registerProvider := func(name string, aliases []string) (err error) { 392 defer func() { err, _ = recover().(error) }() 393 registered := &dummyProvider{} 394 environs.RegisterProvider(name, registered, aliases...) 395 p, err := environs.Provider(name) 396 c.Assert(err, jc.ErrorIsNil) 397 c.Assert(p, gc.Equals, registered) 398 for _, alias := range aliases { 399 p, err := environs.Provider(alias) 400 c.Assert(err, jc.ErrorIsNil) 401 c.Assert(p, gc.Equals, registered) 402 c.Assert(p, gc.Equals, registered) 403 } 404 return nil 405 } 406 for i, test := range tests { 407 c.Logf("test %d: %v", i, test) 408 for k := range *environs.Providers { 409 delete(*environs.Providers, k) 410 } 411 for k := range *environs.ProviderAliases { 412 delete(*environs.ProviderAliases, k) 413 } 414 for _, step := range test { 415 err := registerProvider(step.name, step.aliases) 416 if step.err == "" { 417 c.Assert(err, jc.ErrorIsNil) 418 } else { 419 c.Assert(err, gc.ErrorMatches, step.err) 420 } 421 } 422 } 423 } 424 425 type ConfigDeprecationSuite struct { 426 suite 427 writer *loggo.TestWriter 428 } 429 430 var _ = gc.Suite(&ConfigDeprecationSuite{}) 431 432 func (s *ConfigDeprecationSuite) setupLogger(c *gc.C) func() { 433 var err error 434 s.writer = &loggo.TestWriter{} 435 err = loggo.RegisterWriter("test", s.writer, loggo.WARNING) 436 c.Assert(err, jc.ErrorIsNil) 437 return func() { 438 _, _, err := loggo.RemoveWriter("test") 439 c.Assert(err, jc.ErrorIsNil) 440 } 441 } 442 443 func (s *ConfigDeprecationSuite) checkDeprecationWarning(c *gc.C, attrs testing.Attrs, expectedMsg string) { 444 content := ` 445 environments: 446 deprecated: 447 type: dummy 448 state-server: false 449 ` 450 restore := s.setupLogger(c) 451 defer restore() 452 453 envs, err := environs.ReadEnvironsBytes([]byte(content)) 454 c.Assert(err, jc.ErrorIsNil) 455 environs.UpdateEnvironAttrs(envs, "deprecated", attrs) 456 _, err = envs.Config("deprecated") 457 c.Assert(err, jc.ErrorIsNil) 458 459 var stripped string 460 if log := s.writer.Log(); len(log) == 1 { 461 stripped = strings.Replace(log[0].Message, "\n", "", -1) 462 } 463 464 c.Check(stripped, gc.Matches, expectedMsg) 465 } 466 467 const ( 468 // This is a standard configuration warning when old attribute was specified. 469 standardDeprecationWarning = `.*Your configuration should be updated to set .* %v.*` 470 471 // This is a standard deprecation warning when both old and new attributes were specified. 472 standardDeprecationWarningWithNew = `.*is deprecated and will be ignored since the new .*` 473 ) 474 475 func (s *ConfigDeprecationSuite) TestDeprecatedToolsURLWarning(c *gc.C) { 476 attrs := testing.Attrs{ 477 "tools-metadata-url": "aknowndeprecatedfield", 478 } 479 expected := fmt.Sprintf(standardDeprecationWarning, "aknowndeprecatedfield") 480 s.checkDeprecationWarning(c, attrs, expected) 481 } 482 483 func (s *ConfigDeprecationSuite) TestDeprecatedSafeModeWarning(c *gc.C) { 484 // Test that the warning is logged. 485 attrs := testing.Attrs{"provisioner-safe-mode": true} 486 expected := fmt.Sprintf(standardDeprecationWarning, "destroyed") 487 s.checkDeprecationWarning(c, attrs, expected) 488 } 489 490 func (s *ConfigDeprecationSuite) TestDeprecatedSafeModeWarningWithHarvest(c *gc.C) { 491 attrs := testing.Attrs{ 492 "provisioner-safe-mode": true, 493 "provisioner-harvest-mode": "none", 494 } 495 // Test that the warning is logged. 496 expected := fmt.Sprintf(standardDeprecationWarningWithNew) 497 s.checkDeprecationWarning(c, attrs, expected) 498 } 499 500 func (s *ConfigDeprecationSuite) TestDeprecatedToolsURLWithNewURLWarning(c *gc.C) { 501 attrs := testing.Attrs{ 502 "tools-metadata-url": "aknowndeprecatedfield", 503 "agent-metadata-url": "newvalue", 504 } 505 expected := fmt.Sprintf(standardDeprecationWarningWithNew) 506 s.checkDeprecationWarning(c, attrs, expected) 507 } 508 509 func (s *ConfigDeprecationSuite) TestDeprecatedTypeNullWarning(c *gc.C) { 510 attrs := testing.Attrs{"type": "null"} 511 expected := `Provider type "null" has been renamed to "manual".Please update your environment configuration.` 512 s.checkDeprecationWarning(c, attrs, expected) 513 } 514 515 func (s *ConfigDeprecationSuite) TestDeprecatedLxcUseCloneWarning(c *gc.C) { 516 attrs := testing.Attrs{"lxc-use-clone": true} 517 expected := fmt.Sprintf(standardDeprecationWarning, true) 518 s.checkDeprecationWarning(c, attrs, expected) 519 } 520 521 func (s *ConfigDeprecationSuite) TestDeprecatedToolsStreamWarning(c *gc.C) { 522 attrs := testing.Attrs{"tools-stream": "devel"} 523 expected := fmt.Sprintf(standardDeprecationWarning, "devel") 524 s.checkDeprecationWarning(c, attrs, expected) 525 } 526 527 func (s *ConfigDeprecationSuite) TestDeprecatedToolsStreamWIthAgentWarning(c *gc.C) { 528 attrs := testing.Attrs{ 529 "tools-stream": "devel", 530 "agent-stream": "proposed", 531 } 532 expected := fmt.Sprintf(standardDeprecationWarningWithNew) 533 s.checkDeprecationWarning(c, attrs, expected) 534 } 535 536 func (s *ConfigDeprecationSuite) TestDeprecatedBlockWarning(c *gc.C) { 537 assertBlockWarning := func(tst string) { 538 attrs := testing.Attrs{tst: true} 539 s.checkDeprecationWarning(c, attrs, ".*is deprecated and will be ignored since.*") 540 } 541 assertBlockWarning("block-destroy-environment") 542 assertBlockWarning("block-remove-object") 543 assertBlockWarning("block-all-changes") 544 }