github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/model/setcredential_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for info.
     3  
     4  package model_test
     5  
     6  import (
     7  	"regexp"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/cmd/cmdtesting"
    11  	"github.com/juju/errors"
    12  	jujutesting "github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/names.v2"
    16  
    17  	"github.com/juju/juju/api/base"
    18  	"github.com/juju/juju/cloud"
    19  	"github.com/juju/juju/cmd/juju/model"
    20  	coremodel "github.com/juju/juju/core/model"
    21  	"github.com/juju/juju/jujuclient"
    22  	_ "github.com/juju/juju/provider/ec2" // needed when getting valid local credentials
    23  	"github.com/juju/juju/testing"
    24  )
    25  
    26  var _ = gc.Suite(&ModelCredentialCommandSuite{})
    27  
    28  type ModelCredentialCommandSuite struct {
    29  	jujutesting.IsolationSuite
    30  
    31  	store *jujuclient.MemStore
    32  
    33  	modelClient fakeModelClient
    34  	cloudClient fakeCloudClient
    35  	rootFunc    func() (base.APICallCloser, error)
    36  }
    37  
    38  func (s *ModelCredentialCommandSuite) SetUpTest(c *gc.C) {
    39  	s.IsolationSuite.SetUpTest(c)
    40  
    41  	s.store = jujuclient.NewMemStore()
    42  	s.store.CurrentControllerName = "testing"
    43  	s.store.Controllers["testing"] = jujuclient.ControllerDetails{}
    44  	s.store.Accounts["testing"] = jujuclient.AccountDetails{
    45  		User: "admin",
    46  	}
    47  	err := s.store.UpdateModel("testing", "admin/mymodel", jujuclient.ModelDetails{
    48  		ModelUUID: testing.ModelTag.Id(),
    49  		ModelType: coremodel.IAAS,
    50  	})
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	s.store.Models["testing"].CurrentModel = "admin/mymodel"
    53  
    54  	s.rootFunc = func() (base.APICallCloser, error) { return &fakeRoot{}, nil }
    55  	s.modelClient = fakeModelClient{}
    56  	s.cloudClient = fakeCloudClient{}
    57  }
    58  
    59  func (s *ModelCredentialCommandSuite) TestBadArguments(c *gc.C) {
    60  	badArgs := []struct {
    61  		about  string
    62  		args   []string
    63  		err    string
    64  		stderr string
    65  	}{{
    66  		about:  "no arguments",
    67  		args:   []string{},
    68  		err:    regexp.QuoteMeta("Usage: juju set-credential [options] <cloud name> <credential name>"),
    69  		stderr: "ERROR Usage: juju set-credential [options] <cloud name> <credential name>\n",
    70  	}, {
    71  		about:  "1 argument",
    72  		args:   []string{"cloud"},
    73  		err:    regexp.QuoteMeta("Usage: juju set-credential [options] <cloud name> <credential name>"),
    74  		stderr: "ERROR Usage: juju set-credential [options] <cloud name> <credential name>\n",
    75  	}, {
    76  		about:  "3 argument",
    77  		args:   []string{"cloud", "cred", "nothing"},
    78  		err:    regexp.QuoteMeta("Usage: juju set-credential [options] <cloud name> <credential name>"),
    79  		stderr: "ERROR Usage: juju set-credential [options] <cloud name> <credential name>\n",
    80  	}, {
    81  		about:  "not valid cloud name",
    82  		args:   []string{"#1foo", "cred"},
    83  		err:    "cloud name \"#1foo\" not valid",
    84  		stderr: "ERROR cloud name \"#1foo\" not valid\n",
    85  	}, {
    86  		about:  "not valid cloud credential name",
    87  		args:   []string{"cloud", "0foo"},
    88  		err:    "cloud credential name \"0foo\" not valid",
    89  		stderr: "ERROR cloud credential name \"0foo\" not valid\n",
    90  	}}
    91  
    92  	for i, bad := range badArgs {
    93  		c.Logf("%d: %v", i, bad.about)
    94  		ctx, err := cmdtesting.RunCommand(c, s.newSetCredentialCommand(), bad.args...)
    95  		c.Assert(err, gc.ErrorMatches, bad.err)
    96  
    97  		c.Assert(cmdtesting.Stderr(ctx), gc.Equals, bad.stderr)
    98  		c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "")
    99  
   100  		s.modelClient.CheckNoCalls(c)
   101  		s.cloudClient.CheckNoCalls(c)
   102  	}
   103  }
   104  
   105  func (s *ModelCredentialCommandSuite) TestRootAPIError(c *gc.C) {
   106  	s.rootFunc = func() (base.APICallCloser, error) {
   107  		return nil, errors.New("kaboom")
   108  	}
   109  	ctx, err := cmdtesting.RunCommand(c, s.newSetCredentialCommand(), "cloud", "credential")
   110  	c.Assert(err, gc.ErrorMatches, "opening API connection: kaboom")
   111  
   112  	c.Assert(cmdtesting.Stderr(ctx), gc.Equals, "Failed to change model credential: opening API connection: kaboom\n")
   113  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "")
   114  
   115  	s.modelClient.CheckNoCalls(c)
   116  	s.cloudClient.CheckNoCalls(c)
   117  }
   118  
   119  func (s *ModelCredentialCommandSuite) TestSetCredentialNotFoundAnywhere(c *gc.C) {
   120  	s.assertCredentialNotFound(c, `
   121  Did not find credential remotely. Looking locally...
   122  Failed to change model credential: loading credentials: credentials for cloud aws not found
   123  `[1:])
   124  }
   125  
   126  func (s *ModelCredentialCommandSuite) TestSetCredentialRemoteSearchErred(c *gc.C) {
   127  	s.cloudClient.SetErrors(errors.New("boom"))
   128  	s.assertCredentialNotFound(c, `
   129  Could not determine if there are remote credentials for the user: boom
   130  Did not find credential remotely. Looking locally...
   131  Failed to change model credential: loading credentials: credentials for cloud aws not found
   132  `[1:])
   133  }
   134  
   135  func (s *ModelCredentialCommandSuite) assertCredentialNotFound(c *gc.C, expectedStderr string) {
   136  	ctx, err := cmdtesting.RunCommand(c, s.newSetCredentialCommand(), "aws", "credential")
   137  	c.Assert(err, gc.ErrorMatches, "loading credentials: credentials for cloud aws not found")
   138  
   139  	c.Assert(cmdtesting.Stderr(ctx), gc.Equals, expectedStderr)
   140  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "")
   141  
   142  	s.modelClient.CheckNoCalls(c)
   143  	s.cloudClient.CheckCalls(c, []jujutesting.StubCall{
   144  		{"UserCredentials", []interface{}{
   145  			names.NewUserTag("admin"),
   146  			names.NewCloudTag("aws"),
   147  		}},
   148  		{"Close", nil},
   149  	})
   150  }
   151  
   152  func (s *ModelCredentialCommandSuite) TestSetCredentialFoundRemote(c *gc.C) {
   153  	err := s.assertRemoteCredentialFound(c, `
   154  Found credential remotely, on the controller. Not looking locally...
   155  Changed cloud credential on model "admin/mymodel" to "credential".
   156  `[1:])
   157  	c.Assert(err, jc.ErrorIsNil)
   158  }
   159  
   160  func (s *ModelCredentialCommandSuite) TestSetCredentialErred(c *gc.C) {
   161  	s.modelClient.SetErrors(errors.New("kaboom"))
   162  	err := s.assertRemoteCredentialFound(c, `
   163  Found credential remotely, on the controller. Not looking locally...
   164  Failed to change model credential: kaboom
   165  `[1:])
   166  	c.Assert(err, gc.ErrorMatches, "kaboom")
   167  }
   168  
   169  func (s *ModelCredentialCommandSuite) assertRemoteCredentialFound(c *gc.C, expectedStderr string) error {
   170  	credentialTag := names.NewCloudCredentialTag("aws/admin/credential")
   171  	s.cloudClient.userCredentials = []names.CloudCredentialTag{credentialTag}
   172  	ctx, err := cmdtesting.RunCommand(c, s.newSetCredentialCommand(), "aws", "credential")
   173  
   174  	c.Assert(cmdtesting.Stderr(ctx), gc.Equals, expectedStderr)
   175  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "")
   176  
   177  	s.modelClient.CheckCalls(c, []jujutesting.StubCall{
   178  		{"ChangeModelCredential", []interface{}{
   179  			testing.ModelTag,
   180  			credentialTag,
   181  		}},
   182  		{"Close", nil},
   183  	})
   184  	s.cloudClient.CheckCalls(c, []jujutesting.StubCall{
   185  		{"UserCredentials", []interface{}{
   186  			names.NewUserTag("admin"),
   187  			names.NewCloudTag("aws"),
   188  		}},
   189  		{"Close", nil},
   190  	})
   191  	// This the error from running the command.
   192  	// It's returned to allow individual test to assert their expectations.
   193  	return err
   194  }
   195  
   196  func (s *ModelCredentialCommandSuite) TestSetCredentialLocal(c *gc.C) {
   197  	err := s.assertLocalCredentialUsed(c, `
   198  Did not find credential remotely. Looking locally...
   199  Uploading local credential to the controller.
   200  Changed cloud credential on model "admin/mymodel" to "credential".
   201  `[1:])
   202  	c.Assert(err, jc.ErrorIsNil)
   203  
   204  	s.modelClient.CheckCalls(c, []jujutesting.StubCall{
   205  		{"ChangeModelCredential", []interface{}{
   206  			testing.ModelTag,
   207  			names.NewCloudCredentialTag("aws/admin/credential"),
   208  		}},
   209  		{"Close", nil},
   210  	})
   211  }
   212  
   213  func (s *ModelCredentialCommandSuite) TestSetCredentialLocalUploadFailed(c *gc.C) {
   214  	s.cloudClient.SetErrors(nil, errors.New("upload failed"))
   215  	err := s.assertLocalCredentialUsed(c, `
   216  Did not find credential remotely. Looking locally...
   217  Uploading local credential to the controller.
   218  Failed to change model credential: upload failed
   219  `[1:])
   220  	c.Assert(err, gc.ErrorMatches, "upload failed")
   221  	s.modelClient.CheckNoCalls(c)
   222  }
   223  
   224  func (s *ModelCredentialCommandSuite) assertLocalCredentialUsed(c *gc.C, expectedStderr string) error {
   225  	credential := cloud.NewCredential(cloud.AccessKeyAuthType,
   226  		map[string]string{
   227  			"access-key": "v",
   228  			"secret-key": "v",
   229  		},
   230  	)
   231  	cloudCredential := &cloud.CloudCredential{
   232  		AuthCredentials: map[string]cloud.Credential{
   233  			"credential": credential,
   234  		},
   235  	}
   236  	s.store.Credentials["aws"] = *cloudCredential
   237  	ctx, err := cmdtesting.RunCommand(c, s.newSetCredentialCommand(), "aws", "credential")
   238  
   239  	c.Assert(cmdtesting.Stderr(ctx), gc.Equals, expectedStderr)
   240  	c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "")
   241  
   242  	s.cloudClient.CheckCalls(c, []jujutesting.StubCall{
   243  		{"UserCredentials", []interface{}{
   244  			names.NewUserTag("admin"),
   245  			names.NewCloudTag("aws"),
   246  		}},
   247  		{"AddCredential", []interface{}{
   248  			names.NewCloudCredentialTag("aws/admin/credential").String(),
   249  			credential,
   250  		}},
   251  		{"Close", nil},
   252  	})
   253  	return err
   254  }
   255  
   256  func (s *ModelCredentialCommandSuite) newSetCredentialCommand() cmd.Command {
   257  	return model.NewModelCredentialCommandForTest(&s.modelClient, &s.cloudClient, s.rootFunc, s.store)
   258  }
   259  
   260  type fakeModelClient struct {
   261  	jujutesting.Stub
   262  }
   263  
   264  func (f *fakeModelClient) Close() error {
   265  	f.MethodCall(f, "Close")
   266  	return f.NextErr()
   267  }
   268  
   269  func (f *fakeModelClient) ChangeModelCredential(model names.ModelTag, credential names.CloudCredentialTag) error {
   270  	f.MethodCall(f, "ChangeModelCredential", model, credential)
   271  	return f.NextErr()
   272  }
   273  
   274  type fakeCloudClient struct {
   275  	jujutesting.Stub
   276  
   277  	userCredentials []names.CloudCredentialTag
   278  }
   279  
   280  func (f *fakeCloudClient) Close() error {
   281  	f.MethodCall(f, "Close")
   282  	return f.NextErr()
   283  }
   284  
   285  func (f *fakeCloudClient) UserCredentials(u names.UserTag, c names.CloudTag) ([]names.CloudCredentialTag, error) {
   286  	f.MethodCall(f, "UserCredentials", u, c)
   287  	return f.userCredentials, f.NextErr()
   288  }
   289  
   290  func (f *fakeCloudClient) AddCredential(tag string, credential cloud.Credential) error {
   291  	f.MethodCall(f, "AddCredential", tag, credential)
   292  	return f.NextErr()
   293  }
   294  
   295  type fakeRoot struct {
   296  	base.APICaller
   297  	jujutesting.Stub
   298  }
   299  
   300  func (f *fakeRoot) Close() error {
   301  	f.MethodCall(f, "Close")
   302  	return f.NextErr()
   303  }