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