github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 "sort" 11 "strings" 12 13 "github.com/juju/loggo" 14 gc "launchpad.net/gocheck" 15 16 "launchpad.net/juju-core/environs" 17 "launchpad.net/juju-core/environs/config" 18 "launchpad.net/juju-core/provider/dummy" 19 _ "launchpad.net/juju-core/provider/manual" 20 "launchpad.net/juju-core/testing" 21 jc "launchpad.net/juju-core/testing/checkers" 22 "launchpad.net/juju-core/testing/testbase" 23 ) 24 25 type suite struct { 26 testbase.LoggingSuite 27 } 28 29 var _ = gc.Suite(&suite{}) 30 31 func (s *suite) TearDownTest(c *gc.C) { 32 dummy.Reset() 33 s.LoggingSuite.TearDownTest(c) 34 } 35 36 var invalidConfigTests = []struct { 37 env string 38 err string 39 }{ 40 {"'", "YAML error:.*"}, 41 {` 42 default: unknown 43 environments: 44 only: 45 type: unknown 46 `, `default environment .* does not exist`, 47 }, 48 } 49 50 func (*suite) TestInvalidConfig(c *gc.C) { 51 for i, t := range invalidConfigTests { 52 c.Logf("running test %v", i) 53 _, err := environs.ReadEnvironsBytes([]byte(t.env)) 54 c.Check(err, gc.ErrorMatches, t.err) 55 } 56 } 57 58 var invalidEnvTests = []struct { 59 env string 60 name string 61 err string 62 }{ 63 {` 64 environments: 65 only: 66 foo: bar 67 `, "", `environment "only" has no type`, 68 }, {` 69 environments: 70 only: 71 foo: bar 72 `, "only", `environment "only" has no type`, 73 }, {` 74 environments: 75 only: 76 foo: bar 77 type: crazy 78 `, "only", `environment "only" has an unknown provider type "crazy"`, 79 }, 80 } 81 82 func (*suite) TestInvalidEnv(c *gc.C) { 83 defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() 84 for i, t := range invalidEnvTests { 85 c.Logf("running test %v", i) 86 es, err := environs.ReadEnvironsBytes([]byte(t.env)) 87 c.Check(err, gc.IsNil) 88 cfg, err := es.Config(t.name) 89 c.Check(err, gc.ErrorMatches, t.err) 90 c.Check(cfg, gc.IsNil) 91 } 92 } 93 94 func (*suite) TestNoWarningForDeprecatedButUnusedEnv(c *gc.C) { 95 // This tests that a config that has a deprecated field doesn't 96 // generate a Warning if we don't actually ask for that environment. 97 // However, we can only really trigger that when we have a deprecated 98 // field. If support for the field is removed entirely, another 99 // mechanism will need to be used 100 defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() 101 content := ` 102 environments: 103 valid: 104 type: dummy 105 state-server: false 106 deprecated: 107 type: dummy 108 state-server: false 109 tools-url: aknowndeprecatedfield 110 ` 111 tw := &loggo.TestWriter{} 112 // we only capture Warning or above 113 c.Assert(loggo.RegisterWriter("invalid-env-tester", tw, loggo.WARNING), gc.IsNil) 114 defer loggo.RemoveWriter("invalid-env-tester") 115 116 envs, err := environs.ReadEnvironsBytes([]byte(content)) 117 c.Check(err, gc.IsNil) 118 names := envs.Names() 119 sort.Strings(names) 120 c.Check(names, gc.DeepEquals, []string{"deprecated", "valid"}) 121 // There should be no warning in the log 122 c.Check(tw.Log, gc.HasLen, 0) 123 // Now we actually grab the 'valid' entry 124 _, err = envs.Config("valid") 125 c.Check(err, gc.IsNil) 126 // And still we have no warnings 127 c.Check(tw.Log, gc.HasLen, 0) 128 // Only once we grab the deprecated one do we see any warnings 129 _, err = envs.Config("deprecated") 130 c.Check(err, gc.IsNil) 131 c.Check(tw.Log, gc.HasLen, 1) 132 } 133 134 func (*suite) TestNoHomeBeforeConfig(c *gc.C) { 135 // Test that we don't actually need HOME set until we call envs.Config() 136 // Because of this, we intentionally do *not* call testing.MakeFakeHomeNoEnvironments() 137 content := ` 138 environments: 139 valid: 140 type: dummy 141 amazon: 142 type: ec2 143 ` 144 _, err := environs.ReadEnvironsBytes([]byte(content)) 145 c.Check(err, gc.IsNil) 146 } 147 148 func (*suite) TestNoEnv(c *gc.C) { 149 defer testing.MakeFakeHomeNoEnvironments(c).Restore() 150 es, err := environs.ReadEnvirons("") 151 c.Assert(es, gc.IsNil) 152 c.Assert(err, jc.Satisfies, environs.IsNoEnv) 153 } 154 155 var configTests = []struct { 156 env string 157 check func(c *gc.C, envs *environs.Environs) 158 }{ 159 {` 160 environments: 161 only: 162 type: dummy 163 state-server: false 164 `, func(c *gc.C, envs *environs.Environs) { 165 cfg, err := envs.Config("") 166 c.Assert(err, gc.IsNil) 167 c.Assert(cfg.Name(), gc.Equals, "only") 168 }}, {` 169 default: 170 invalid 171 environments: 172 valid: 173 type: dummy 174 state-server: false 175 invalid: 176 type: crazy 177 `, func(c *gc.C, envs *environs.Environs) { 178 cfg, err := envs.Config("") 179 c.Assert(err, gc.ErrorMatches, `environment "invalid" has an unknown provider type "crazy"`) 180 c.Assert(cfg, gc.IsNil) 181 cfg, err = envs.Config("valid") 182 c.Assert(err, gc.IsNil) 183 c.Assert(cfg.Name(), gc.Equals, "valid") 184 }}, {` 185 environments: 186 one: 187 type: dummy 188 state-server: false 189 two: 190 type: dummy 191 state-server: false 192 `, func(c *gc.C, envs *environs.Environs) { 193 cfg, err := envs.Config("") 194 c.Assert(err, gc.ErrorMatches, `no default environment found`) 195 c.Assert(cfg, gc.IsNil) 196 }}, 197 } 198 199 func (*suite) TestConfig(c *gc.C) { 200 defer testing.MakeFakeHomeNoEnvironments(c, "only", "valid", "one", "two").Restore() 201 for i, t := range configTests { 202 c.Logf("running test %v", i) 203 envs, err := environs.ReadEnvironsBytes([]byte(t.env)) 204 c.Assert(err, gc.IsNil) 205 t.check(c, envs) 206 } 207 } 208 209 func (*suite) TestDefaultConfigFile(c *gc.C) { 210 defer testing.MakeEmptyFakeHome(c).Restore() 211 212 env := ` 213 environments: 214 only: 215 type: dummy 216 state-server: false 217 authorized-keys: i-am-a-key 218 ` 219 outfile, err := environs.WriteEnvirons("", env) 220 c.Assert(err, gc.IsNil) 221 path := testing.HomePath(".juju", "environments.yaml") 222 c.Assert(path, gc.Equals, outfile) 223 224 envs, err := environs.ReadEnvirons("") 225 c.Assert(err, gc.IsNil) 226 cfg, err := envs.Config("") 227 c.Assert(err, gc.IsNil) 228 c.Assert(cfg.Name(), gc.Equals, "only") 229 } 230 231 func (*suite) TestConfigPerm(c *gc.C) { 232 defer testing.MakeSampleHome(c).Restore() 233 234 path := testing.HomePath(".juju") 235 info, err := os.Lstat(path) 236 c.Assert(err, gc.IsNil) 237 oldPerm := info.Mode().Perm() 238 env := ` 239 environments: 240 only: 241 type: dummy 242 state-server: false 243 authorized-keys: i-am-a-key 244 ` 245 outfile, err := environs.WriteEnvirons("", env) 246 c.Assert(err, gc.IsNil) 247 248 info, err = os.Lstat(outfile) 249 c.Assert(err, gc.IsNil) 250 c.Assert(info.Mode().Perm(), gc.Equals, os.FileMode(0600)) 251 252 info, err = os.Lstat(filepath.Dir(outfile)) 253 c.Assert(err, gc.IsNil) 254 c.Assert(info.Mode().Perm(), gc.Equals, oldPerm) 255 256 } 257 258 func (*suite) TestNamedConfigFile(c *gc.C) { 259 defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() 260 261 env := ` 262 environments: 263 only: 264 type: dummy 265 state-server: false 266 authorized-keys: i-am-a-key 267 ` 268 path := filepath.Join(c.MkDir(), "a-file") 269 outfile, err := environs.WriteEnvirons(path, env) 270 c.Assert(err, gc.IsNil) 271 c.Assert(path, gc.Equals, outfile) 272 273 envs, err := environs.ReadEnvirons(path) 274 c.Assert(err, gc.IsNil) 275 cfg, err := envs.Config("") 276 c.Assert(err, gc.IsNil) 277 c.Assert(cfg.Name(), gc.Equals, "only") 278 } 279 280 func inMap(attrs testing.Attrs, attr string) bool { 281 _, ok := attrs[attr] 282 return ok 283 } 284 285 func (*suite) TestBootstrapConfig(c *gc.C) { 286 defer testing.MakeFakeHomeNoEnvironments(c, "bladaam").Restore() 287 attrs := dummySampleConfig().Merge(testing.Attrs{ 288 "agent-version": "1.2.3", 289 }) 290 c.Assert(inMap(attrs, "secret"), jc.IsTrue) 291 c.Assert(inMap(attrs, "ca-private-key"), jc.IsTrue) 292 c.Assert(inMap(attrs, "admin-secret"), jc.IsTrue) 293 294 cfg, err := config.New(config.NoDefaults, attrs) 295 c.Assert(err, gc.IsNil) 296 c.Assert(err, gc.IsNil) 297 298 cfg1, err := environs.BootstrapConfig(cfg) 299 c.Assert(err, gc.IsNil) 300 301 expect := cfg.AllAttrs() 302 expect["admin-secret"] = "" 303 expect["ca-private-key"] = "" 304 c.Assert(cfg1.AllAttrs(), gc.DeepEquals, expect) 305 } 306 307 type dummyProvider struct { 308 environs.EnvironProvider 309 } 310 311 func (s *suite) TestRegisterProvider(c *gc.C) { 312 s.PatchValue(environs.Providers, make(map[string]environs.EnvironProvider)) 313 s.PatchValue(environs.ProviderAliases, make(map[string]string)) 314 type step struct { 315 name string 316 aliases []string 317 err string 318 } 319 type test []step 320 321 tests := []test{ 322 []step{{ 323 name: "providerName", 324 }}, 325 []step{{ 326 name: "providerName", 327 aliases: []string{"providerName"}, 328 err: "juju: duplicate provider alias \"providerName\"", 329 }}, 330 []step{{ 331 name: "providerName", 332 aliases: []string{"providerAlias", "providerAlias"}, 333 err: "juju: duplicate provider alias \"providerAlias\"", 334 }}, 335 []step{{ 336 name: "providerName", 337 aliases: []string{"providerAlias1", "providerAlias2"}, 338 }}, 339 []step{{ 340 name: "providerName", 341 }, { 342 name: "providerName", 343 err: "juju: duplicate provider name \"providerName\"", 344 }}, 345 []step{{ 346 name: "providerName1", 347 }, { 348 name: "providerName2", 349 aliases: []string{"providerName"}, 350 }}, 351 []step{{ 352 name: "providerName1", 353 }, { 354 name: "providerName2", 355 aliases: []string{"providerName1"}, 356 err: "juju: duplicate provider alias \"providerName1\"", 357 }}, 358 } 359 360 registerProvider := func(name string, aliases []string) (err error) { 361 defer func() { err, _ = recover().(error) }() 362 registered := &dummyProvider{} 363 environs.RegisterProvider(name, registered, aliases...) 364 p, err := environs.Provider(name) 365 c.Assert(err, gc.IsNil) 366 c.Assert(p, gc.Equals, registered) 367 for _, alias := range aliases { 368 p, err := environs.Provider(alias) 369 c.Assert(err, gc.IsNil) 370 c.Assert(p, gc.Equals, registered) 371 c.Assert(p, gc.Equals, registered) 372 } 373 return nil 374 } 375 for i, test := range tests { 376 c.Logf("test %d: %v", i, test) 377 for k := range *environs.Providers { 378 delete(*environs.Providers, k) 379 } 380 for k := range *environs.ProviderAliases { 381 delete(*environs.ProviderAliases, k) 382 } 383 for _, step := range test { 384 err := registerProvider(step.name, step.aliases) 385 if step.err == "" { 386 c.Assert(err, gc.IsNil) 387 } else { 388 c.Assert(err, gc.ErrorMatches, step.err) 389 } 390 } 391 } 392 } 393 394 type ConfigDeprecationSuite struct { 395 suite 396 writer *loggo.TestWriter 397 } 398 399 var _ = gc.Suite(&ConfigDeprecationSuite{}) 400 401 func (s *ConfigDeprecationSuite) setupLogger(c *gc.C) func() { 402 var err error 403 s.writer = &loggo.TestWriter{} 404 err = loggo.RegisterWriter("test", s.writer, loggo.WARNING) 405 c.Assert(err, gc.IsNil) 406 return func() { 407 _, _, err := loggo.RemoveWriter("test") 408 c.Assert(err, gc.IsNil) 409 } 410 } 411 412 func (s *ConfigDeprecationSuite) checkDeprecationWarning(c *gc.C, attrs testing.Attrs, expectedMsg string) { 413 defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() 414 content := ` 415 environments: 416 deprecated: 417 type: dummy 418 state-server: false 419 ` 420 restore := s.setupLogger(c) 421 defer restore() 422 423 envs, err := environs.ReadEnvironsBytes([]byte(content)) 424 c.Check(err, gc.IsNil) 425 environs.UpdateEnvironAttrs(envs, "deprecated", attrs) 426 _, err = envs.Config("deprecated") 427 c.Check(err, gc.IsNil) 428 c.Assert(s.writer.Log, gc.HasLen, 1) 429 stripped := strings.Replace(s.writer.Log[0].Message, "\n", "", -1) 430 c.Assert(stripped, gc.Matches, expectedMsg) 431 } 432 433 func (s *ConfigDeprecationSuite) TestDeprecatedToolsURLWarning(c *gc.C) { 434 attrs := testing.Attrs{ 435 "tools-url": "aknowndeprecatedfield", 436 } 437 expected := fmt.Sprintf(`.*Config attribute "tools-url" \(aknowndeprecatedfield\) is deprecated\.` + 438 `The location to find tools is now specified using the "tools-metadata-url" attribute.*`) 439 s.checkDeprecationWarning(c, attrs, expected) 440 } 441 442 func (s *ConfigDeprecationSuite) TestDeprecatedToolsURLWithNewURLWarning(c *gc.C) { 443 attrs := testing.Attrs{ 444 "tools-url": "aknowndeprecatedfield", 445 "tools-metadata-url": "newvalue", 446 } 447 expected := fmt.Sprintf( 448 `.*Config attribute "tools-url" \(aknowndeprecatedfield\) is deprecated and will be ignored since` + 449 `the new tools URL attribute "tools-metadata-url".*`) 450 s.checkDeprecationWarning(c, attrs, expected) 451 } 452 453 func (s *ConfigDeprecationSuite) TestDeprecatedTypeNullWarning(c *gc.C) { 454 attrs := testing.Attrs{"type": "null"} 455 expected := `Provider type \"null\" has been renamed to \"manual\"\.Please update your environment configuration\.` 456 s.checkDeprecationWarning(c, attrs, expected) 457 }