github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/juju/system/createenvironment_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package system_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"os/user"
    10  
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/errors"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/yaml.v1"
    17  
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/cmd/envcmd"
    20  	"github.com/juju/juju/cmd/juju/system"
    21  	"github.com/juju/juju/environs/configstore"
    22  	"github.com/juju/juju/feature"
    23  	"github.com/juju/juju/testing"
    24  )
    25  
    26  type createSuite struct {
    27  	testing.FakeJujuHomeSuite
    28  	fake       *fakeCreateClient
    29  	parser     func(interface{}) (interface{}, error)
    30  	store      configstore.Storage
    31  	serverUUID string
    32  	server     configstore.EnvironInfo
    33  }
    34  
    35  var _ = gc.Suite(&createSuite{})
    36  
    37  func (s *createSuite) SetUpTest(c *gc.C) {
    38  	s.FakeJujuHomeSuite.SetUpTest(c)
    39  	s.SetFeatureFlags(feature.JES)
    40  	s.fake = &fakeCreateClient{}
    41  	s.parser = nil
    42  	store := configstore.Default
    43  	s.AddCleanup(func(*gc.C) {
    44  		configstore.Default = store
    45  	})
    46  	s.store = configstore.NewMem()
    47  	configstore.Default = func() (configstore.Storage, error) {
    48  		return s.store, nil
    49  	}
    50  	// Set up the current environment, and write just enough info
    51  	// so we don't try to refresh
    52  	envName := "test-master"
    53  	s.serverUUID = "fake-server-uuid"
    54  	info := s.store.CreateInfo(envName)
    55  	info.SetAPIEndpoint(configstore.APIEndpoint{
    56  		Addresses:   []string{"localhost"},
    57  		CACert:      testing.CACert,
    58  		EnvironUUID: s.serverUUID,
    59  		ServerUUID:  s.serverUUID,
    60  	})
    61  	info.SetAPICredentials(configstore.APICredentials{User: "bob", Password: "sekrit"})
    62  	err := info.Write()
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	s.server = info
    65  	err = envcmd.WriteCurrentEnvironment(envName)
    66  	c.Assert(err, jc.ErrorIsNil)
    67  }
    68  
    69  func (s *createSuite) run(c *gc.C, args ...string) (*cmd.Context, error) {
    70  	command := system.NewCreateEnvironmentCommand(s.fake, s.parser)
    71  	return testing.RunCommand(c, envcmd.WrapSystem(command), args...)
    72  }
    73  
    74  func (s *createSuite) TestInit(c *gc.C) {
    75  
    76  	for i, test := range []struct {
    77  		args   []string
    78  		err    string
    79  		name   string
    80  		owner  string
    81  		path   string
    82  		values map[string]string
    83  	}{
    84  		{
    85  			err: "environment name is required",
    86  		}, {
    87  			args: []string{"new-env"},
    88  			name: "new-env",
    89  		}, {
    90  			args:  []string{"new-env", "--owner", "foo"},
    91  			name:  "new-env",
    92  			owner: "foo",
    93  		}, {
    94  			args: []string{"new-env", "--owner", "not=valid"},
    95  			err:  `"not=valid" is not a valid user`,
    96  		}, {
    97  			args:   []string{"new-env", "key=value", "key2=value2"},
    98  			name:   "new-env",
    99  			values: map[string]string{"key": "value", "key2": "value2"},
   100  		}, {
   101  			args: []string{"new-env", "key=value", "key=value2"},
   102  			err:  `key "key" specified more than once`,
   103  		}, {
   104  			args: []string{"new-env", "another"},
   105  			err:  `expected "key=value", got "another"`,
   106  		}, {
   107  			args: []string{"new-env", "--config", "some-file"},
   108  			name: "new-env",
   109  			path: "some-file",
   110  		},
   111  	} {
   112  		c.Logf("test %d", i)
   113  		create := &system.CreateEnvironmentCommand{}
   114  		err := testing.InitCommand(create, test.args)
   115  		if test.err != "" {
   116  			c.Assert(err, gc.ErrorMatches, test.err)
   117  			continue
   118  		}
   119  
   120  		c.Assert(err, jc.ErrorIsNil)
   121  		c.Assert(create.Name(), gc.Equals, test.name)
   122  		c.Assert(create.Owner(), gc.Equals, test.owner)
   123  		c.Assert(create.ConfigFile().Path, gc.Equals, test.path)
   124  		// The config value parse method returns an empty map
   125  		// if there were no values
   126  		if len(test.values) == 0 {
   127  			c.Assert(create.ConfValues(), gc.HasLen, 0)
   128  		} else {
   129  			c.Assert(create.ConfValues(), jc.DeepEquals, test.values)
   130  		}
   131  	}
   132  }
   133  
   134  func (s *createSuite) TestCreateExistingName(c *gc.C) {
   135  	// Make a configstore entry with the same name.
   136  	info := s.store.CreateInfo("test")
   137  	err := info.Write()
   138  	c.Assert(err, jc.ErrorIsNil)
   139  
   140  	_, err = s.run(c, "test")
   141  	c.Assert(err, gc.ErrorMatches, `environment "test" already exists`)
   142  }
   143  
   144  func (s *createSuite) TestComandLineConfigPassedThrough(c *gc.C) {
   145  	_, err := s.run(c, "test", "account=magic", "cloud=special")
   146  	c.Assert(err, jc.ErrorIsNil)
   147  
   148  	c.Assert(s.fake.config["account"], gc.Equals, "magic")
   149  	c.Assert(s.fake.config["cloud"], gc.Equals, "special")
   150  }
   151  
   152  func (s *createSuite) TestConfigFileValuesPassedThrough(c *gc.C) {
   153  	config := map[string]string{
   154  		"account": "magic",
   155  		"cloud":   "9",
   156  	}
   157  	bytes, err := yaml.Marshal(config)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	file, err := ioutil.TempFile(c.MkDir(), "")
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	file.Write(bytes)
   162  	file.Close()
   163  
   164  	_, err = s.run(c, "test", "--config", file.Name())
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(s.fake.config["account"], gc.Equals, "magic")
   167  	c.Assert(s.fake.config["cloud"], gc.Equals, "9")
   168  }
   169  
   170  func (s *createSuite) TestConfigFileWithNestedMaps(c *gc.C) {
   171  	nestedConfig := map[string]interface{}{
   172  		"account": "magic",
   173  		"cloud":   "9",
   174  	}
   175  	config := map[string]interface{}{
   176  		"foo":    "bar",
   177  		"nested": nestedConfig,
   178  	}
   179  
   180  	bytes, err := yaml.Marshal(config)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	file, err := ioutil.TempFile(c.MkDir(), "")
   183  	c.Assert(err, jc.ErrorIsNil)
   184  	file.Write(bytes)
   185  	file.Close()
   186  
   187  	_, err = s.run(c, "test", "--config", file.Name())
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	c.Assert(s.fake.config["foo"], gc.Equals, "bar")
   190  	c.Assert(s.fake.config["nested"], jc.DeepEquals, nestedConfig)
   191  }
   192  
   193  func (s *createSuite) TestConfigFileFailsToConform(c *gc.C) {
   194  	nestedConfig := map[int]interface{}{
   195  		9: "9",
   196  	}
   197  	config := map[string]interface{}{
   198  		"foo":    "bar",
   199  		"nested": nestedConfig,
   200  	}
   201  	bytes, err := yaml.Marshal(config)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	file, err := ioutil.TempFile(c.MkDir(), "")
   204  	c.Assert(err, jc.ErrorIsNil)
   205  	file.Write(bytes)
   206  	file.Close()
   207  
   208  	_, err = s.run(c, "test", "--config", file.Name())
   209  	c.Assert(err, gc.ErrorMatches, `unable to parse config file: map keyed with non-string value`)
   210  }
   211  
   212  func (s *createSuite) TestConfigFileFailsWithUnknownType(c *gc.C) {
   213  	config := map[string]interface{}{
   214  		"account": "magic",
   215  		"cloud":   "9",
   216  	}
   217  
   218  	bytes, err := yaml.Marshal(config)
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	file, err := ioutil.TempFile(c.MkDir(), "")
   221  	c.Assert(err, jc.ErrorIsNil)
   222  	file.Write(bytes)
   223  	file.Close()
   224  
   225  	s.parser = func(interface{}) (interface{}, error) { return "not a map", nil }
   226  	_, err = s.run(c, "test", "--config", file.Name())
   227  	c.Assert(err, gc.ErrorMatches, `config must contain a YAML map with string keys`)
   228  }
   229  
   230  func (s *createSuite) TestConfigFileFormatError(c *gc.C) {
   231  	file, err := ioutil.TempFile(c.MkDir(), "")
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	file.Write(([]byte)("not: valid: yaml"))
   234  	file.Close()
   235  
   236  	_, err = s.run(c, "test", "--config", file.Name())
   237  	c.Assert(err, gc.ErrorMatches, `unable to parse config file: YAML error: .*`)
   238  }
   239  
   240  func (s *createSuite) TestConfigFileDoesntExist(c *gc.C) {
   241  	_, err := s.run(c, "test", "--config", "missing-file")
   242  	errMsg := ".*" + utils.NoSuchFileErrRegexp
   243  	c.Assert(err, gc.ErrorMatches, errMsg)
   244  }
   245  
   246  func (s *createSuite) TestConfigValuePrecedence(c *gc.C) {
   247  	config := map[string]string{
   248  		"account": "magic",
   249  		"cloud":   "9",
   250  	}
   251  	bytes, err := yaml.Marshal(config)
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	file, err := ioutil.TempFile(c.MkDir(), "")
   254  	c.Assert(err, jc.ErrorIsNil)
   255  	file.Write(bytes)
   256  	file.Close()
   257  
   258  	_, err = s.run(c, "test", "--config", file.Name(), "account=magic", "cloud=special")
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	c.Assert(s.fake.config["account"], gc.Equals, "magic")
   261  	c.Assert(s.fake.config["cloud"], gc.Equals, "special")
   262  }
   263  
   264  var setConfigSpecialCaseDefaultsTests = []struct {
   265  	about        string
   266  	userEnvVar   string
   267  	userCurrent  func() (*user.User, error)
   268  	config       map[string]interface{}
   269  	expectConfig map[string]interface{}
   270  	expectError  string
   271  }{{
   272  	about:      "use env var if available",
   273  	userEnvVar: "bob",
   274  	config: map[string]interface{}{
   275  		"name": "envname",
   276  		"type": "local",
   277  	},
   278  	expectConfig: map[string]interface{}{
   279  		"name":      "envname",
   280  		"type":      "local",
   281  		"namespace": "bob-envname",
   282  	},
   283  }, {
   284  	about: "fall back to user.Current",
   285  	userCurrent: func() (*user.User, error) {
   286  		return &user.User{Username: "bob"}, nil
   287  	},
   288  	config: map[string]interface{}{
   289  		"name": "envname",
   290  		"type": "local",
   291  	},
   292  	expectConfig: map[string]interface{}{
   293  		"name":      "envname",
   294  		"type":      "local",
   295  		"namespace": "bob-envname",
   296  	},
   297  }, {
   298  	about:      "other provider types unaffected",
   299  	userEnvVar: "bob",
   300  	config: map[string]interface{}{
   301  		"name": "envname",
   302  		"type": "dummy",
   303  	},
   304  	expectConfig: map[string]interface{}{
   305  		"name": "envname",
   306  		"type": "dummy",
   307  	},
   308  }, {
   309  	about: "explicit namespace takes precedence",
   310  	userCurrent: func() (*user.User, error) {
   311  		return &user.User{Username: "bob"}, nil
   312  	},
   313  	config: map[string]interface{}{
   314  		"name":      "envname",
   315  		"namespace": "something",
   316  		"type":      "local",
   317  	},
   318  	expectConfig: map[string]interface{}{
   319  		"name":      "envname",
   320  		"namespace": "something",
   321  		"type":      "local",
   322  	},
   323  }, {
   324  	about: "user.Current returns error",
   325  	userCurrent: func() (*user.User, error) {
   326  		return nil, errors.New("an error")
   327  	},
   328  	config: map[string]interface{}{
   329  		"name": "envname",
   330  		"type": "local",
   331  	},
   332  	expectError: "failed to determine username for namespace: an error",
   333  }}
   334  
   335  func (s *createSuite) TestSetConfigSpecialCaseDefaults(c *gc.C) {
   336  	noUserCurrent := func() (*user.User, error) {
   337  		panic("should not be called")
   338  	}
   339  	s.PatchValue(system.UserCurrent, noUserCurrent)
   340  	// We test setConfigSpecialCaseDefaults independently
   341  	// because we can't use the local provider in the tests.
   342  	for i, test := range setConfigSpecialCaseDefaultsTests {
   343  		c.Logf("test %d: %s", i, test.about)
   344  		os.Setenv("USER", test.userEnvVar)
   345  		if test.userCurrent != nil {
   346  			*system.UserCurrent = test.userCurrent
   347  		} else {
   348  			*system.UserCurrent = noUserCurrent
   349  		}
   350  		err := system.SetConfigSpecialCaseDefaults(test.config["name"].(string), test.config)
   351  		if test.expectError != "" {
   352  			c.Assert(err, gc.ErrorMatches, test.expectError)
   353  		} else {
   354  			c.Assert(err, gc.IsNil)
   355  			c.Assert(test.config, jc.DeepEquals, test.expectConfig)
   356  		}
   357  	}
   358  
   359  }
   360  
   361  func (s *createSuite) TestCreateErrorRemoveConfigstoreInfo(c *gc.C) {
   362  	s.fake.err = errors.New("bah humbug")
   363  
   364  	_, err := s.run(c, "test")
   365  	c.Assert(err, gc.ErrorMatches, "bah humbug")
   366  
   367  	_, err = s.store.ReadInfo("test")
   368  	c.Assert(err, gc.ErrorMatches, `environment "test" not found`)
   369  }
   370  
   371  func (s *createSuite) TestCreateStoresValues(c *gc.C) {
   372  	s.fake.env = params.Environment{
   373  		Name:       "test",
   374  		UUID:       "fake-env-uuid",
   375  		OwnerTag:   "ignored-for-now",
   376  		ServerUUID: s.serverUUID,
   377  	}
   378  	_, err := s.run(c, "test")
   379  	c.Assert(err, jc.ErrorIsNil)
   380  
   381  	info, err := s.store.ReadInfo("test")
   382  	c.Assert(err, jc.ErrorIsNil)
   383  	// Stores the credentials of the original environment
   384  	c.Assert(info.APICredentials(), jc.DeepEquals, s.server.APICredentials())
   385  	endpoint := info.APIEndpoint()
   386  	expected := s.server.APIEndpoint()
   387  	c.Assert(endpoint.Addresses, jc.DeepEquals, expected.Addresses)
   388  	c.Assert(endpoint.Hostnames, jc.DeepEquals, expected.Hostnames)
   389  	c.Assert(endpoint.ServerUUID, gc.Equals, expected.ServerUUID)
   390  	c.Assert(endpoint.CACert, gc.Equals, expected.CACert)
   391  	c.Assert(endpoint.EnvironUUID, gc.Equals, "fake-env-uuid")
   392  }
   393  
   394  func (s *createSuite) TestNoEnvCacheOtherUser(c *gc.C) {
   395  	s.fake.env = params.Environment{
   396  		Name:       "test",
   397  		UUID:       "fake-env-uuid",
   398  		OwnerTag:   "ignored-for-now",
   399  		ServerUUID: s.serverUUID,
   400  	}
   401  	_, err := s.run(c, "test", "--owner", "zeus")
   402  	c.Assert(err, jc.ErrorIsNil)
   403  
   404  	_, err = s.store.ReadInfo("test")
   405  	c.Assert(err, gc.ErrorMatches, `environment "test" not found`)
   406  }
   407  
   408  // fakeCreateClient is used to mock out the behavior of the real
   409  // CreateEnvironment command.
   410  type fakeCreateClient struct {
   411  	owner   string
   412  	account map[string]interface{}
   413  	config  map[string]interface{}
   414  	err     error
   415  	env     params.Environment
   416  }
   417  
   418  var _ system.CreateEnvironmentAPI = (*fakeCreateClient)(nil)
   419  
   420  func (*fakeCreateClient) Close() error {
   421  	return nil
   422  }
   423  
   424  func (*fakeCreateClient) ConfigSkeleton(provider, region string) (params.EnvironConfig, error) {
   425  	return params.EnvironConfig{
   426  		"type":         "dummy",
   427  		"state-server": false,
   428  	}, nil
   429  }
   430  func (f *fakeCreateClient) CreateEnvironment(owner string, account, config map[string]interface{}) (params.Environment, error) {
   431  	var env params.Environment
   432  	if f.err != nil {
   433  		return env, f.err
   434  	}
   435  	f.owner = owner
   436  	f.account = account
   437  	f.config = config
   438  	return f.env, nil
   439  }