github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/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  	"github.com/juju/errors"
     8  	gitjujutesting "github.com/juju/testing"
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	basetesting "github.com/juju/juju/api/base/testing"
    14  	cloudapi "github.com/juju/juju/api/cloud"
    15  	"github.com/juju/juju/apiserver/common"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/cloud"
    18  	coretesting "github.com/juju/juju/testing"
    19  )
    20  
    21  type cloudSuite struct {
    22  	gitjujutesting.IsolationSuite
    23  }
    24  
    25  var _ = gc.Suite(&cloudSuite{})
    26  
    27  func (s *cloudSuite) TestCloud(c *gc.C) {
    28  	apiCaller := basetesting.APICallerFunc(
    29  		func(objType string,
    30  			version int,
    31  			id, request string,
    32  			a, result interface{},
    33  		) error {
    34  			c.Check(objType, gc.Equals, "Cloud")
    35  			c.Check(id, gc.Equals, "")
    36  			c.Check(request, gc.Equals, "Cloud")
    37  			c.Check(a, jc.DeepEquals, params.Entities{
    38  				Entities: []params.Entity{{Tag: "cloud-foo"}},
    39  			})
    40  			c.Assert(result, gc.FitsTypeOf, &params.CloudResults{})
    41  			results := result.(*params.CloudResults)
    42  			results.Results = append(results.Results, params.CloudResult{
    43  				Cloud: &params.Cloud{
    44  					Type:      "dummy",
    45  					AuthTypes: []string{"empty", "userpass"},
    46  					Regions:   []params.CloudRegion{{Name: "nether", Endpoint: "endpoint"}},
    47  				},
    48  			})
    49  			return nil
    50  		},
    51  	)
    52  
    53  	client := cloudapi.NewClient(apiCaller)
    54  	result, err := client.Cloud(names.NewCloudTag("foo"))
    55  	c.Assert(err, jc.ErrorIsNil)
    56  	c.Assert(result, jc.DeepEquals, cloud.Cloud{
    57  		Name:      "foo",
    58  		Type:      "dummy",
    59  		AuthTypes: []cloud.AuthType{cloud.EmptyAuthType, cloud.UserPassAuthType},
    60  		Regions:   []cloud.Region{{Name: "nether", Endpoint: "endpoint"}},
    61  	})
    62  }
    63  
    64  func (s *cloudSuite) TestClouds(c *gc.C) {
    65  	apiCaller := basetesting.APICallerFunc(
    66  		func(objType string,
    67  			version int,
    68  			id, request string,
    69  			a, result_ interface{},
    70  		) error {
    71  			c.Check(objType, gc.Equals, "Cloud")
    72  			c.Check(id, gc.Equals, "")
    73  			c.Check(request, gc.Equals, "Clouds")
    74  			c.Check(a, gc.IsNil)
    75  			c.Assert(result_, gc.FitsTypeOf, &params.CloudsResult{})
    76  			result := result_.(*params.CloudsResult)
    77  			result.Clouds = map[string]params.Cloud{
    78  				"cloud-foo": {
    79  					Type: "bar",
    80  				},
    81  				"cloud-baz": {
    82  					Type:      "qux",
    83  					AuthTypes: []string{"empty", "userpass"},
    84  					Regions:   []params.CloudRegion{{Name: "nether", Endpoint: "endpoint"}},
    85  				},
    86  			}
    87  			return nil
    88  		},
    89  	)
    90  
    91  	client := cloudapi.NewClient(apiCaller)
    92  	clouds, err := client.Clouds()
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	c.Assert(clouds, jc.DeepEquals, map[names.CloudTag]cloud.Cloud{
    95  		names.NewCloudTag("foo"): {
    96  			Name: "foo",
    97  			Type: "bar",
    98  		},
    99  		names.NewCloudTag("baz"): {
   100  			Name:      "baz",
   101  			Type:      "qux",
   102  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType, cloud.UserPassAuthType},
   103  			Regions:   []cloud.Region{{Name: "nether", Endpoint: "endpoint"}},
   104  		},
   105  	})
   106  }
   107  
   108  func (s *cloudSuite) TestDefaultCloud(c *gc.C) {
   109  	apiCaller := basetesting.APICallerFunc(
   110  		func(objType string,
   111  			version int,
   112  			id, request string,
   113  			a, result interface{},
   114  		) error {
   115  			c.Check(objType, gc.Equals, "Cloud")
   116  			c.Check(id, gc.Equals, "")
   117  			c.Check(request, gc.Equals, "DefaultCloud")
   118  			c.Assert(result, gc.FitsTypeOf, &params.StringResult{})
   119  			results := result.(*params.StringResult)
   120  			results.Result = "cloud-foo"
   121  			return nil
   122  		},
   123  	)
   124  
   125  	client := cloudapi.NewClient(apiCaller)
   126  	result, err := client.DefaultCloud()
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	c.Assert(result, jc.DeepEquals, names.NewCloudTag("foo"))
   129  }
   130  
   131  func (s *cloudSuite) TestUserCredentials(c *gc.C) {
   132  	apiCaller := basetesting.APICallerFunc(
   133  		func(objType string,
   134  			version int,
   135  			id, request string,
   136  			a, result interface{},
   137  		) error {
   138  			c.Check(objType, gc.Equals, "Cloud")
   139  			c.Check(id, gc.Equals, "")
   140  			c.Check(request, gc.Equals, "UserCredentials")
   141  			c.Assert(result, gc.FitsTypeOf, &params.StringsResults{})
   142  			c.Assert(a, jc.DeepEquals, params.UserClouds{UserClouds: []params.UserCloud{{
   143  				UserTag:  "user-bob",
   144  				CloudTag: "cloud-foo",
   145  			}}})
   146  			*result.(*params.StringsResults) = params.StringsResults{
   147  				Results: []params.StringsResult{{
   148  					Result: []string{
   149  						"cloudcred-foo_bob_one",
   150  						"cloudcred-foo_bob_two",
   151  					},
   152  				}},
   153  			}
   154  			return nil
   155  		},
   156  	)
   157  
   158  	client := cloudapi.NewClient(apiCaller)
   159  	result, err := client.UserCredentials(names.NewUserTag("bob"), names.NewCloudTag("foo"))
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	c.Assert(result, jc.SameContents, []names.CloudCredentialTag{
   162  		names.NewCloudCredentialTag("foo/bob/one"),
   163  		names.NewCloudCredentialTag("foo/bob/two"),
   164  	})
   165  }
   166  
   167  func (s *cloudSuite) TestUpdateCredentialV2(c *gc.C) {
   168  	var called bool
   169  	apiCaller := basetesting.BestVersionCaller{
   170  		APICallerFunc: basetesting.APICallerFunc(
   171  			func(objType string,
   172  				version int,
   173  				id, request string,
   174  				a, result interface{},
   175  			) error {
   176  				c.Check(objType, gc.Equals, "Cloud")
   177  				c.Check(id, gc.Equals, "")
   178  				c.Check(request, gc.Equals, "UpdateCredentials")
   179  				c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   180  				c.Assert(a, jc.DeepEquals, params.UpdateCredentialArgs{
   181  					Credentials: []params.TaggedCredential{{
   182  						Tag: "cloudcred-foo_bob_bar",
   183  						Credential: params.CloudCredential{
   184  							AuthType: "userpass",
   185  							Attributes: map[string]string{
   186  								"username": "admin",
   187  								"password": "adm1n",
   188  							},
   189  						},
   190  					}}})
   191  				*result.(*params.ErrorResults) = params.ErrorResults{
   192  					Results: []params.ErrorResult{{}},
   193  				}
   194  				called = true
   195  				return nil
   196  			},
   197  		),
   198  		BestVersion: 2,
   199  	}
   200  	client := cloudapi.NewClient(apiCaller)
   201  	result, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	c.Assert(result, gc.IsNil)
   204  	c.Assert(called, jc.IsTrue)
   205  }
   206  
   207  func (s *cloudSuite) TestUpdateCredential(c *gc.C) {
   208  	var called bool
   209  	apiCaller := basetesting.BestVersionCaller{
   210  		APICallerFunc: basetesting.APICallerFunc(
   211  			func(objType string,
   212  				version int,
   213  				id, request string,
   214  				a, result interface{},
   215  			) error {
   216  				c.Check(objType, gc.Equals, "Cloud")
   217  				c.Check(id, gc.Equals, "")
   218  				c.Check(request, gc.Equals, "UpdateCredentialsCheckModels")
   219  				c.Assert(result, gc.FitsTypeOf, &params.UpdateCredentialResults{})
   220  				c.Assert(a, jc.DeepEquals, params.UpdateCredentialArgs{
   221  					Credentials: []params.TaggedCredential{{
   222  						Tag: "cloudcred-foo_bob_bar",
   223  						Credential: params.CloudCredential{
   224  							AuthType: "userpass",
   225  							Attributes: map[string]string{
   226  								"username": "admin",
   227  								"password": "adm1n",
   228  							},
   229  						},
   230  					}}})
   231  				*result.(*params.UpdateCredentialResults) = params.UpdateCredentialResults{
   232  					Results: []params.UpdateCredentialResult{{}},
   233  				}
   234  				called = true
   235  				return nil
   236  			},
   237  		),
   238  		BestVersion: 3,
   239  	}
   240  	client := cloudapi.NewClient(apiCaller)
   241  	result, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   242  	c.Assert(err, jc.ErrorIsNil)
   243  	c.Assert(result, gc.IsNil)
   244  	c.Assert(called, jc.IsTrue)
   245  }
   246  
   247  func (s *cloudSuite) TestUpdateCredentialErrorV2(c *gc.C) {
   248  	var called bool
   249  	apiCaller := basetesting.BestVersionCaller{
   250  		APICallerFunc: basetesting.APICallerFunc(
   251  			func(objType string,
   252  				version int,
   253  				id, request string,
   254  				a, result interface{},
   255  			) error {
   256  				c.Check(request, gc.Equals, "UpdateCredentials")
   257  				*result.(*params.ErrorResults) = params.ErrorResults{
   258  					Results: []params.ErrorResult{{common.ServerError(errors.New("validation failure"))}},
   259  				}
   260  				called = true
   261  				return nil
   262  			},
   263  		),
   264  		BestVersion: 2,
   265  	}
   266  	client := cloudapi.NewClient(apiCaller)
   267  	errs, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   268  	c.Assert(err, gc.ErrorMatches, "validation failure")
   269  	c.Assert(errs, gc.IsNil)
   270  	c.Assert(called, jc.IsTrue)
   271  }
   272  
   273  func (s *cloudSuite) TestUpdateCredentialError(c *gc.C) {
   274  	var called bool
   275  	apiCaller := basetesting.BestVersionCaller{
   276  		APICallerFunc: basetesting.APICallerFunc(
   277  			func(objType string,
   278  				version int,
   279  				id, request string,
   280  				a, result interface{},
   281  			) error {
   282  				c.Check(request, gc.Equals, "UpdateCredentialsCheckModels")
   283  				*result.(*params.UpdateCredentialResults) = params.UpdateCredentialResults{
   284  					Results: []params.UpdateCredentialResult{
   285  						{CredentialTag: "cloudcred-foo_bob_bar",
   286  							Error: common.ServerError(errors.New("validation failure")),
   287  						},
   288  					},
   289  				}
   290  				called = true
   291  				return nil
   292  			},
   293  		),
   294  		BestVersion: 3,
   295  	}
   296  	client := cloudapi.NewClient(apiCaller)
   297  	errs, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   298  	c.Assert(err, gc.ErrorMatches, "validation failure")
   299  	c.Assert(errs, gc.IsNil)
   300  	c.Assert(called, jc.IsTrue)
   301  }
   302  
   303  func (s *cloudSuite) TestUpdateCredentialCallErrorV2(c *gc.C) {
   304  	var called bool
   305  	apiCaller := basetesting.BestVersionCaller{
   306  		APICallerFunc: basetesting.APICallerFunc(
   307  			func(objType string,
   308  				version int,
   309  				id, request string,
   310  				a, result interface{},
   311  			) error {
   312  				c.Check(request, gc.Equals, "UpdateCredentials")
   313  				called = true
   314  				return errors.New("scary but true")
   315  			},
   316  		),
   317  		BestVersion: 2,
   318  	}
   319  	client := cloudapi.NewClient(apiCaller)
   320  	result, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   321  	c.Assert(err, gc.ErrorMatches, "scary but true")
   322  	c.Assert(result, gc.IsNil)
   323  	c.Assert(called, jc.IsTrue)
   324  }
   325  
   326  func (s *cloudSuite) TestUpdateCredentialCallError(c *gc.C) {
   327  	var called bool
   328  	apiCaller := basetesting.BestVersionCaller{
   329  		APICallerFunc: basetesting.APICallerFunc(
   330  			func(objType string,
   331  				version int,
   332  				id, request string,
   333  				a, result interface{},
   334  			) error {
   335  				c.Check(request, gc.Equals, "UpdateCredentialsCheckModels")
   336  				called = true
   337  				return errors.New("scary but true")
   338  			},
   339  		),
   340  		BestVersion: 3,
   341  	}
   342  	client := cloudapi.NewClient(apiCaller)
   343  	result, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   344  	c.Assert(err, gc.ErrorMatches, "scary but true")
   345  	c.Assert(result, gc.IsNil)
   346  	c.Assert(called, jc.IsTrue)
   347  }
   348  
   349  func (s *cloudSuite) TestUpdateCredentialManyResultsV2(c *gc.C) {
   350  	var called bool
   351  	apiCaller := basetesting.BestVersionCaller{
   352  		APICallerFunc: basetesting.APICallerFunc(
   353  			func(objType string,
   354  				version int,
   355  				id, request string,
   356  				a, result interface{},
   357  			) error {
   358  				c.Check(request, gc.Equals, "UpdateCredentials")
   359  				*result.(*params.ErrorResults) = params.ErrorResults{
   360  					Results: []params.ErrorResult{
   361  						{},
   362  						{},
   363  					},
   364  				}
   365  				called = true
   366  				return nil
   367  			},
   368  		),
   369  		BestVersion: 2,
   370  	}
   371  	client := cloudapi.NewClient(apiCaller)
   372  	result, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   373  	c.Assert(err, gc.ErrorMatches, `expected 1 result for when updating credential "bar", got 2`)
   374  	c.Assert(result, gc.IsNil)
   375  	c.Assert(called, jc.IsTrue)
   376  }
   377  
   378  func (s *cloudSuite) TestUpdateCredentialManyResults(c *gc.C) {
   379  	var called bool
   380  	apiCaller := basetesting.BestVersionCaller{
   381  		APICallerFunc: basetesting.APICallerFunc(
   382  			func(objType string,
   383  				version int,
   384  				id, request string,
   385  				a, result interface{},
   386  			) error {
   387  				c.Check(request, gc.Equals, "UpdateCredentialsCheckModels")
   388  				*result.(*params.UpdateCredentialResults) = params.UpdateCredentialResults{
   389  					Results: []params.UpdateCredentialResult{
   390  						{},
   391  						{},
   392  					}}
   393  				called = true
   394  				return nil
   395  			},
   396  		),
   397  		BestVersion: 3,
   398  	}
   399  	client := cloudapi.NewClient(apiCaller)
   400  	result, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   401  	c.Assert(err, gc.ErrorMatches, `expected 1 result for when updating credential "bar", got 2`)
   402  	c.Assert(result, gc.IsNil)
   403  	c.Assert(called, jc.IsTrue)
   404  }
   405  
   406  func (s *cloudSuite) TestUpdateCredentialModelErrors(c *gc.C) {
   407  	var called bool
   408  	apiCaller := basetesting.BestVersionCaller{
   409  		APICallerFunc: basetesting.APICallerFunc(
   410  			func(objType string,
   411  				version int,
   412  				id, request string,
   413  				a, result interface{},
   414  			) error {
   415  				c.Check(request, gc.Equals, "UpdateCredentialsCheckModels")
   416  				*result.(*params.UpdateCredentialResults) = params.UpdateCredentialResults{
   417  					Results: []params.UpdateCredentialResult{
   418  						{
   419  							CredentialTag: testCredentialTag.String(),
   420  							Models: []params.UpdateCredentialModelResult{
   421  								{
   422  									ModelUUID: coretesting.ModelTag.Id(),
   423  									ModelName: "test-model",
   424  									Errors: []params.ErrorResult{
   425  										{common.ServerError(errors.New("validation failure one"))},
   426  										{common.ServerError(errors.New("validation failure two"))},
   427  									},
   428  								},
   429  							},
   430  						},
   431  					}}
   432  				called = true
   433  				return nil
   434  			},
   435  		),
   436  		BestVersion: 3,
   437  	}
   438  	client := cloudapi.NewClient(apiCaller)
   439  	errs, err := client.UpdateCredentialsCheckModels(testCredentialTag, testCredential)
   440  	c.Assert(err, jc.ErrorIsNil)
   441  	c.Assert(errs, gc.DeepEquals, []params.UpdateCredentialModelResult{
   442  		{
   443  			ModelUUID: "deadbeef-0bad-400d-8000-4b1d0d06f00d",
   444  			ModelName: "test-model",
   445  			Errors: []params.ErrorResult{
   446  				{Error: &params.Error{Message: "validation failure one", Code: ""}},
   447  				{Error: &params.Error{Message: "validation failure two", Code: ""}},
   448  			},
   449  		},
   450  	})
   451  	c.Assert(called, jc.IsTrue)
   452  }
   453  
   454  var (
   455  	testCredentialTag = names.NewCloudCredentialTag("foo/bob/bar")
   456  	testCredential    = cloud.NewCredential(cloud.UserPassAuthType, map[string]string{
   457  		"username": "admin",
   458  		"password": "adm1n",
   459  	})
   460  )
   461  
   462  func (s *cloudSuite) TestRevokeCredential(c *gc.C) {
   463  	var called bool
   464  	apiCallerF := basetesting.APICallerFunc(
   465  		func(objType string,
   466  			version int,
   467  			id, request string,
   468  			a, result interface{},
   469  		) error {
   470  			c.Check(objType, gc.Equals, "Cloud")
   471  			c.Check(id, gc.Equals, "")
   472  			c.Check(request, gc.Equals, "RevokeCredentialsCheckModels")
   473  			c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   474  			c.Assert(a, jc.DeepEquals, params.RevokeCredentialArgs{
   475  				Credentials: []params.RevokeCredentialArg{
   476  					{Tag: "cloudcred-foo_bob_bar"},
   477  				},
   478  			})
   479  			*result.(*params.ErrorResults) = params.ErrorResults{
   480  				Results: []params.ErrorResult{{}},
   481  			}
   482  			called = true
   483  			return nil
   484  		},
   485  	)
   486  	apiCaller := basetesting.BestVersionCaller{
   487  		APICallerFunc: apiCallerF,
   488  		BestVersion:   3,
   489  	}
   490  
   491  	client := cloudapi.NewClient(apiCaller)
   492  	tag := names.NewCloudCredentialTag("foo/bob/bar")
   493  	err := client.RevokeCredential(tag)
   494  	c.Assert(err, jc.ErrorIsNil)
   495  	c.Assert(called, jc.IsTrue)
   496  }
   497  
   498  func (s *cloudSuite) TestRevokeCredentialV2(c *gc.C) {
   499  	var called bool
   500  	apiCallerF := basetesting.APICallerFunc(
   501  		func(objType string,
   502  			version int,
   503  			id, request string,
   504  			a, result interface{},
   505  		) error {
   506  			c.Check(objType, gc.Equals, "Cloud")
   507  			c.Check(id, gc.Equals, "")
   508  			c.Check(request, gc.Equals, "RevokeCredentials")
   509  			c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   510  			c.Assert(a, jc.DeepEquals, params.Entities{Entities: []params.Entity{{
   511  				Tag: "cloudcred-foo_bob_bar",
   512  			}}})
   513  			*result.(*params.ErrorResults) = params.ErrorResults{
   514  				Results: []params.ErrorResult{{}},
   515  			}
   516  			called = true
   517  			return nil
   518  		},
   519  	)
   520  	apiCaller := basetesting.BestVersionCaller{
   521  		APICallerFunc: apiCallerF,
   522  		BestVersion:   2,
   523  	}
   524  
   525  	client := cloudapi.NewClient(apiCaller)
   526  	tag := names.NewCloudCredentialTag("foo/bob/bar")
   527  	err := client.RevokeCredential(tag)
   528  	c.Assert(err, jc.ErrorIsNil)
   529  	c.Assert(called, jc.IsTrue)
   530  }
   531  
   532  func (s *cloudSuite) TestCredentials(c *gc.C) {
   533  	apiCaller := basetesting.APICallerFunc(
   534  		func(objType string,
   535  			version int,
   536  			id, request string,
   537  			a, result interface{},
   538  		) error {
   539  			c.Check(objType, gc.Equals, "Cloud")
   540  			c.Check(id, gc.Equals, "")
   541  			c.Check(request, gc.Equals, "Credential")
   542  			c.Assert(result, gc.FitsTypeOf, &params.CloudCredentialResults{})
   543  			c.Assert(a, jc.DeepEquals, params.Entities{Entities: []params.Entity{{
   544  				Tag: "cloudcred-foo_bob_bar",
   545  			}}})
   546  			*result.(*params.CloudCredentialResults) = params.CloudCredentialResults{
   547  				Results: []params.CloudCredentialResult{
   548  					{
   549  						Result: &params.CloudCredential{
   550  							AuthType:   "userpass",
   551  							Attributes: map[string]string{"username": "fred"},
   552  							Redacted:   []string{"password"},
   553  						},
   554  					}, {
   555  						Result: &params.CloudCredential{
   556  							AuthType:   "userpass",
   557  							Attributes: map[string]string{"username": "mary"},
   558  							Redacted:   []string{"password"},
   559  						},
   560  					},
   561  				},
   562  			}
   563  			return nil
   564  		},
   565  	)
   566  
   567  	client := cloudapi.NewClient(apiCaller)
   568  	tag := names.NewCloudCredentialTag("foo/bob/bar")
   569  	result, err := client.Credentials(tag)
   570  	c.Assert(err, jc.ErrorIsNil)
   571  	c.Assert(result, jc.DeepEquals, []params.CloudCredentialResult{
   572  		{
   573  			Result: &params.CloudCredential{
   574  				AuthType:   "userpass",
   575  				Attributes: map[string]string{"username": "fred"},
   576  				Redacted:   []string{"password"},
   577  			},
   578  		}, {
   579  			Result: &params.CloudCredential{
   580  				AuthType:   "userpass",
   581  				Attributes: map[string]string{"username": "mary"},
   582  				Redacted:   []string{"password"},
   583  			},
   584  		},
   585  	})
   586  }
   587  
   588  func (s *cloudSuite) TestAddCloudNotInV1API(c *gc.C) {
   589  	apiCaller := basetesting.BestVersionCaller{
   590  		APICallerFunc: basetesting.APICallerFunc(
   591  			func(objType string,
   592  				version int,
   593  				id, request string,
   594  				a, result interface{},
   595  			) error {
   596  				c.Check(objType, gc.Equals, "Cloud")
   597  				c.Check(id, gc.Equals, "")
   598  				c.Check(request, gc.Equals, "AddCloud")
   599  				return nil
   600  			},
   601  		),
   602  		BestVersion: 1,
   603  	}
   604  	client := cloudapi.NewClient(apiCaller)
   605  	err := client.AddCloud(cloud.Cloud{
   606  		Name:      "foo",
   607  		Type:      "dummy",
   608  		AuthTypes: []cloud.AuthType{cloud.EmptyAuthType, cloud.UserPassAuthType},
   609  		Regions:   []cloud.Region{{Name: "nether", Endpoint: "endpoint"}},
   610  	})
   611  
   612  	c.Assert(err, gc.ErrorMatches, "AddCloud\\(\\).* not implemented")
   613  }
   614  
   615  func (s *cloudSuite) TestAddCloudV2API(c *gc.C) {
   616  	var called bool
   617  	apiCaller := basetesting.BestVersionCaller{
   618  		APICallerFunc: basetesting.APICallerFunc(
   619  			func(objType string,
   620  				version int,
   621  				id, request string,
   622  				a, result interface{},
   623  			) error {
   624  				called = true
   625  				c.Check(objType, gc.Equals, "Cloud")
   626  				c.Check(id, gc.Equals, "")
   627  				c.Check(request, gc.Equals, "AddCloud")
   628  				return nil
   629  			},
   630  		),
   631  		BestVersion: 2,
   632  	}
   633  
   634  	client := cloudapi.NewClient(apiCaller)
   635  	err := client.AddCloud(cloud.Cloud{
   636  		Name:      "foo",
   637  		Type:      "dummy",
   638  		AuthTypes: []cloud.AuthType{cloud.EmptyAuthType, cloud.UserPassAuthType},
   639  		Regions:   []cloud.Region{{Name: "nether", Endpoint: "endpoint"}},
   640  	})
   641  
   642  	c.Assert(err, jc.ErrorIsNil)
   643  	c.Assert(called, jc.IsTrue)
   644  }
   645  
   646  func (s *cloudSuite) TestAddCredentialNotInV1API(c *gc.C) {
   647  	apiCaller := basetesting.BestVersionCaller{
   648  		APICallerFunc: basetesting.APICallerFunc(
   649  			func(objType string,
   650  				version int,
   651  				id, request string,
   652  				a, result interface{},
   653  			) error {
   654  				return nil
   655  			},
   656  		),
   657  		BestVersion: 1,
   658  	}
   659  	client := cloudapi.NewClient(apiCaller)
   660  	err := client.AddCredential("cloudcred-acloud-user-credname",
   661  		cloud.NewCredential(cloud.UserPassAuthType, map[string]string{}))
   662  
   663  	c.Assert(err, gc.ErrorMatches, "AddCredential\\(\\).* not implemented")
   664  }
   665  
   666  func (s *cloudSuite) TestAddCredentialV2API(c *gc.C) {
   667  	var called bool
   668  	apiCaller := basetesting.BestVersionCaller{
   669  		APICallerFunc: basetesting.APICallerFunc(
   670  			func(objType string,
   671  				version int,
   672  				id, request string,
   673  				a, result interface{},
   674  			) error {
   675  				called = true
   676  				c.Check(objType, gc.Equals, "Cloud")
   677  				c.Check(id, gc.Equals, "")
   678  				c.Check(request, gc.Equals, "AddCredentials")
   679  				c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   680  				*result.(*params.ErrorResults) = params.ErrorResults{
   681  					Results: []params.ErrorResult{{}},
   682  				}
   683  
   684  				return nil
   685  			},
   686  		),
   687  		BestVersion: 2,
   688  	}
   689  
   690  	client := cloudapi.NewClient(apiCaller)
   691  	err := client.AddCredential("cloudcred-acloud-user-credname",
   692  		cloud.NewCredential(cloud.UserPassAuthType,
   693  			map[string]string{}))
   694  
   695  	c.Assert(err, jc.ErrorIsNil)
   696  	c.Assert(called, jc.IsTrue)
   697  }
   698  
   699  func (s *cloudSuite) TestCredentialContentsArgumentCheck(c *gc.C) {
   700  	apiCaller := basetesting.BestVersionCaller{BestVersion: 2}
   701  	client := cloudapi.NewClient(apiCaller)
   702  
   703  	// Check supplying cloud name without credential name is invalid.
   704  	result, err := client.CredentialContents("cloud", "", true)
   705  	c.Assert(result, gc.IsNil)
   706  	c.Assert(err, gc.ErrorMatches, "credential name must be supplied")
   707  
   708  	// Check supplying credential name without cloud name is invalid.
   709  	result, err = client.CredentialContents("", "credential", true)
   710  	c.Assert(result, gc.IsNil)
   711  	c.Assert(err, gc.ErrorMatches, "cloud name must be supplied")
   712  }
   713  
   714  func (s *cloudSuite) TestCredentialContentsAll(c *gc.C) {
   715  	expectedResults := []params.CredentialContentResult{
   716  		{
   717  			Result: &params.ControllerCredentialInfo{
   718  				Content: params.CredentialContent{
   719  					Cloud:    "cloud-name",
   720  					Name:     "credential-name",
   721  					AuthType: "userpass",
   722  					Attributes: map[string]string{
   723  						"username": "fred",
   724  						"password": "sekret"},
   725  				},
   726  				Models: []params.ModelAccess{
   727  					{Model: "abcmodel", Access: "admin"},
   728  					{Model: "xyzmodel", Access: "read"},
   729  					{Model: "no-access-model"}, // no access
   730  				},
   731  			},
   732  		}, {
   733  			Error: common.ServerError(errors.New("boom")),
   734  		},
   735  	}
   736  	apiCaller := basetesting.BestVersionCaller{
   737  		APICallerFunc: basetesting.APICallerFunc(
   738  			func(objType string,
   739  				version int,
   740  				id, request string,
   741  				a, result interface{},
   742  			) error {
   743  				c.Check(objType, gc.Equals, "Cloud")
   744  				c.Check(id, gc.Equals, "")
   745  				c.Check(request, gc.Equals, "CredentialContents")
   746  				c.Assert(result, gc.FitsTypeOf, &params.CredentialContentResults{})
   747  				c.Assert(a, jc.DeepEquals, params.CloudCredentialArgs{
   748  					IncludeSecrets: true,
   749  				})
   750  				*result.(*params.CredentialContentResults) = params.CredentialContentResults{
   751  					Results: expectedResults,
   752  				}
   753  				return nil
   754  			},
   755  		),
   756  		BestVersion: 2,
   757  	}
   758  
   759  	client := cloudapi.NewClient(apiCaller)
   760  	results, err := client.CredentialContents("", "", true)
   761  	c.Assert(err, jc.ErrorIsNil)
   762  	c.Assert(results, jc.DeepEquals, expectedResults)
   763  }
   764  
   765  func (s *cloudSuite) TestCredentialContentsOne(c *gc.C) {
   766  	apiCaller := basetesting.BestVersionCaller{
   767  		APICallerFunc: basetesting.APICallerFunc(
   768  			func(objType string,
   769  				version int,
   770  				id, request string,
   771  				a, result interface{},
   772  			) error {
   773  				c.Check(objType, gc.Equals, "Cloud")
   774  				c.Check(id, gc.Equals, "")
   775  				c.Check(request, gc.Equals, "CredentialContents")
   776  				c.Assert(result, gc.FitsTypeOf, &params.CredentialContentResults{})
   777  				c.Assert(a, jc.DeepEquals, params.CloudCredentialArgs{
   778  					IncludeSecrets: true,
   779  					Credentials: []params.CloudCredentialArg{
   780  						{CloudName: "cloud-name", CredentialName: "credential-name"},
   781  					},
   782  				})
   783  				*result.(*params.CredentialContentResults) = params.CredentialContentResults{
   784  					Results: []params.CredentialContentResult{
   785  						{},
   786  					},
   787  				}
   788  				return nil
   789  			},
   790  		),
   791  		BestVersion: 2,
   792  	}
   793  
   794  	client := cloudapi.NewClient(apiCaller)
   795  	results, err := client.CredentialContents("cloud-name", "credential-name", true)
   796  	c.Assert(err, jc.ErrorIsNil)
   797  	c.Assert(results, gc.HasLen, 1)
   798  }
   799  
   800  func (s *cloudSuite) TestCredentialContentsGotMoreThanBargainedFor(c *gc.C) {
   801  	apiCaller := basetesting.BestVersionCaller{
   802  		APICallerFunc: basetesting.APICallerFunc(
   803  			func(objType string,
   804  				version int,
   805  				id, request string,
   806  				a, result interface{},
   807  			) error {
   808  				*result.(*params.CredentialContentResults) = params.CredentialContentResults{
   809  					Results: []params.CredentialContentResult{
   810  						{},
   811  						{},
   812  					},
   813  				}
   814  				return nil
   815  			},
   816  		),
   817  		BestVersion: 2,
   818  	}
   819  
   820  	client := cloudapi.NewClient(apiCaller)
   821  	results, err := client.CredentialContents("cloud-name", "credential-name", true)
   822  	c.Assert(results, gc.IsNil)
   823  	c.Assert(err, gc.ErrorMatches, "expected 1 result for credential \"cloud-name\" on cloud \"credential-name\", got 2")
   824  }
   825  
   826  func (s *cloudSuite) TestCredentialContentsServerError(c *gc.C) {
   827  	apiCaller := basetesting.BestVersionCaller{
   828  		APICallerFunc: basetesting.APICallerFunc(
   829  			func(objType string,
   830  				version int,
   831  				id, request string,
   832  				a, result interface{},
   833  			) error {
   834  				return errors.New("boom")
   835  			}),
   836  		BestVersion: 2,
   837  	}
   838  
   839  	client := cloudapi.NewClient(apiCaller)
   840  	results, err := client.CredentialContents("", "", true)
   841  	c.Assert(results, gc.IsNil)
   842  	c.Assert(err, gc.ErrorMatches, "boom")
   843  }
   844  
   845  func (s *cloudSuite) TestCredentialContentsNotInV2API(c *gc.C) {
   846  	apiCaller := basetesting.BestVersionCaller{
   847  		APICallerFunc: basetesting.APICallerFunc(
   848  			func(objType string,
   849  				version int,
   850  				id, request string,
   851  				a, result interface{},
   852  			) error {
   853  				return nil
   854  			},
   855  		),
   856  		BestVersion: 1,
   857  	}
   858  	client := cloudapi.NewClient(apiCaller)
   859  	_, err := client.CredentialContents("", "", true)
   860  	c.Assert(err, gc.ErrorMatches, "CredentialContents\\(\\).* not implemented")
   861  }
   862  
   863  func (s *cloudSuite) TestRemoveCloud(c *gc.C) {
   864  	var called bool
   865  	apiCaller := basetesting.BestVersionCaller{
   866  		APICallerFunc: basetesting.APICallerFunc(
   867  			func(objType string,
   868  				version int,
   869  				id, request string,
   870  				a, result interface{},
   871  			) error {
   872  				called = true
   873  				c.Check(objType, gc.Equals, "Cloud")
   874  				c.Check(id, gc.Equals, "")
   875  				c.Check(request, gc.Equals, "RemoveClouds")
   876  				c.Check(a, jc.DeepEquals, params.Entities{
   877  					Entities: []params.Entity{{Tag: "cloud-foo"}},
   878  				})
   879  				c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   880  				results := result.(*params.ErrorResults)
   881  				results.Results = append(results.Results, params.ErrorResult{
   882  					Error: &params.Error{Message: "FAIL"},
   883  				})
   884  				return nil
   885  			},
   886  		),
   887  		BestVersion: 2,
   888  	}
   889  
   890  	client := cloudapi.NewClient(apiCaller)
   891  	err := client.RemoveCloud("foo")
   892  	c.Assert(err, gc.ErrorMatches, "FAIL")
   893  	c.Assert(called, jc.IsTrue)
   894  }
   895  
   896  func (s *cloudSuite) TestRemoveCloudNotInV1API(c *gc.C) {
   897  	apiCaller := basetesting.BestVersionCaller{
   898  		APICallerFunc: basetesting.APICallerFunc(
   899  			func(objType string,
   900  				version int,
   901  				id, request string,
   902  				a, result interface{},
   903  			) error {
   904  				c.Check(objType, gc.Equals, "Cloud")
   905  				c.Check(id, gc.Equals, "")
   906  				c.Check(request, gc.Equals, "RemoveCloud")
   907  				return nil
   908  			},
   909  		),
   910  		BestVersion: 1,
   911  	}
   912  	client := cloudapi.NewClient(apiCaller)
   913  	err := client.RemoveCloud("foo")
   914  
   915  	c.Assert(err, gc.ErrorMatches, "RemoveCloud\\(\\).* not implemented")
   916  }
   917  
   918  func (s *cloudSuite) TestGrantCloud(c *gc.C) {
   919  	var called bool
   920  	apiCaller := basetesting.BestVersionCaller{
   921  		APICallerFunc: basetesting.APICallerFunc(
   922  			func(objType string,
   923  				version int,
   924  				id, request string,
   925  				a, result interface{},
   926  			) error {
   927  				called = true
   928  				c.Check(objType, gc.Equals, "Cloud")
   929  				c.Check(id, gc.Equals, "")
   930  				c.Check(request, gc.Equals, "ModifyCloudAccess")
   931  				c.Check(a, jc.DeepEquals, params.ModifyCloudAccessRequest{
   932  					Changes: []params.ModifyCloudAccess{
   933  						{UserTag: "user-fred", CloudTag: "cloud-fluffy", Action: "grant", Access: "admin"},
   934  					},
   935  				})
   936  				c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   937  				results := result.(*params.ErrorResults)
   938  				results.Results = append(results.Results, params.ErrorResult{
   939  					Error: &params.Error{Message: "FAIL"},
   940  				})
   941  				return nil
   942  			},
   943  		),
   944  		BestVersion: 3,
   945  	}
   946  
   947  	client := cloudapi.NewClient(apiCaller)
   948  	err := client.GrantCloud("fred", "admin", "fluffy")
   949  	c.Assert(err, gc.ErrorMatches, "FAIL")
   950  	c.Assert(called, jc.IsTrue)
   951  }
   952  
   953  func (s *cloudSuite) TestGrantCloudAccessNotInV2API(c *gc.C) {
   954  	apiCaller := basetesting.BestVersionCaller{
   955  		APICallerFunc: basetesting.APICallerFunc(
   956  			func(objType string,
   957  				version int,
   958  				id, request string,
   959  				a, result interface{},
   960  			) error {
   961  				c.Fail()
   962  				return nil
   963  			},
   964  		),
   965  		BestVersion: 2,
   966  	}
   967  	client := cloudapi.NewClient(apiCaller)
   968  	err := client.GrantCloud("foo", "admin", "fluffy")
   969  	c.Assert(err, gc.ErrorMatches, "GrantCloud\\(\\).* not implemented")
   970  }
   971  
   972  func (s *cloudSuite) TestRevokeCloud(c *gc.C) {
   973  	var called bool
   974  	apiCaller := basetesting.BestVersionCaller{
   975  		APICallerFunc: basetesting.APICallerFunc(
   976  			func(objType string,
   977  				version int,
   978  				id, request string,
   979  				a, result interface{},
   980  			) error {
   981  				called = true
   982  				c.Check(objType, gc.Equals, "Cloud")
   983  				c.Check(id, gc.Equals, "")
   984  				c.Check(request, gc.Equals, "ModifyCloudAccess")
   985  				c.Check(a, jc.DeepEquals, params.ModifyCloudAccessRequest{
   986  					Changes: []params.ModifyCloudAccess{
   987  						{UserTag: "user-fred", CloudTag: "cloud-fluffy", Action: "revoke", Access: "admin"},
   988  					},
   989  				})
   990  				c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   991  				results := result.(*params.ErrorResults)
   992  				results.Results = append(results.Results, params.ErrorResult{
   993  					Error: &params.Error{Message: "FAIL"},
   994  				})
   995  				return nil
   996  			},
   997  		),
   998  		BestVersion: 3,
   999  	}
  1000  
  1001  	client := cloudapi.NewClient(apiCaller)
  1002  	err := client.RevokeCloud("fred", "admin", "fluffy")
  1003  	c.Assert(err, gc.ErrorMatches, "FAIL")
  1004  	c.Assert(called, jc.IsTrue)
  1005  }
  1006  
  1007  func (s *cloudSuite) TestRevokeCloudAccessNotInV2API(c *gc.C) {
  1008  	apiCaller := basetesting.BestVersionCaller{
  1009  		APICallerFunc: basetesting.APICallerFunc(
  1010  			func(objType string,
  1011  				version int,
  1012  				id, request string,
  1013  				a, result interface{},
  1014  			) error {
  1015  				c.Fail()
  1016  				return nil
  1017  			},
  1018  		),
  1019  		BestVersion: 2,
  1020  	}
  1021  	client := cloudapi.NewClient(apiCaller)
  1022  	err := client.RevokeCloud("foo", "admin", "fluffy")
  1023  	c.Assert(err, gc.ErrorMatches, "RevokeCloud\\(\\).* not implemented")
  1024  }