launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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/loggo/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  }