github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/environs/open_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environs_test
     5  
     6  import (
     7  	"strings"
     8  
     9  	gc "launchpad.net/gocheck"
    10  
    11  	"launchpad.net/juju-core/cert"
    12  	"launchpad.net/juju-core/constraints"
    13  	"launchpad.net/juju-core/environs"
    14  	"launchpad.net/juju-core/environs/bootstrap"
    15  	"launchpad.net/juju-core/environs/config"
    16  	"launchpad.net/juju-core/environs/configstore"
    17  	envtesting "launchpad.net/juju-core/environs/testing"
    18  	"launchpad.net/juju-core/errors"
    19  	"launchpad.net/juju-core/provider/dummy"
    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 OpenSuite struct {
    26  	testbase.LoggingSuite
    27  	envtesting.ToolsFixture
    28  }
    29  
    30  var _ = gc.Suite(&OpenSuite{})
    31  
    32  func (*OpenSuite) TearDownTest(c *gc.C) {
    33  	dummy.Reset()
    34  }
    35  
    36  func (*OpenSuite) TestNewDummyEnviron(c *gc.C) {
    37  	// matches *Settings.Map()
    38  	cfg, err := config.New(config.NoDefaults, dummySampleConfig())
    39  	c.Assert(err, gc.IsNil)
    40  	ctx := testing.Context(c)
    41  	env, err := environs.Prepare(cfg, ctx, configstore.NewMem())
    42  	c.Assert(err, gc.IsNil)
    43  	envtesting.UploadFakeTools(c, env.Storage())
    44  	err = bootstrap.Bootstrap(ctx, env, constraints.Value{})
    45  	c.Assert(err, gc.IsNil)
    46  }
    47  
    48  func (*OpenSuite) TestNewUnknownEnviron(c *gc.C) {
    49  	attrs := dummySampleConfig().Merge(testing.Attrs{
    50  		"type": "wondercloud",
    51  	})
    52  	env, err := environs.NewFromAttrs(attrs)
    53  	c.Assert(err, gc.ErrorMatches, "no registered provider for.*")
    54  	c.Assert(env, gc.IsNil)
    55  }
    56  
    57  func (*OpenSuite) TestNewFromName(c *gc.C) {
    58  	defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore()
    59  	store := configstore.NewMem()
    60  	ctx := testing.Context(c)
    61  	e, err := environs.PrepareFromName("erewhemos", ctx, store)
    62  	c.Assert(err, gc.IsNil)
    63  
    64  	e, err = environs.NewFromName("erewhemos", store)
    65  	c.Assert(err, gc.IsNil)
    66  	c.Assert(e.Name(), gc.Equals, "erewhemos")
    67  }
    68  
    69  func (*OpenSuite) TestNewFromNameWithInvalidInfo(c *gc.C) {
    70  	defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore()
    71  	store := configstore.NewMem()
    72  	cfg, _, err := environs.ConfigForName("erewhemos", store)
    73  	c.Assert(err, gc.IsNil)
    74  	info, err := store.CreateInfo("erewhemos")
    75  	c.Assert(err, gc.IsNil)
    76  
    77  	// The configuration from environments.yaml is invalid
    78  	// because it doesn't contain the state-id attribute which
    79  	// the dummy environment adds at Prepare time.
    80  	info.SetBootstrapConfig(cfg.AllAttrs())
    81  	err = info.Write()
    82  	c.Assert(err, gc.IsNil)
    83  
    84  	e, err := environs.NewFromName("erewhemos", store)
    85  	c.Assert(err, gc.ErrorMatches, "environment is not prepared")
    86  	c.Assert(e, gc.IsNil)
    87  }
    88  
    89  func (*OpenSuite) TestNewFromNameWithInvalidEnvironConfig(c *gc.C) {
    90  	defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore()
    91  	store := configstore.NewMem()
    92  
    93  	e, err := environs.NewFromName("erewhemos", store)
    94  	c.Assert(err, gc.Equals, environs.ErrNotBootstrapped)
    95  	c.Assert(e, gc.IsNil)
    96  }
    97  
    98  func (*OpenSuite) TestPrepareFromName(c *gc.C) {
    99  	defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore()
   100  	ctx := testing.Context(c)
   101  	e, err := environs.PrepareFromName("erewhemos", ctx, configstore.NewMem())
   102  	c.Assert(err, gc.IsNil)
   103  	c.Assert(e.Name(), gc.Equals, "erewhemos")
   104  	// Check we can access storage ok, which implies the environment has been prepared.
   105  	c.Assert(e.Storage(), gc.NotNil)
   106  }
   107  
   108  func (*OpenSuite) TestConfigForName(c *gc.C) {
   109  	defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore()
   110  	cfg, source, err := environs.ConfigForName("erewhemos", configstore.NewMem())
   111  	c.Assert(err, gc.IsNil)
   112  	c.Assert(source, gc.Equals, environs.ConfigFromEnvirons)
   113  	c.Assert(cfg.Name(), gc.Equals, "erewhemos")
   114  }
   115  
   116  func (*OpenSuite) TestConfigForNameNoDefault(c *gc.C) {
   117  	defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore()
   118  	cfg, source, err := environs.ConfigForName("", configstore.NewMem())
   119  	c.Assert(err, gc.ErrorMatches, "no default environment found")
   120  	c.Assert(cfg, gc.IsNil)
   121  	c.Assert(source, gc.Equals, environs.ConfigFromEnvirons)
   122  }
   123  
   124  func (*OpenSuite) TestConfigForNameDefault(c *gc.C) {
   125  	defer testing.MakeFakeHome(c, testing.SingleEnvConfig, testing.SampleCertName).Restore()
   126  	cfg, source, err := environs.ConfigForName("", configstore.NewMem())
   127  	c.Assert(err, gc.IsNil)
   128  	c.Assert(cfg.Name(), gc.Equals, "erewhemos")
   129  	c.Assert(source, gc.Equals, environs.ConfigFromEnvirons)
   130  }
   131  
   132  func (*OpenSuite) TestConfigForNameFromInfo(c *gc.C) {
   133  	defer testing.MakeFakeHome(c, testing.SingleEnvConfig, testing.SampleCertName).Restore()
   134  	store := configstore.NewMem()
   135  	cfg, source, err := environs.ConfigForName("", store)
   136  	c.Assert(err, gc.IsNil)
   137  	c.Assert(source, gc.Equals, environs.ConfigFromEnvirons)
   138  
   139  	info, err := store.CreateInfo("test-config")
   140  	c.Assert(err, gc.IsNil)
   141  	var attrs testing.Attrs = cfg.AllAttrs()
   142  	attrs = attrs.Merge(testing.Attrs{
   143  		"name": "test-config",
   144  	})
   145  	info.SetBootstrapConfig(attrs)
   146  	err = info.Write()
   147  	c.Assert(err, gc.IsNil)
   148  
   149  	cfg, source, err = environs.ConfigForName("test-config", store)
   150  	c.Assert(err, gc.IsNil)
   151  	c.Assert(source, gc.Equals, environs.ConfigFromInfo)
   152  	c.Assert(testing.Attrs(cfg.AllAttrs()), gc.DeepEquals, attrs)
   153  }
   154  
   155  func (*OpenSuite) TestNew(c *gc.C) {
   156  	cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(
   157  		testing.Attrs{
   158  			"state-server": false,
   159  			"name":         "erewhemos",
   160  		},
   161  	))
   162  	c.Assert(err, gc.IsNil)
   163  	e, err := environs.New(cfg)
   164  	c.Assert(err, gc.ErrorMatches, "environment is not prepared")
   165  	c.Assert(e, gc.IsNil)
   166  }
   167  
   168  func (*OpenSuite) TestPrepare(c *gc.C) {
   169  	baselineAttrs := dummy.SampleConfig().Merge(testing.Attrs{
   170  		"state-server": false,
   171  		"name":         "erewhemos",
   172  	}).Delete(
   173  		"ca-cert",
   174  		"ca-private-key",
   175  		"admin-secret",
   176  	)
   177  	cfg, err := config.New(config.NoDefaults, baselineAttrs)
   178  	c.Assert(err, gc.IsNil)
   179  	store := configstore.NewMem()
   180  	ctx := testing.Context(c)
   181  	env, err := environs.Prepare(cfg, ctx, store)
   182  	c.Assert(err, gc.IsNil)
   183  	// Check we can access storage ok, which implies the environment has been prepared.
   184  	c.Assert(env.Storage(), gc.NotNil)
   185  
   186  	// Check that the environment info file was correctly created.
   187  	info, err := store.ReadInfo("erewhemos")
   188  	c.Assert(err, gc.IsNil)
   189  	c.Assert(info.Initialized(), jc.IsTrue)
   190  	c.Assert(info.BootstrapConfig(), gc.DeepEquals, env.Config().AllAttrs())
   191  	c.Logf("bootstrap config: %#v", info.BootstrapConfig())
   192  
   193  	// Check that an admin-secret was chosen.
   194  	adminSecret := env.Config().AdminSecret()
   195  	c.Assert(adminSecret, gc.HasLen, 32)
   196  	c.Assert(adminSecret, gc.Matches, "^[0-9a-f]*$")
   197  
   198  	// Check that the CA cert was generated.
   199  	cfgCertPEM, cfgCertOK := env.Config().CACert()
   200  	cfgKeyPEM, cfgKeyOK := env.Config().CAPrivateKey()
   201  	c.Assert(cfgCertOK, gc.Equals, true)
   202  	c.Assert(cfgKeyOK, gc.Equals, true)
   203  
   204  	// Check the common name of the generated cert
   205  	caCert, _, err := cert.ParseCertAndKey(cfgCertPEM, cfgKeyPEM)
   206  	c.Assert(err, gc.IsNil)
   207  	c.Assert(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for environment "`+testing.SampleEnvName+`"`)
   208  
   209  	// Check we can call Prepare again.
   210  	env, err = environs.Prepare(cfg, ctx, store)
   211  	c.Assert(err, gc.IsNil)
   212  	c.Assert(env.Name(), gc.Equals, "erewhemos")
   213  	c.Assert(env.Storage(), gc.NotNil)
   214  	c.Assert(env.Config().AllAttrs(), gc.DeepEquals, info.BootstrapConfig())
   215  }
   216  
   217  func (*OpenSuite) TestPrepareGeneratesDifferentAdminSecrets(c *gc.C) {
   218  	baselineAttrs := dummy.SampleConfig().Merge(testing.Attrs{
   219  		"state-server": false,
   220  		"name":         "erewhemos",
   221  	}).Delete(
   222  		"admin-secret",
   223  	)
   224  	cfg, err := config.New(config.NoDefaults, baselineAttrs)
   225  	c.Assert(err, gc.IsNil)
   226  
   227  	ctx := testing.Context(c)
   228  	env0, err := environs.Prepare(cfg, ctx, configstore.NewMem())
   229  	c.Assert(err, gc.IsNil)
   230  	adminSecret0 := env0.Config().AdminSecret()
   231  	c.Assert(adminSecret0, gc.HasLen, 32)
   232  	c.Assert(adminSecret0, gc.Matches, "^[0-9a-f]*$")
   233  
   234  	env1, err := environs.Prepare(cfg, ctx, configstore.NewMem())
   235  	c.Assert(err, gc.IsNil)
   236  	adminSecret1 := env1.Config().AdminSecret()
   237  	c.Assert(adminSecret1, gc.HasLen, 32)
   238  	c.Assert(adminSecret1, gc.Matches, "^[0-9a-f]*$")
   239  
   240  	c.Assert(adminSecret1, gc.Not(gc.Equals), adminSecret0)
   241  }
   242  
   243  func (*OpenSuite) TestPrepareWithMissingKey(c *gc.C) {
   244  	cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Delete("ca-cert", "ca-private-key").Merge(
   245  		testing.Attrs{
   246  			"state-server": false,
   247  			"name":         "erewhemos",
   248  			"ca-cert":      string(testing.CACert),
   249  		},
   250  	))
   251  	c.Assert(err, gc.IsNil)
   252  	store := configstore.NewMem()
   253  	env, err := environs.Prepare(cfg, testing.Context(c), store)
   254  	c.Assert(err, gc.ErrorMatches, "cannot ensure CA certificate: environment configuration with a certificate but no CA private key")
   255  	c.Assert(env, gc.IsNil)
   256  	// Ensure that the config storage info is cleaned up.
   257  	_, err = store.ReadInfo(cfg.Name())
   258  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   259  }
   260  
   261  func (*OpenSuite) TestPrepareWithExistingKeyPair(c *gc.C) {
   262  	cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(
   263  		testing.Attrs{
   264  			"state-server":   false,
   265  			"name":           "erewhemos",
   266  			"ca-cert":        string(testing.CACert),
   267  			"ca-private-key": string(testing.CAKey),
   268  		},
   269  	))
   270  	c.Assert(err, gc.IsNil)
   271  	ctx := testing.Context(c)
   272  	env, err := environs.Prepare(cfg, ctx, configstore.NewMem())
   273  	c.Assert(err, gc.IsNil)
   274  	cfgCertPEM, cfgCertOK := env.Config().CACert()
   275  	cfgKeyPEM, cfgKeyOK := env.Config().CAPrivateKey()
   276  	c.Assert(cfgCertOK, gc.Equals, true)
   277  	c.Assert(cfgKeyOK, gc.Equals, true)
   278  	c.Assert(string(cfgCertPEM), gc.DeepEquals, testing.CACert)
   279  	c.Assert(string(cfgKeyPEM), gc.DeepEquals, testing.CAKey)
   280  }
   281  
   282  func (*OpenSuite) TestDestroy(c *gc.C) {
   283  	cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(
   284  		testing.Attrs{
   285  			"state-server": false,
   286  			"name":         "erewhemos",
   287  		},
   288  	))
   289  	c.Assert(err, gc.IsNil)
   290  
   291  	store := configstore.NewMem()
   292  	// Prepare the environment and sanity-check that
   293  	// the config storage info has been made.
   294  	ctx := testing.Context(c)
   295  	e, err := environs.Prepare(cfg, ctx, store)
   296  	c.Assert(err, gc.IsNil)
   297  	_, err = store.ReadInfo(e.Name())
   298  	c.Assert(err, gc.IsNil)
   299  
   300  	err = environs.Destroy(e, store)
   301  	c.Assert(err, gc.IsNil)
   302  
   303  	// Check that the environment has actually been destroyed
   304  	// and that the config info has been destroyed too.
   305  	_, _, err = e.StateInfo()
   306  	c.Assert(err, gc.ErrorMatches, "environment has been destroyed")
   307  	_, err = store.ReadInfo(e.Name())
   308  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   309  }
   310  
   311  func (*OpenSuite) TestNewFromAttrs(c *gc.C) {
   312  	e, err := environs.NewFromAttrs(dummy.SampleConfig().Merge(
   313  		testing.Attrs{
   314  			"state-server": false,
   315  			"name":         "erewhemos",
   316  		},
   317  	))
   318  	c.Assert(err, gc.ErrorMatches, "environment is not prepared")
   319  	c.Assert(e, gc.IsNil)
   320  }
   321  
   322  const checkEnv = `
   323  environments:
   324      test:
   325          type: dummy
   326          state-server: false
   327          authorized-keys: i-am-a-key
   328  `
   329  
   330  type checkEnvironmentSuite struct{}
   331  
   332  var _ = gc.Suite(&checkEnvironmentSuite{})
   333  
   334  func (s *checkEnvironmentSuite) TearDownTest(c *gc.C) {
   335  	dummy.Reset()
   336  }
   337  
   338  func (s *checkEnvironmentSuite) TestCheckEnvironment(c *gc.C) {
   339  	defer testing.MakeFakeHome(c, checkEnv, "existing").Restore()
   340  
   341  	ctx := testing.Context(c)
   342  	environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem())
   343  	c.Assert(err, gc.IsNil)
   344  
   345  	// VerifyStorage is sufficient for our tests and much simpler
   346  	// than Bootstrap which calls it.
   347  	stor := environ.Storage()
   348  	err = environs.VerifyStorage(stor)
   349  	c.Assert(err, gc.IsNil)
   350  	err = environs.CheckEnvironment(environ)
   351  	c.Assert(err, gc.IsNil)
   352  }
   353  
   354  func (s *checkEnvironmentSuite) TestCheckEnvironmentFileNotFound(c *gc.C) {
   355  	defer testing.MakeFakeHome(c, checkEnv, "existing").Restore()
   356  
   357  	ctx := testing.Context(c)
   358  	environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem())
   359  	c.Assert(err, gc.IsNil)
   360  
   361  	// VerifyStorage is sufficient for our tests and much simpler
   362  	// than Bootstrap which calls it.
   363  	stor := environ.Storage()
   364  	err = environs.VerifyStorage(stor)
   365  	c.Assert(err, gc.IsNil)
   366  
   367  	// When the bootstrap-verify file does not exist, it still believes
   368  	// the environment is a juju-core one because earlier versions
   369  	// did not create that file.
   370  	err = stor.Remove(environs.VerificationFilename)
   371  	c.Assert(err, gc.IsNil)
   372  	err = environs.CheckEnvironment(environ)
   373  	c.Assert(err, gc.IsNil)
   374  }
   375  
   376  func (s *checkEnvironmentSuite) TestCheckEnvironmentGetFails(c *gc.C) {
   377  	defer testing.MakeFakeHome(c, checkEnv, "existing").Restore()
   378  
   379  	ctx := testing.Context(c)
   380  	environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem())
   381  	c.Assert(err, gc.IsNil)
   382  
   383  	// VerifyStorage is sufficient for our tests and much simpler
   384  	// than Bootstrap which calls it.
   385  	stor := environ.Storage()
   386  	err = environs.VerifyStorage(stor)
   387  	c.Assert(err, gc.IsNil)
   388  
   389  	// When fetching the verification file from storage fails,
   390  	// we get an InvalidEnvironmentError.
   391  	someError := errors.Unauthorizedf("you shall not pass")
   392  	dummy.Poison(stor, environs.VerificationFilename, someError)
   393  	err = environs.CheckEnvironment(environ)
   394  	c.Assert(err, gc.Equals, someError)
   395  }
   396  
   397  func (s *checkEnvironmentSuite) TestCheckEnvironmentBadContent(c *gc.C) {
   398  	defer testing.MakeFakeHome(c, checkEnv, "existing").Restore()
   399  
   400  	ctx := testing.Context(c)
   401  	environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem())
   402  	c.Assert(err, gc.IsNil)
   403  
   404  	// We mock a bad (eg. from a Python-juju environment) bootstrap-verify.
   405  	stor := environ.Storage()
   406  	content := "bad verification content"
   407  	reader := strings.NewReader(content)
   408  	err = stor.Put(environs.VerificationFilename, reader, int64(len(content)))
   409  	c.Assert(err, gc.IsNil)
   410  
   411  	// When the bootstrap-verify file contains unexpected content,
   412  	// we get an InvalidEnvironmentError.
   413  	err = environs.CheckEnvironment(environ)
   414  	c.Assert(err, gc.Equals, environs.InvalidEnvironmentError)
   415  }