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