github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/cloud/cloud_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cloud_test
     5  
     6  import (
     7  	gitjujutesting "github.com/juju/testing"
     8  	jc "github.com/juju/testing/checkers"
     9  	gc "gopkg.in/check.v1"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/errors"
    13  	cloudfacade "github.com/juju/juju/apiserver/cloud"
    14  	"github.com/juju/juju/apiserver/params"
    15  	apiservertesting "github.com/juju/juju/apiserver/testing"
    16  	"github.com/juju/juju/cloud"
    17  	_ "github.com/juju/juju/provider/dummy"
    18  )
    19  
    20  type cloudSuite struct {
    21  	gitjujutesting.IsolationSuite
    22  	backend    mockBackend
    23  	authorizer apiservertesting.FakeAuthorizer
    24  	api        *cloudfacade.CloudAPI
    25  }
    26  
    27  var _ = gc.Suite(&cloudSuite{})
    28  
    29  func (s *cloudSuite) SetUpTest(c *gc.C) {
    30  	s.IsolationSuite.SetUpTest(c)
    31  	s.authorizer = apiservertesting.FakeAuthorizer{
    32  		Tag: names.NewUserTag("admin"),
    33  	}
    34  	s.backend = mockBackend{
    35  		cloud: cloud.Cloud{
    36  			Type:      "dummy",
    37  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType, cloud.UserPassAuthType},
    38  			Regions:   []cloud.Region{{Name: "nether", Endpoint: "endpoint"}},
    39  		},
    40  		creds: map[string]cloud.Credential{
    41  			names.NewCloudCredentialTag("meep/bruce/one").Id(): cloud.NewEmptyCredential(),
    42  			names.NewCloudCredentialTag("meep/bruce/two").Id(): cloud.NewCredential(cloud.UserPassAuthType, map[string]string{
    43  				"username": "admin",
    44  				"password": "adm1n",
    45  			}),
    46  		},
    47  	}
    48  	var err error
    49  	s.api, err = cloudfacade.NewCloudAPI(&s.backend, &s.authorizer)
    50  	c.Assert(err, jc.ErrorIsNil)
    51  }
    52  
    53  func (s *cloudSuite) TestCloud(c *gc.C) {
    54  	results, err := s.api.Cloud(params.Entities{
    55  		Entities: []params.Entity{{Tag: "cloud-my-cloud"}, {Tag: "machine-0"}},
    56  	})
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	s.backend.CheckCalls(c, []gitjujutesting.StubCall{
    59  		{"Cloud", []interface{}{"my-cloud"}},
    60  	})
    61  	c.Assert(results.Results, gc.HasLen, 2)
    62  	c.Assert(results.Results[0].Error, gc.IsNil)
    63  	c.Assert(results.Results[0].Cloud, jc.DeepEquals, &params.Cloud{
    64  		Type:      "dummy",
    65  		AuthTypes: []string{"empty", "userpass"},
    66  		Regions:   []params.CloudRegion{{Name: "nether", Endpoint: "endpoint"}},
    67  	})
    68  	c.Assert(results.Results[1].Error, jc.DeepEquals, &params.Error{
    69  		Message: `"machine-0" is not a valid cloud tag`,
    70  	})
    71  }
    72  
    73  func (s *cloudSuite) TestClouds(c *gc.C) {
    74  	result, err := s.api.Clouds()
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	s.backend.CheckCallNames(c, "Clouds")
    77  	c.Assert(result.Clouds, jc.DeepEquals, map[string]params.Cloud{
    78  		"cloud-my-cloud": {
    79  			Type:      "dummy",
    80  			AuthTypes: []string{"empty", "userpass"},
    81  			Regions:   []params.CloudRegion{{Name: "nether", Endpoint: "endpoint"}},
    82  		},
    83  	})
    84  }
    85  
    86  func (s *cloudSuite) TestDefaultCloud(c *gc.C) {
    87  	result, err := s.api.DefaultCloud()
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	s.backend.CheckCallNames(c, "ControllerModel")
    90  	c.Assert(result, jc.DeepEquals, params.StringResult{
    91  		Result: "cloud-some-cloud",
    92  	})
    93  }
    94  
    95  func (s *cloudSuite) TestUserCredentials(c *gc.C) {
    96  	s.authorizer.Tag = names.NewUserTag("bruce")
    97  	results, err := s.api.UserCredentials(params.UserClouds{UserClouds: []params.UserCloud{{
    98  		UserTag:  "machine-0",
    99  		CloudTag: "cloud-meep",
   100  	}, {
   101  		UserTag:  "user-admin",
   102  		CloudTag: "cloud-meep",
   103  	}, {
   104  		UserTag:  "user-bruce",
   105  		CloudTag: "cloud-meep",
   106  	}}})
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	s.backend.CheckCallNames(c, "ControllerTag", "CloudCredentials")
   109  	s.backend.CheckCall(c, 1, "CloudCredentials", names.NewUserTag("bruce"), "meep")
   110  
   111  	c.Assert(results.Results, gc.HasLen, 3)
   112  	c.Assert(results.Results[0].Error, jc.DeepEquals, &params.Error{
   113  		Message: `"machine-0" is not a valid user tag`,
   114  	})
   115  	c.Assert(results.Results[1].Error, jc.DeepEquals, &params.Error{
   116  		Message: "permission denied", Code: params.CodeUnauthorized,
   117  	})
   118  	c.Assert(results.Results[2].Error, gc.IsNil)
   119  	c.Assert(results.Results[2].Result, jc.SameContents, []string{
   120  		"cloudcred-meep_bruce_one",
   121  		"cloudcred-meep_bruce_two",
   122  	})
   123  }
   124  
   125  func (s *cloudSuite) TestUserCredentialsAdminAccess(c *gc.C) {
   126  	s.authorizer.Tag = names.NewUserTag("admin")
   127  	results, err := s.api.UserCredentials(params.UserClouds{UserClouds: []params.UserCloud{{
   128  		UserTag:  "user-julia",
   129  		CloudTag: "cloud-meep",
   130  	}}})
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	s.backend.CheckCallNames(c, "ControllerTag", "CloudCredentials")
   133  	c.Assert(results.Results, gc.HasLen, 1)
   134  	// admin can access others' credentials
   135  	c.Assert(results.Results[0].Error, gc.IsNil)
   136  }
   137  
   138  func (s *cloudSuite) TestUpdateCredentials(c *gc.C) {
   139  	s.backend.SetErrors(nil, errors.NotFoundf("cloud"))
   140  	s.authorizer.Tag = names.NewUserTag("bruce")
   141  	results, err := s.api.UpdateCredentials(params.UpdateCloudCredentials{Credentials: []params.UpdateCloudCredential{{
   142  		Tag: "machine-0",
   143  	}, {
   144  		Tag: "cloudcred-meep_admin_whatever",
   145  	}, {
   146  		Tag: "cloudcred-meep_bruce_three",
   147  		Credential: params.CloudCredential{
   148  			AuthType:   "oauth1",
   149  			Attributes: map[string]string{"token": "foo:bar:baz"},
   150  		},
   151  	}, {
   152  		Tag: "cloudcred-badcloud_bruce_three",
   153  		Credential: params.CloudCredential{
   154  			AuthType:   "oauth1",
   155  			Attributes: map[string]string{"token": "foo:bar:baz"},
   156  		},
   157  	}}})
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	s.backend.CheckCallNames(c, "ControllerTag", "UpdateCloudCredential", "UpdateCloudCredential")
   160  	c.Assert(results.Results, gc.HasLen, 4)
   161  	c.Assert(results.Results[0].Error, jc.DeepEquals, &params.Error{
   162  		Message: `"machine-0" is not a valid cloudcred tag`,
   163  	})
   164  	c.Assert(results.Results[1].Error, jc.DeepEquals, &params.Error{
   165  		Message: "permission denied", Code: params.CodeUnauthorized,
   166  	})
   167  	c.Assert(results.Results[2].Error, gc.IsNil)
   168  	c.Assert(results.Results[3].Error, jc.DeepEquals, &params.Error{
   169  		Message: `cannot update credential "three": controller does not manage cloud "badcloud"`,
   170  	})
   171  
   172  	s.backend.CheckCall(
   173  		c, 1, "UpdateCloudCredential",
   174  		names.NewCloudCredentialTag("meep/bruce/three"),
   175  		cloud.NewCredential(
   176  			cloud.OAuth1AuthType,
   177  			map[string]string{"token": "foo:bar:baz"},
   178  		),
   179  	)
   180  }
   181  
   182  func (s *cloudSuite) TestUpdateCredentialsAdminAccess(c *gc.C) {
   183  	s.authorizer.Tag = names.NewUserTag("admin")
   184  	results, err := s.api.UpdateCredentials(params.UpdateCloudCredentials{Credentials: []params.UpdateCloudCredential{{
   185  		Tag: "cloudcred-meep_julia_three",
   186  		Credential: params.CloudCredential{
   187  			AuthType:   "oauth1",
   188  			Attributes: map[string]string{"token": "foo:bar:baz"},
   189  		},
   190  	}}})
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	s.backend.CheckCallNames(c, "ControllerTag", "UpdateCloudCredential")
   193  	c.Assert(results.Results, gc.HasLen, 1)
   194  	// admin can update others' credentials
   195  	c.Assert(results.Results[0].Error, gc.IsNil)
   196  }
   197  
   198  func (s *cloudSuite) TestRevokeCredentials(c *gc.C) {
   199  	s.authorizer.Tag = names.NewUserTag("bruce")
   200  	results, err := s.api.RevokeCredentials(params.Entities{Entities: []params.Entity{{
   201  		Tag: "machine-0",
   202  	}, {
   203  		Tag: "cloudcred-meep_admin_whatever",
   204  	}, {
   205  		Tag: "cloudcred-meep_bruce_three",
   206  	}}})
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	s.backend.CheckCallNames(c, "ControllerTag", "RemoveCloudCredential")
   209  	c.Assert(results.Results, gc.HasLen, 3)
   210  	c.Assert(results.Results[0].Error, jc.DeepEquals, &params.Error{
   211  		Message: `"machine-0" is not a valid cloudcred tag`,
   212  	})
   213  	c.Assert(results.Results[1].Error, jc.DeepEquals, &params.Error{
   214  		Message: "permission denied", Code: params.CodeUnauthorized,
   215  	})
   216  	c.Assert(results.Results[2].Error, gc.IsNil)
   217  
   218  	s.backend.CheckCall(
   219  		c, 1, "RemoveCloudCredential",
   220  		names.NewCloudCredentialTag("meep/bruce/three"),
   221  	)
   222  }
   223  
   224  func (s *cloudSuite) TestRevokeCredentialsAdminAccess(c *gc.C) {
   225  	s.authorizer.Tag = names.NewUserTag("admin")
   226  	results, err := s.api.RevokeCredentials(params.Entities{Entities: []params.Entity{{
   227  		Tag: "cloudcred-meep_julia_three",
   228  	}}})
   229  	c.Assert(err, jc.ErrorIsNil)
   230  	s.backend.CheckCallNames(c, "ControllerTag", "RemoveCloudCredential")
   231  	c.Assert(results.Results, gc.HasLen, 1)
   232  	// admin can revoke others' credentials
   233  	c.Assert(results.Results[0].Error, gc.IsNil)
   234  }
   235  
   236  func (s *cloudSuite) TestCredential(c *gc.C) {
   237  	s.authorizer.Tag = names.NewUserTag("bruce")
   238  	results, err := s.api.Credential(params.Entities{Entities: []params.Entity{{
   239  		Tag: "machine-0",
   240  	}, {
   241  		Tag: "cloudcred-meep_admin_foo",
   242  	}, {
   243  		Tag: "cloudcred-meep_bruce_two",
   244  	}}})
   245  	c.Assert(err, jc.ErrorIsNil)
   246  	s.backend.CheckCallNames(c, "ControllerTag", "CloudCredentials", "Cloud")
   247  	s.backend.CheckCall(c, 1, "CloudCredentials", names.NewUserTag("bruce"), "meep")
   248  
   249  	c.Assert(results.Results, gc.HasLen, 3)
   250  	c.Assert(results.Results[0].Error, jc.DeepEquals, &params.Error{
   251  		Message: `"machine-0" is not a valid cloudcred tag`,
   252  	})
   253  	c.Assert(results.Results[1].Error, jc.DeepEquals, &params.Error{
   254  		Message: "permission denied", Code: params.CodeUnauthorized,
   255  	})
   256  	c.Assert(results.Results[2].Error, gc.IsNil)
   257  	c.Assert(results.Results[2].Result, jc.DeepEquals, &params.CloudCredential{
   258  		AuthType:   "userpass",
   259  		Attributes: map[string]string{"username": "admin"},
   260  		Redacted:   []string{"password"},
   261  	})
   262  }
   263  
   264  func (s *cloudSuite) TestCredentialAdminAccess(c *gc.C) {
   265  	s.authorizer.Tag = names.NewUserTag("admin")
   266  	results, err := s.api.Credential(params.Entities{Entities: []params.Entity{{
   267  		Tag: "cloudcred-meep_bruce_two",
   268  	}}})
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	s.backend.CheckCallNames(c, "ControllerTag", "CloudCredentials", "Cloud")
   271  	c.Assert(results.Results, gc.HasLen, 1)
   272  	// admin can access others' credentials
   273  	c.Assert(results.Results[0].Error, gc.IsNil)
   274  }
   275  
   276  type mockBackend struct {
   277  	gitjujutesting.Stub
   278  	cloud cloud.Cloud
   279  	creds map[string]cloud.Credential
   280  }
   281  
   282  func (st *mockBackend) IsControllerAdmin(user names.UserTag) (bool, error) {
   283  	st.MethodCall(st, "IsControllerAdmin", user)
   284  	return user.Id() == "admin", st.NextErr()
   285  }
   286  
   287  func (st *mockBackend) ControllerModel() (cloudfacade.Model, error) {
   288  	st.MethodCall(st, "ControllerModel")
   289  	credentialTag := names.NewCloudCredentialTag("some-cloud/admin/some-credential")
   290  	return &mockModel{"some-cloud", "some-region", credentialTag}, st.NextErr()
   291  }
   292  
   293  func (st *mockBackend) ControllerTag() names.ControllerTag {
   294  	st.MethodCall(st, "ControllerTag")
   295  	return names.NewControllerTag("deadbeef-1bad-500d-9000-4b1d0d06f00d")
   296  }
   297  
   298  func (st *mockBackend) ModelTag() names.ModelTag {
   299  	st.MethodCall(st, "ModelTag")
   300  	return names.NewModelTag("deadbeef-0bad-400d-8000-4b1d0d06f00d")
   301  }
   302  
   303  func (st *mockBackend) Cloud(name string) (cloud.Cloud, error) {
   304  	st.MethodCall(st, "Cloud", name)
   305  	return st.cloud, st.NextErr()
   306  }
   307  
   308  func (st *mockBackend) Clouds() (map[names.CloudTag]cloud.Cloud, error) {
   309  	st.MethodCall(st, "Clouds")
   310  	return map[names.CloudTag]cloud.Cloud{
   311  		names.NewCloudTag("my-cloud"): st.cloud,
   312  	}, st.NextErr()
   313  }
   314  
   315  func (st *mockBackend) CloudCredentials(user names.UserTag, cloudName string) (map[string]cloud.Credential, error) {
   316  	st.MethodCall(st, "CloudCredentials", user, cloudName)
   317  	return st.creds, st.NextErr()
   318  }
   319  
   320  func (st *mockBackend) UpdateCloudCredential(tag names.CloudCredentialTag, cred cloud.Credential) error {
   321  	st.MethodCall(st, "UpdateCloudCredential", tag, cred)
   322  	return st.NextErr()
   323  }
   324  
   325  func (st *mockBackend) RemoveCloudCredential(tag names.CloudCredentialTag) error {
   326  	st.MethodCall(st, "RemoveCloudCredential", tag)
   327  	return st.NextErr()
   328  }
   329  
   330  func (st *mockBackend) Close() error {
   331  	st.MethodCall(st, "Close")
   332  	return st.NextErr()
   333  }
   334  
   335  type mockModel struct {
   336  	cloud              string
   337  	cloudRegion        string
   338  	cloudCredentialTag names.CloudCredentialTag
   339  }
   340  
   341  func (m *mockModel) Cloud() string {
   342  	return m.cloud
   343  }
   344  
   345  func (m *mockModel) CloudRegion() string {
   346  	return m.cloudRegion
   347  }
   348  
   349  func (m *mockModel) CloudCredential() (names.CloudCredentialTag, bool) {
   350  	return m.cloudCredentialTag, true
   351  }