github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/environment/jenv_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environment_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	gitjujutesting "github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/yaml.v1"
    16  
    17  	"github.com/juju/juju/cmd/envcmd"
    18  	"github.com/juju/juju/cmd/juju/environment"
    19  	"github.com/juju/juju/environs/configstore"
    20  	"github.com/juju/juju/juju/osenv"
    21  	"github.com/juju/juju/testing"
    22  )
    23  
    24  type jenvSuite struct {
    25  	testing.FakeJujuHomeSuite
    26  }
    27  
    28  var _ = gc.Suite(&jenvSuite{})
    29  
    30  var jenvInitErrorsTests = []struct {
    31  	about string
    32  	args  []string
    33  	err   string
    34  }{{
    35  	about: "no args",
    36  	err:   "no jenv file provided",
    37  }, {
    38  	about: "invalid env name",
    39  	args:  []string{"path/to/jenv", "invalid/name"},
    40  	err:   `invalid environment name "invalid/name"`,
    41  }, {
    42  	about: "too many args",
    43  	args:  []string{"path/to/jenv", "env-name", "unexpected"},
    44  	err:   `unrecognized args: \["unexpected"\]`,
    45  }}
    46  
    47  func (*jenvSuite) TestInitErrors(c *gc.C) {
    48  	for i, test := range jenvInitErrorsTests {
    49  		c.Logf("test %d: %s", i, test.about)
    50  
    51  		jenvCmd := &environment.JenvCommand{}
    52  		err := testing.InitCommand(jenvCmd, test.args)
    53  		c.Assert(err, gc.ErrorMatches, test.err)
    54  	}
    55  }
    56  
    57  func (*jenvSuite) TestJenvFileNotFound(c *gc.C) {
    58  	jenvCmd := &environment.JenvCommand{}
    59  	ctx, err := testing.RunCommand(c, jenvCmd, "/no/such/file.jenv")
    60  	c.Assert(err, gc.ErrorMatches, `jenv file "/no/such/file.jenv" not found`)
    61  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
    62  }
    63  
    64  func (*jenvSuite) TestJenvFileDirectory(c *gc.C) {
    65  	jenvCmd := &environment.JenvCommand{}
    66  	ctx, err := testing.RunCommand(c, jenvCmd, c.MkDir())
    67  
    68  	// The error is different on some platforms
    69  	c.Assert(err, gc.ErrorMatches, "cannot read the provided jenv file .*: (is a directory|The handle is invalid.)")
    70  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
    71  }
    72  
    73  func (*jenvSuite) TestJenvFileNotReadable(c *gc.C) {
    74  	if runtime.GOOS == "windows" {
    75  		c.Skip("Cannot test on windows because it uses chmod")
    76  	}
    77  	// Create a read-only jenv file.
    78  	f := openJenvFile(c, nil)
    79  	defer f.Close()
    80  	err := f.Chmod(0)
    81  	c.Assert(err, jc.ErrorIsNil)
    82  
    83  	// Run the command.
    84  	jenvCmd := &environment.JenvCommand{}
    85  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
    86  	c.Assert(err, gc.ErrorMatches, "cannot read the provided jenv file .* permission denied")
    87  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
    88  }
    89  
    90  var jenvFileContentErrorsTests = []struct {
    91  	about    string
    92  	contents []byte
    93  	err      string
    94  }{{
    95  	about: "empty",
    96  	err:   "invalid jenv file .*: missing required fields in jenv data: User, Password, EnvironUUID, StateServers, CACert",
    97  }, {
    98  	about:    "invalid YAML",
    99  	contents: []byte(":"),
   100  	err:      "invalid jenv file .*: cannot unmarshal jenv data: YAML error: .*",
   101  }, {
   102  	about:    "missing field",
   103  	contents: makeJenvContents("myuser", "mypasswd", "env-uuid", "", "1.2.3.4:17070"),
   104  	err:      "invalid jenv file .*: missing required fields in jenv data: CACert",
   105  }}
   106  
   107  func (*jenvSuite) TestJenvFileContentErrors(c *gc.C) {
   108  	for i, test := range jenvFileContentErrorsTests {
   109  		c.Logf("test %d: %s", i, test.about)
   110  
   111  		// Create the jenv file with the contents provided by the test.
   112  		f := openJenvFile(c, test.contents)
   113  		defer f.Close()
   114  
   115  		jenvCmd := &environment.JenvCommand{}
   116  		ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   117  		c.Assert(err, gc.ErrorMatches, test.err)
   118  		c.Assert(testing.Stdout(ctx), gc.Equals, "")
   119  	}
   120  }
   121  
   122  func (*jenvSuite) TestConfigStoreError(c *gc.C) {
   123  	if runtime.GOOS == "windows" {
   124  		c.Skip("Cannot test on windows because it uses chmod")
   125  	}
   126  	// Create a jenv file.
   127  	f := openJenvFile(c, nil)
   128  	defer f.Close()
   129  
   130  	// Remove Juju home read permissions.
   131  	home := gitjujutesting.HomePath(".juju")
   132  	err := os.Chmod(home, 0)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	defer os.Chmod(home, 0700)
   135  
   136  	jenvCmd := &environment.JenvCommand{}
   137  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   138  	c.Assert(err, gc.ErrorMatches, "cannot get config store: .*: permission denied")
   139  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
   140  }
   141  
   142  func (*jenvSuite) TestWriteError(c *gc.C) {
   143  	if runtime.GOOS == "windows" {
   144  		c.Skip("Cannot test on windows because it uses chmod")
   145  	}
   146  	// Create a jenv file.
   147  	f := openJenvFile(c, makeValidJenvContents())
   148  	defer f.Close()
   149  
   150  	// Create the environments dir without write permissions.
   151  	envsDir := gitjujutesting.HomePath(".juju", "environments")
   152  	err := os.Mkdir(envsDir, 0500)
   153  	c.Assert(err, jc.ErrorIsNil)
   154  
   155  	jenvCmd := &environment.JenvCommand{}
   156  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   157  	c.Assert(err, gc.ErrorMatches, "cannot write the jenv file: cannot write info: .*: permission denied")
   158  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
   159  }
   160  
   161  func (*jenvSuite) TestSwitchErrorJujuEnvSet(c *gc.C) {
   162  	// Create a jenv file.
   163  	f := openJenvFile(c, makeValidJenvContents())
   164  	defer f.Close()
   165  
   166  	// Override the default Juju environment with the environment variable.
   167  	err := os.Setenv(osenv.JujuEnvEnvKey, "ec2")
   168  	c.Assert(err, jc.ErrorIsNil)
   169  
   170  	jenvCmd := &environment.JenvCommand{}
   171  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   172  	c.Assert(err, gc.ErrorMatches, `cannot switch to the new environment "testing": cannot switch when JUJU_ENV is overriding the environment \(set to "ec2"\)`)
   173  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
   174  }
   175  
   176  func (*jenvSuite) TestSwitchErrorEnvironmentsNotReadable(c *gc.C) {
   177  	if runtime.GOOS == "windows" {
   178  		c.Skip("Cannot test on windows because it uses chmod")
   179  	}
   180  	// Create a jenv file.
   181  	f := openJenvFile(c, makeValidJenvContents())
   182  	defer f.Close()
   183  
   184  	// Remove write permissions to the environments.yaml file.
   185  	envPath := gitjujutesting.HomePath(".juju", "environments.yaml")
   186  	err := os.Chmod(envPath, 0200)
   187  	c.Assert(err, jc.ErrorIsNil)
   188  
   189  	jenvCmd := &environment.JenvCommand{}
   190  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   191  	c.Assert(err, gc.ErrorMatches, `cannot switch to the new environment "testing": cannot get the default environment: .*: permission denied`)
   192  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
   193  }
   194  
   195  func (*jenvSuite) TestSwitchErrorCannotWriteCurrentEnvironment(c *gc.C) {
   196  	if runtime.GOOS == "windows" {
   197  		c.Skip("Cannot test on windows because it uses chmod")
   198  	}
   199  	// Create a jenv file.
   200  	f := openJenvFile(c, makeValidJenvContents())
   201  	defer f.Close()
   202  
   203  	// Create the current environment file without write permissions.
   204  	currentEnvPath := gitjujutesting.HomePath(".juju", envcmd.CurrentEnvironmentFilename)
   205  	currentEnvFile, err := os.Create(currentEnvPath)
   206  	c.Assert(err, jc.ErrorIsNil)
   207  	defer currentEnvFile.Close()
   208  	err = currentEnvFile.Chmod(0500)
   209  	c.Assert(err, jc.ErrorIsNil)
   210  
   211  	jenvCmd := &environment.JenvCommand{}
   212  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   213  	c.Assert(err, gc.ErrorMatches, `cannot switch to the new environment "testing": unable to write to the environment file: .*: permission denied`)
   214  	c.Assert(testing.Stdout(ctx), gc.Equals, "")
   215  }
   216  
   217  func (*jenvSuite) TestSuccess(c *gc.C) {
   218  	// Create a jenv file.
   219  	contents := makeJenvContents("who", "Secret!", "env-UUID", testing.CACert, "1.2.3.4:17070")
   220  	f := openJenvFile(c, contents)
   221  	defer f.Close()
   222  
   223  	// Import the newly created jenv file.
   224  	jenvCmd := &environment.JenvCommand{}
   225  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   226  	c.Assert(err, jc.ErrorIsNil)
   227  
   228  	// The jenv file has been properly imported.
   229  	assertJenvContents(c, contents, "testing")
   230  
   231  	// The default environment is now the newly imported one, and the output
   232  	// reflects the change.
   233  	currEnv, err := envcmd.ReadCurrentEnvironment()
   234  	c.Assert(err, jc.ErrorIsNil)
   235  	c.Assert(currEnv, gc.Equals, "testing")
   236  	c.Assert(testing.Stdout(ctx), gc.Equals, "erewhemos -> testing\n")
   237  
   238  	// Trying to import the jenv with the same name a second time raises an
   239  	// error.
   240  	jenvCmd = &environment.JenvCommand{}
   241  	ctx, err = testing.RunCommand(c, jenvCmd, f.Name())
   242  	c.Assert(err, gc.ErrorMatches, `an environment named "testing" already exists: you can provide a second parameter to rename the environment`)
   243  
   244  	// Overriding the environment name solves the problem.
   245  	jenvCmd = &environment.JenvCommand{}
   246  	ctx, err = testing.RunCommand(c, jenvCmd, f.Name(), "another")
   247  	c.Assert(err, jc.ErrorIsNil)
   248  	assertJenvContents(c, contents, "another")
   249  
   250  	currEnv, err = envcmd.ReadCurrentEnvironment()
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	c.Assert(currEnv, gc.Equals, "another")
   253  	c.Assert(testing.Stdout(ctx), gc.Equals, "testing -> another\n")
   254  }
   255  
   256  func (*jenvSuite) TestSuccessCustomEnvironmentName(c *gc.C) {
   257  	// Create a jenv file.
   258  	contents := makeValidJenvContents()
   259  	f := openJenvFile(c, contents)
   260  	defer f.Close()
   261  
   262  	// Import the newly created jenv file with a customized name.
   263  	jenvCmd := &environment.JenvCommand{}
   264  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name(), "my-env")
   265  	c.Assert(err, jc.ErrorIsNil)
   266  
   267  	// The jenv file has been properly imported.
   268  	assertJenvContents(c, contents, "my-env")
   269  
   270  	// The default environment is now the newly imported one, and the output
   271  	// reflects the change.
   272  	currEnv, err := envcmd.ReadCurrentEnvironment()
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	c.Assert(currEnv, gc.Equals, "my-env")
   275  	c.Assert(testing.Stdout(ctx), gc.Equals, "erewhemos -> my-env\n")
   276  }
   277  
   278  func (*jenvSuite) TestSuccessNoJujuEnvironments(c *gc.C) {
   279  	// Create a jenv file.
   280  	contents := makeValidJenvContents()
   281  	f := openJenvFile(c, contents)
   282  	defer f.Close()
   283  
   284  	// Remove the Juju environments.yaml file.
   285  	envPath := gitjujutesting.HomePath(".juju", "environments.yaml")
   286  	err := os.Remove(envPath)
   287  	c.Assert(err, jc.ErrorIsNil)
   288  
   289  	// Importing a jenv does not require having environments already defined.
   290  	jenvCmd := &environment.JenvCommand{}
   291  	ctx, err := testing.RunCommand(c, jenvCmd, f.Name())
   292  	c.Assert(err, jc.ErrorIsNil)
   293  	assertJenvContents(c, contents, "testing")
   294  	c.Assert(testing.Stdout(ctx), gc.Equals, "-> testing\n")
   295  }
   296  
   297  // openJenvFile creates and returns a jenv file with the given contents.
   298  // Callers are responsible of closing the file.
   299  func openJenvFile(c *gc.C, contents []byte) *os.File {
   300  	path := filepath.Join(c.MkDir(), "testing.jenv")
   301  	f, err := os.Create(path)
   302  	c.Assert(err, jc.ErrorIsNil)
   303  	_, err = f.Write(contents)
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	return f
   306  }
   307  
   308  // makeJenvContents returns a YAML encoded environment info data.
   309  func makeJenvContents(user, password, environUUID, caCert string, stateServers ...string) []byte {
   310  	b, err := yaml.Marshal(configstore.EnvironInfoData{
   311  		User:         user,
   312  		Password:     password,
   313  		EnvironUUID:  environUUID,
   314  		CACert:       caCert,
   315  		StateServers: stateServers,
   316  	})
   317  	if err != nil {
   318  		panic(err)
   319  	}
   320  	return b
   321  }
   322  
   323  // makeValidJenvContents returns valid jenv file YAML encoded contents.
   324  func makeValidJenvContents() []byte {
   325  	return makeJenvContents(
   326  		"myuser", "mypasswd", "env-uuid", testing.CACert,
   327  		"1.2.3.4:17070", "example.com:17070")
   328  }
   329  
   330  // assertJenvContents checks that the jenv file corresponding to the given
   331  // envName is correctly present in the Juju Home and has the given contents.
   332  func assertJenvContents(c *gc.C, contents []byte, envName string) {
   333  	path := gitjujutesting.HomePath(".juju", "environments", envName+".jenv")
   334  	// Ensure the jenv file has been created.
   335  	c.Assert(path, jc.IsNonEmptyFile)
   336  
   337  	// Retrieve the jenv file contents.
   338  	b, err := ioutil.ReadFile(path)
   339  	c.Assert(err, jc.ErrorIsNil)
   340  
   341  	// Ensure the jenv file reflects the source contents.
   342  	var data map[string]interface{}
   343  	err = yaml.Unmarshal(contents, &data)
   344  	c.Assert(err, jc.ErrorIsNil)
   345  	c.Assert(string(b), jc.YAMLEquals, data)
   346  }