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