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, ¶ms.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, ¶ms.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, ¶ms.Error{ 113 Message: `"machine-0" is not a valid user tag`, 114 }) 115 c.Assert(results.Results[1].Error, jc.DeepEquals, ¶ms.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, ¶ms.Error{ 162 Message: `"machine-0" is not a valid cloudcred tag`, 163 }) 164 c.Assert(results.Results[1].Error, jc.DeepEquals, ¶ms.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, ¶ms.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, ¶ms.Error{ 211 Message: `"machine-0" is not a valid cloudcred tag`, 212 }) 213 c.Assert(results.Results[1].Error, jc.DeepEquals, ¶ms.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, ¶ms.Error{ 251 Message: `"machine-0" is not a valid cloudcred tag`, 252 }) 253 c.Assert(results.Results[1].Error, jc.DeepEquals, ¶ms.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, ¶ms.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 }