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  }