github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/client/storage/client_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storage_test
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/collections/set"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	jc "github.com/juju/testing/checkers"
    13  	"go.uber.org/mock/gomock"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	basemocks "github.com/juju/juju/api/base/mocks"
    17  	"github.com/juju/juju/api/client/storage"
    18  	apiservererrors "github.com/juju/juju/apiserver/errors"
    19  	"github.com/juju/juju/rpc/params"
    20  	jujustorage "github.com/juju/juju/storage"
    21  )
    22  
    23  type storageMockSuite struct {
    24  }
    25  
    26  var _ = gc.Suite(&storageMockSuite{})
    27  
    28  func (s *storageMockSuite) TestStorageDetails(c *gc.C) {
    29  	ctrl := gomock.NewController(c)
    30  	defer ctrl.Finish()
    31  
    32  	one := "shared-fs/0"
    33  	oneTag := names.NewStorageTag(one)
    34  	two := "db-dir/1000"
    35  	twoTag := names.NewStorageTag(two)
    36  	expected := set.NewStrings(oneTag.String(), twoTag.String())
    37  	msg := "call failure"
    38  	args := params.Entities{
    39  		Entities: []params.Entity{
    40  			{Tag: oneTag.String()},
    41  			{Tag: twoTag.String()},
    42  		},
    43  	}
    44  	instances := []params.StorageDetailsResult{
    45  		{
    46  			Result: &params.StorageDetails{StorageTag: oneTag.String()},
    47  		},
    48  		{
    49  			Result: &params.StorageDetails{
    50  				StorageTag: twoTag.String(),
    51  				Status: params.EntityStatus{
    52  					Status: "attached",
    53  				},
    54  				Persistent: true,
    55  			},
    56  		},
    57  		{
    58  			Error: apiservererrors.ServerError(errors.New(msg)),
    59  		},
    60  	}
    61  	results := params.StorageDetailsResults{
    62  		Results: instances,
    63  	}
    64  	result := new(params.StorageDetailsResults)
    65  
    66  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
    67  	mockFacadeCaller.EXPECT().FacadeCall("StorageDetails", args, result).SetArg(2, results).Return(nil)
    68  
    69  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
    70  
    71  	tags := []names.StorageTag{oneTag, twoTag}
    72  	found, err := storageClient.StorageDetails(tags)
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	c.Assert(found, gc.HasLen, 3)
    75  	c.Assert(expected.Contains(found[0].Result.StorageTag), jc.IsTrue)
    76  	c.Assert(expected.Contains(found[1].Result.StorageTag), jc.IsTrue)
    77  	c.Assert(found[2].Error, gc.ErrorMatches, msg)
    78  }
    79  
    80  func (s *storageMockSuite) TestStorageDetailsFacadeCallError(c *gc.C) {
    81  	ctrl := gomock.NewController(c)
    82  	defer ctrl.Finish()
    83  
    84  	msg := "facade failure"
    85  	one := "shared-fs/0"
    86  	oneTag := names.NewStorageTag(one)
    87  
    88  	result := new(params.StorageDetailsResults)
    89  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
    90  	mockFacadeCaller.EXPECT().FacadeCall("StorageDetails", gomock.AssignableToTypeOf(params.Entities{}), result).SetArg(2, params.StorageDetailsResults{}).Return(errors.New(msg))
    91  
    92  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
    93  	found, err := storageClient.StorageDetails([]names.StorageTag{oneTag})
    94  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
    95  	c.Assert(found, gc.HasLen, 0)
    96  }
    97  
    98  func (s *storageMockSuite) TestListStorageDetails(c *gc.C) {
    99  	ctrl := gomock.NewController(c)
   100  	defer ctrl.Finish()
   101  
   102  	storageTag := names.NewStorageTag("db-dir/1000")
   103  	result := new(params.StorageDetailsListResults)
   104  	results := params.StorageDetailsListResults{
   105  		Results: []params.StorageDetailsListResult{{
   106  			Result: []params.StorageDetails{{
   107  				StorageTag: storageTag.String(),
   108  				Status: params.EntityStatus{
   109  					Status: "attached",
   110  				},
   111  				Persistent: true,
   112  			}},
   113  		}},
   114  	}
   115  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   116  	mockFacadeCaller.EXPECT().FacadeCall("ListStorageDetails", gomock.AssignableToTypeOf(params.StorageFilters{}), result).SetArg(2, results).Return(nil)
   117  
   118  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   119  	found, err := storageClient.ListStorageDetails()
   120  	c.Check(err, jc.ErrorIsNil)
   121  	c.Assert(found, gc.HasLen, 1)
   122  	expected := []params.StorageDetails{{
   123  		StorageTag: "storage-db-dir-1000",
   124  		Status: params.EntityStatus{
   125  			Status: "attached",
   126  		},
   127  		Persistent: true,
   128  	}}
   129  
   130  	c.Assert(found, jc.DeepEquals, expected)
   131  }
   132  
   133  func (s *storageMockSuite) TestListStorageDetailsFacadeCallError(c *gc.C) {
   134  	ctrl := gomock.NewController(c)
   135  	defer ctrl.Finish()
   136  
   137  	msg := "facade failure"
   138  
   139  	result := new(params.StorageDetailsListResults)
   140  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   141  	mockFacadeCaller.EXPECT().FacadeCall("ListStorageDetails", gomock.AssignableToTypeOf(params.StorageFilters{}), result).SetArg(2, params.StorageDetailsListResults{}).Return(errors.New(msg))
   142  
   143  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   144  	found, err := storageClient.ListStorageDetails()
   145  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   146  	c.Assert(found, gc.HasLen, 0)
   147  }
   148  
   149  func (s *storageMockSuite) TestListPools(c *gc.C) {
   150  	ctrl := gomock.NewController(c)
   151  	defer ctrl.Finish()
   152  
   153  	someNames := []string{"a", "b"}
   154  	types := []string{"1"}
   155  	expected := []params.StoragePool{
   156  		{Name: "name0", Provider: "type0"},
   157  		{Name: "name1", Provider: "type1"},
   158  		{Name: "name2", Provider: "type2"},
   159  	}
   160  	want := len(expected)
   161  
   162  	args := params.StoragePoolFilters{
   163  		Filters: []params.StoragePoolFilter{{
   164  			Names:     someNames,
   165  			Providers: types,
   166  		}},
   167  	}
   168  	result := new(params.StoragePoolsResults)
   169  	pools := make([]params.StoragePool, want)
   170  	for i := 0; i < want; i++ {
   171  		pools[i] = params.StoragePool{
   172  			Name:     fmt.Sprintf("name%v", i),
   173  			Provider: fmt.Sprintf("type%v", i),
   174  		}
   175  	}
   176  	results := params.StoragePoolsResults{
   177  		Results: []params.StoragePoolsResult{{
   178  			Result: pools,
   179  		}},
   180  	}
   181  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   182  	mockFacadeCaller.EXPECT().FacadeCall("ListPools", args, result).SetArg(2, results).Return(nil)
   183  
   184  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   185  
   186  	found, err := storageClient.ListPools(types, someNames)
   187  	c.Assert(err, jc.ErrorIsNil)
   188  	c.Assert(found, gc.HasLen, want)
   189  	c.Assert(found, gc.DeepEquals, expected)
   190  }
   191  
   192  func (s *storageMockSuite) TestListPoolsFacadeCallError(c *gc.C) {
   193  	ctrl := gomock.NewController(c)
   194  	defer ctrl.Finish()
   195  
   196  	msg := "facade failure"
   197  
   198  	result := new(params.StoragePoolsResults)
   199  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   200  	mockFacadeCaller.EXPECT().FacadeCall("ListPools", gomock.AssignableToTypeOf(params.StoragePoolFilters{}), result).SetArg(2, params.StoragePoolsResults{}).Return(errors.New(msg))
   201  
   202  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   203  	found, err := storageClient.ListPools(nil, nil)
   204  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   205  	c.Assert(found, gc.HasLen, 0)
   206  }
   207  
   208  func (s *storageMockSuite) TestCreatePool(c *gc.C) {
   209  	ctrl := gomock.NewController(c)
   210  	defer ctrl.Finish()
   211  
   212  	poolName := "poolName"
   213  	poolType := "poolType"
   214  	poolConfig := map[string]interface{}{
   215  		"test": "one",
   216  		"pass": true,
   217  	}
   218  	args := params.StoragePoolArgs{
   219  		Pools: []params.StoragePool{{
   220  			Name:     poolName,
   221  			Provider: poolType,
   222  			Attrs:    poolConfig,
   223  		},
   224  		}}
   225  	result := new(params.ErrorResults)
   226  	results := params.ErrorResults{
   227  		Results: make([]params.ErrorResult, len(args.Pools)),
   228  	}
   229  
   230  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   231  	mockFacadeCaller.EXPECT().FacadeCall("CreatePool", args, result).SetArg(2, results).Return(nil)
   232  
   233  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   234  	err := storageClient.CreatePool(poolName, poolType, poolConfig)
   235  	c.Assert(err, jc.ErrorIsNil)
   236  }
   237  
   238  func (s *storageMockSuite) TestCreatePoolFacadeCallError(c *gc.C) {
   239  	ctrl := gomock.NewController(c)
   240  	defer ctrl.Finish()
   241  
   242  	msg := "facade failure"
   243  
   244  	result := new(params.ErrorResults)
   245  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   246  	mockFacadeCaller.EXPECT().FacadeCall("CreatePool", gomock.AssignableToTypeOf(params.StoragePoolArgs{}), result).Return(errors.New(msg))
   247  
   248  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   249  	err := storageClient.CreatePool("", "", nil)
   250  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   251  }
   252  
   253  func (s *storageMockSuite) TestListVolumes(c *gc.C) {
   254  	ctrl := gomock.NewController(c)
   255  	defer ctrl.Finish()
   256  
   257  	args := params.VolumeFilters{
   258  		Filters: []params.VolumeFilter{
   259  			{Machines: []string{"machine-0"}},
   260  			{Machines: []string{"machine-1"}},
   261  		}}
   262  	result := new(params.VolumeDetailsListResults)
   263  	details := params.VolumeDetails{
   264  		VolumeTag: "volume-0",
   265  		MachineAttachments: map[string]params.VolumeAttachmentDetails{
   266  			"machine-0": {},
   267  			"machine-1": {},
   268  		},
   269  	}
   270  	results := params.VolumeDetailsListResults{
   271  		Results: []params.VolumeDetailsListResult{{
   272  			Result: []params.VolumeDetails{details},
   273  		}, {
   274  			Result: []params.VolumeDetails{details},
   275  		}},
   276  	}
   277  
   278  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   279  	mockFacadeCaller.EXPECT().FacadeCall("ListVolumes", args, result).SetArg(2, results).Return(nil)
   280  
   281  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   282  	found, err := storageClient.ListVolumes([]string{"0", "1"})
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	c.Assert(found, gc.HasLen, 2)
   285  	for i := 0; i < 2; i++ {
   286  		c.Assert(found[i].Result, jc.DeepEquals, []params.VolumeDetails{{
   287  			VolumeTag: "volume-0",
   288  			MachineAttachments: map[string]params.VolumeAttachmentDetails{
   289  				"machine-0": {},
   290  				"machine-1": {},
   291  			},
   292  		}})
   293  	}
   294  }
   295  
   296  func (s *storageMockSuite) TestListVolumesEmptyFilter(c *gc.C) {
   297  	ctrl := gomock.NewController(c)
   298  	defer ctrl.Finish()
   299  
   300  	tag := "ok"
   301  	args := params.VolumeFilters{
   302  		Filters: []params.VolumeFilter{{}},
   303  	}
   304  	result := new(params.VolumeDetailsListResults)
   305  	results := params.VolumeDetailsListResults{
   306  		Results: []params.VolumeDetailsListResult{
   307  			{Result: []params.VolumeDetails{{VolumeTag: tag}}},
   308  		},
   309  	}
   310  
   311  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   312  	mockFacadeCaller.EXPECT().FacadeCall("ListVolumes", args, result).SetArg(2, results).Return(nil)
   313  
   314  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   315  	found, err := storageClient.ListVolumes(nil)
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(found, gc.HasLen, 1)
   318  	c.Assert(found[0].Result, gc.HasLen, 1)
   319  	c.Assert(found[0].Result[0].VolumeTag, gc.Equals, tag)
   320  }
   321  
   322  func (s *storageMockSuite) TestListVolumesFacadeCallError(c *gc.C) {
   323  	msg := "facade failure"
   324  
   325  	ctrl := gomock.NewController(c)
   326  	defer ctrl.Finish()
   327  
   328  	args := params.VolumeFilters{
   329  		Filters: []params.VolumeFilter{{}},
   330  	}
   331  	result := new(params.VolumeDetailsListResults)
   332  
   333  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   334  	mockFacadeCaller.EXPECT().FacadeCall("ListVolumes", args, result).Return(errors.New(msg))
   335  
   336  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   337  	_, err := storageClient.ListVolumes(nil)
   338  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   339  }
   340  
   341  func (s *storageMockSuite) TestListFilesystems(c *gc.C) {
   342  	ctrl := gomock.NewController(c)
   343  	defer ctrl.Finish()
   344  
   345  	args := params.FilesystemFilters{
   346  		Filters: []params.FilesystemFilter{{
   347  			Machines: []string{"machine-1"},
   348  		}, {
   349  			Machines: []string{"machine-2"},
   350  		}},
   351  	}
   352  	result := new(params.FilesystemDetailsListResults)
   353  	expected := params.FilesystemDetails{
   354  		FilesystemTag: "filesystem-1",
   355  		Info: params.FilesystemInfo{
   356  			FilesystemId: "fs-id",
   357  			Size:         4096,
   358  		},
   359  		Status: params.EntityStatus{
   360  			Status: "attached",
   361  		},
   362  		MachineAttachments: map[string]params.FilesystemAttachmentDetails{
   363  			"0": {
   364  				FilesystemAttachmentInfo: params.FilesystemAttachmentInfo{
   365  					MountPoint: "/mnt/kinabalu",
   366  					ReadOnly:   false,
   367  				},
   368  			},
   369  		},
   370  	}
   371  	results := params.FilesystemDetailsListResults{
   372  		Results: []params.FilesystemDetailsListResult{
   373  			{Result: []params.FilesystemDetails{expected}},
   374  			{},
   375  		},
   376  	}
   377  
   378  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   379  	mockFacadeCaller.EXPECT().FacadeCall("ListFilesystems", args, result).SetArg(2, results).Return(nil)
   380  
   381  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   382  	found, err := storageClient.ListFilesystems([]string{"1", "2"})
   383  	c.Assert(err, jc.ErrorIsNil)
   384  	c.Assert(found, gc.HasLen, 2)
   385  	c.Assert(found[0].Result, jc.DeepEquals, []params.FilesystemDetails{expected})
   386  	c.Assert(found[1].Result, jc.DeepEquals, []params.FilesystemDetails{})
   387  }
   388  
   389  func (s *storageMockSuite) TestListFilesystemsEmptyFilter(c *gc.C) {
   390  	ctrl := gomock.NewController(c)
   391  	defer ctrl.Finish()
   392  
   393  	args := params.FilesystemFilters{
   394  		Filters: []params.FilesystemFilter{{}},
   395  	}
   396  	result := new(params.FilesystemDetailsListResults)
   397  	results := params.FilesystemDetailsListResults{
   398  		Results: []params.FilesystemDetailsListResult{{}},
   399  	}
   400  
   401  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   402  	mockFacadeCaller.EXPECT().FacadeCall("ListFilesystems", args, result).SetArg(2, results).Return(nil)
   403  
   404  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   405  	_, err := storageClient.ListFilesystems(nil)
   406  	c.Assert(err, jc.ErrorIsNil)
   407  }
   408  
   409  func (s *storageMockSuite) TestListFilesystemsFacadeCallError(c *gc.C) {
   410  	msg := "facade failure"
   411  
   412  	ctrl := gomock.NewController(c)
   413  	defer ctrl.Finish()
   414  
   415  	args := params.FilesystemFilters{
   416  		Filters: []params.FilesystemFilter{{}},
   417  	}
   418  	result := new(params.FilesystemDetailsListResults)
   419  
   420  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   421  	mockFacadeCaller.EXPECT().FacadeCall("ListFilesystems", args, result).Return(errors.New(msg))
   422  
   423  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   424  	_, err := storageClient.ListFilesystems(nil)
   425  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   426  }
   427  
   428  func (s *storageMockSuite) TestAddToUnit(c *gc.C) {
   429  	ctrl := gomock.NewController(c)
   430  	defer ctrl.Finish()
   431  	size := uint64(42)
   432  	cons := params.StorageConstraints{
   433  		Pool: "value",
   434  		Size: &size,
   435  	}
   436  
   437  	errOut := "error"
   438  	unitStorages := []params.StorageAddParams{
   439  		{UnitTag: "u-a", StorageName: "one", Constraints: cons},
   440  		{UnitTag: "u-b", StorageName: errOut, Constraints: cons},
   441  		{UnitTag: "u-b", StorageName: "nil-constraints"},
   442  	}
   443  
   444  	storageN := 3
   445  	expectedError := apiservererrors.ServerError(errors.NotValidf("storage directive"))
   446  	expectedDetails := &params.AddStorageDetails{[]string{"a/0", "b/1"}}
   447  	one := func(u, s string, attrs params.StorageConstraints) params.AddStorageResult {
   448  		result := params.AddStorageResult{}
   449  		if s == errOut {
   450  			result.Error = expectedError
   451  		} else {
   452  			result.Result = expectedDetails
   453  		}
   454  		return result
   455  	}
   456  	args := params.StoragesAddParams{
   457  		Storages: unitStorages,
   458  	}
   459  	result := new(params.AddStorageResults)
   460  	results := params.AddStorageResults{
   461  		Results: []params.AddStorageResult{
   462  			one(unitStorages[0].UnitTag, unitStorages[0].StorageName, unitStorages[0].Constraints),
   463  			one(unitStorages[1].UnitTag, unitStorages[1].StorageName, unitStorages[1].Constraints),
   464  			one(unitStorages[2].UnitTag, unitStorages[2].StorageName, unitStorages[2].Constraints),
   465  		},
   466  	}
   467  
   468  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   469  	mockFacadeCaller.EXPECT().FacadeCall("AddToUnit", args, result).SetArg(2, results).Return(nil)
   470  
   471  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   472  	r, err := storageClient.AddToUnit(unitStorages)
   473  	c.Assert(err, jc.ErrorIsNil)
   474  	c.Assert(r, gc.HasLen, storageN)
   475  	expected := []params.AddStorageResult{
   476  		{Result: expectedDetails},
   477  		{Error: expectedError},
   478  		{Result: expectedDetails},
   479  	}
   480  	c.Assert(r, jc.SameContents, expected)
   481  }
   482  
   483  func (s *storageMockSuite) TestAddToUnitFacadeCallError(c *gc.C) {
   484  	ctrl := gomock.NewController(c)
   485  	defer ctrl.Finish()
   486  
   487  	unitStorages := []params.StorageAddParams{
   488  		{UnitTag: "u-a", StorageName: "one"},
   489  	}
   490  	msg := "facade failure"
   491  	args := params.StoragesAddParams{
   492  		Storages: unitStorages,
   493  	}
   494  	result := new(params.AddStorageResults)
   495  
   496  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   497  	mockFacadeCaller.EXPECT().FacadeCall("AddToUnit", args, result).Return(errors.New(msg))
   498  
   499  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   500  	found, err := storageClient.AddToUnit(unitStorages)
   501  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   502  	c.Assert(found, gc.HasLen, 0)
   503  }
   504  
   505  func (s *storageMockSuite) TestRemove(c *gc.C) {
   506  	ctrl := gomock.NewController(c)
   507  	defer ctrl.Finish()
   508  
   509  	false_ := false
   510  	args := params.RemoveStorage{[]params.RemoveStorageInstance{
   511  		{Tag: "storage-foo-0", DestroyAttachments: false, DestroyStorage: false, Force: &false_},
   512  		{Tag: "storage-bar-1", DestroyAttachments: false, DestroyStorage: false, Force: &false_},
   513  	}}
   514  	result := new(params.ErrorResults)
   515  	results := params.ErrorResults{
   516  		Results: []params.ErrorResult{
   517  			{},
   518  			{Error: &params.Error{Message: "baz"}},
   519  		},
   520  	}
   521  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   522  	mockFacadeCaller.EXPECT().FacadeCall("Remove", args, result).SetArg(2, results).Return(nil)
   523  
   524  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   525  	obtained, err := storageClient.Remove([]string{"foo/0", "bar/1"}, false, false, &false_, nil)
   526  	c.Check(err, jc.ErrorIsNil)
   527  	c.Assert(obtained, gc.HasLen, 2)
   528  	c.Assert(obtained[0].Error, gc.IsNil)
   529  	c.Assert(obtained[1].Error, jc.DeepEquals, &params.Error{Message: "baz"})
   530  }
   531  
   532  func (s *storageMockSuite) TestRemoveDestroyAttachments(c *gc.C) {
   533  	ctrl := gomock.NewController(c)
   534  	defer ctrl.Finish()
   535  
   536  	true_ := true
   537  	args := params.RemoveStorage{[]params.RemoveStorageInstance{
   538  		{Tag: "storage-foo-0", DestroyAttachments: true, DestroyStorage: true, Force: &true_},
   539  	}}
   540  	result := new(params.ErrorResults)
   541  	results := params.ErrorResults{
   542  		Results: []params.ErrorResult{{}},
   543  	}
   544  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   545  	mockFacadeCaller.EXPECT().FacadeCall("Remove", args, result).SetArg(2, results).Return(nil)
   546  
   547  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   548  	obtained, err := storageClient.Remove([]string{"foo/0"}, true, true, &true_, nil)
   549  	c.Check(err, jc.ErrorIsNil)
   550  	c.Assert(obtained, gc.HasLen, 1)
   551  	c.Assert(obtained[0].Error, gc.IsNil)
   552  }
   553  
   554  func (s *storageMockSuite) TestRemoveInvalidStorageId(c *gc.C) {
   555  	ctrl := gomock.NewController(c)
   556  	defer ctrl.Finish()
   557  
   558  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   559  
   560  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   561  	_, err := storageClient.Remove([]string{"foo/bar"}, false, false, nil, nil)
   562  	c.Check(err, gc.ErrorMatches, `storage ID "foo/bar" not valid`)
   563  }
   564  
   565  func (s *storageMockSuite) TestDetach(c *gc.C) {
   566  	ctrl := gomock.NewController(c)
   567  	defer ctrl.Finish()
   568  	expectedArgs := params.StorageDetachmentParams{
   569  		StorageIds: params.StorageAttachmentIds{[]params.StorageAttachmentId{
   570  			{StorageTag: "storage-foo-0"},
   571  			{StorageTag: "storage-bar-1"},
   572  		}},
   573  	}
   574  	result := new(params.ErrorResults)
   575  	results := params.ErrorResults{
   576  		Results: []params.ErrorResult{
   577  			{},
   578  			{Error: &params.Error{Message: "baz"}},
   579  		},
   580  	}
   581  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   582  	mockFacadeCaller.EXPECT().FacadeCall("DetachStorage", expectedArgs, result).SetArg(2, results).Return(nil)
   583  
   584  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   585  	obtained, err := storageClient.Detach([]string{"foo/0", "bar/1"}, nil, nil)
   586  	c.Check(err, jc.ErrorIsNil)
   587  	c.Assert(obtained, gc.HasLen, 2)
   588  	c.Assert(obtained[0].Error, gc.IsNil)
   589  	c.Assert(obtained[1].Error, jc.DeepEquals, &params.Error{Message: "baz"})
   590  }
   591  
   592  func (s *storageMockSuite) TestDetachArityMismatch(c *gc.C) {
   593  	ctrl := gomock.NewController(c)
   594  	defer ctrl.Finish()
   595  	result := new(params.ErrorResults)
   596  	results := params.ErrorResults{
   597  		Results: []params.ErrorResult{{}, {}, {}},
   598  	}
   599  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   600  	mockFacadeCaller.EXPECT().FacadeCall("DetachStorage", gomock.AssignableToTypeOf(params.StorageDetachmentParams{}), result).SetArg(2, results).Return(nil)
   601  
   602  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   603  	_, err := storageClient.Detach([]string{"foo/0", "bar/1"}, nil, nil)
   604  	c.Check(err, gc.ErrorMatches, `expected 2 result\(s\), got 3`)
   605  }
   606  
   607  func (s *storageMockSuite) TestAttach(c *gc.C) {
   608  	ctrl := gomock.NewController(c)
   609  	defer ctrl.Finish()
   610  	expectedArgs := params.StorageAttachmentIds{[]params.StorageAttachmentId{
   611  		{
   612  			StorageTag: "storage-bar-1",
   613  			UnitTag:    "unit-foo-0",
   614  		},
   615  		{
   616  			StorageTag: "storage-baz-2",
   617  			UnitTag:    "unit-foo-0",
   618  		},
   619  	}}
   620  	result := new(params.ErrorResults)
   621  	results := params.ErrorResults{
   622  		Results: []params.ErrorResult{
   623  			{},
   624  			{Error: &params.Error{Message: "qux"}},
   625  		},
   626  	}
   627  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   628  	mockFacadeCaller.EXPECT().FacadeCall("Attach", expectedArgs, result).SetArg(2, results).Return(nil)
   629  
   630  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   631  	obtained, err := storageClient.Attach("foo/0", []string{"bar/1", "baz/2"})
   632  	c.Check(err, jc.ErrorIsNil)
   633  	c.Assert(obtained, gc.HasLen, 2)
   634  	c.Assert(obtained[0].Error, gc.IsNil)
   635  	c.Assert(obtained[1].Error, jc.DeepEquals, &params.Error{Message: "qux"})
   636  }
   637  
   638  func (s *storageMockSuite) TestAttachArityMismatch(c *gc.C) {
   639  	ctrl := gomock.NewController(c)
   640  	defer ctrl.Finish()
   641  	result := new(params.ErrorResults)
   642  	results := params.ErrorResults{
   643  		Results: []params.ErrorResult{{}, {}, {}},
   644  	}
   645  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   646  	mockFacadeCaller.EXPECT().FacadeCall("Attach", gomock.AssignableToTypeOf(params.StorageAttachmentIds{}), result).SetArg(2, results).Return(nil)
   647  
   648  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   649  	_, err := storageClient.Attach("foo/0", []string{"bar/1", "baz/2"})
   650  	c.Check(err, gc.ErrorMatches, `expected 2 result\(s\), got 3`)
   651  }
   652  
   653  func (s *storageMockSuite) TestImport(c *gc.C) {
   654  	ctrl := gomock.NewController(c)
   655  	defer ctrl.Finish()
   656  	expectedArgs := params.BulkImportStorageParams{[]params.ImportStorageParams{{
   657  		Kind:        params.StorageKindBlock,
   658  		Pool:        "foo",
   659  		ProviderId:  "bar",
   660  		StorageName: "baz",
   661  	}}}
   662  	result := new(params.ImportStorageResults)
   663  	results := params.ImportStorageResults{
   664  		Results: []params.ImportStorageResult{{
   665  			Result: &params.ImportStorageDetails{
   666  				StorageTag: "storage-qux-0",
   667  			},
   668  		},
   669  		}}
   670  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   671  	mockFacadeCaller.EXPECT().FacadeCall("Import", expectedArgs, result).SetArg(2, results).Return(nil)
   672  
   673  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   674  	storageTag, err := storageClient.Import(jujustorage.StorageKindBlock, "foo", "bar", "baz")
   675  	c.Assert(err, jc.ErrorIsNil)
   676  	c.Assert(storageTag, gc.Equals, names.NewStorageTag("qux/0"))
   677  }
   678  
   679  func (s *storageMockSuite) TestImportError(c *gc.C) {
   680  	ctrl := gomock.NewController(c)
   681  	defer ctrl.Finish()
   682  	result := new(params.ImportStorageResults)
   683  	results := params.ImportStorageResults{
   684  		Results: []params.ImportStorageResult{{
   685  			Error: &params.Error{Message: "qux"},
   686  		},
   687  		}}
   688  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   689  	mockFacadeCaller.EXPECT().FacadeCall("Import", gomock.AssignableToTypeOf(params.BulkImportStorageParams{}), result).SetArg(2, results).Return(nil)
   690  
   691  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   692  	_, err := storageClient.Import(jujustorage.StorageKindBlock, "foo", "bar", "baz")
   693  	c.Check(err, gc.ErrorMatches, "qux")
   694  }
   695  
   696  func (s *storageMockSuite) TestImportArityMismatch(c *gc.C) {
   697  	ctrl := gomock.NewController(c)
   698  	defer ctrl.Finish()
   699  
   700  	result := new(params.ImportStorageResults)
   701  	results := params.ImportStorageResults{
   702  		Results: []params.ImportStorageResult{{}, {}},
   703  	}
   704  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   705  	mockFacadeCaller.EXPECT().FacadeCall("Import", gomock.AssignableToTypeOf(params.BulkImportStorageParams{}), result).SetArg(2, results).Return(nil)
   706  
   707  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   708  	_, err := storageClient.Import(jujustorage.StorageKindBlock, "foo", "bar", "baz")
   709  	c.Check(err, gc.ErrorMatches, `expected 1 result, got 2`)
   710  }
   711  
   712  func (s *storageMockSuite) TestRemovePool(c *gc.C) {
   713  	ctrl := gomock.NewController(c)
   714  	defer ctrl.Finish()
   715  
   716  	poolName := "poolName"
   717  	expectedArgs := params.StoragePoolDeleteArgs{
   718  		Pools: []params.StoragePoolDeleteArg{{
   719  			Name: poolName,
   720  		}},
   721  	}
   722  	result := new(params.ErrorResults)
   723  	results := params.ErrorResults{
   724  		Results: make([]params.ErrorResult, len(expectedArgs.Pools)),
   725  	}
   726  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   727  	mockFacadeCaller.EXPECT().FacadeCall("RemovePool", expectedArgs, result).SetArg(2, results).Return(nil)
   728  
   729  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   730  	err := storageClient.RemovePool(poolName)
   731  	c.Assert(err, jc.ErrorIsNil)
   732  }
   733  
   734  func (s *storageMockSuite) TestRemovePoolFacadeCallError(c *gc.C) {
   735  	ctrl := gomock.NewController(c)
   736  	defer ctrl.Finish()
   737  
   738  	msg := "facade failure"
   739  	result := new(params.ErrorResults)
   740  	results := params.ErrorResults{
   741  		Results: make([]params.ErrorResult, 1),
   742  	}
   743  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   744  	mockFacadeCaller.EXPECT().FacadeCall("RemovePool", gomock.AssignableToTypeOf(params.StoragePoolDeleteArgs{}), result).SetArg(2, results).Return(errors.New(msg))
   745  
   746  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   747  	err := storageClient.RemovePool("")
   748  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   749  }
   750  
   751  func (s *storageMockSuite) TestUpdatePool(c *gc.C) {
   752  	ctrl := gomock.NewController(c)
   753  	defer ctrl.Finish()
   754  
   755  	poolName := "poolName"
   756  	providerType := "loop"
   757  	poolConfig := map[string]interface{}{
   758  		"test": "one",
   759  		"pass": true,
   760  	}
   761  	expectedArgs := params.StoragePoolArgs{
   762  		Pools: []params.StoragePool{{
   763  			Name:     poolName,
   764  			Provider: providerType,
   765  			Attrs:    poolConfig,
   766  		}},
   767  	}
   768  	result := new(params.ErrorResults)
   769  	results := params.ErrorResults{
   770  		Results: make([]params.ErrorResult, len(expectedArgs.Pools)),
   771  	}
   772  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   773  	mockFacadeCaller.EXPECT().FacadeCall("UpdatePool", expectedArgs, result).SetArg(2, results).Return(nil)
   774  
   775  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   776  	err := storageClient.UpdatePool(poolName, providerType, poolConfig)
   777  	c.Assert(err, jc.ErrorIsNil)
   778  }
   779  
   780  func (s *storageMockSuite) TestUpdatePoolFacadeCallError(c *gc.C) {
   781  	ctrl := gomock.NewController(c)
   782  	defer ctrl.Finish()
   783  
   784  	msg := "facade failure"
   785  	result := new(params.ErrorResults)
   786  	results := params.ErrorResults{
   787  		Results: make([]params.ErrorResult, 1),
   788  	}
   789  	mockFacadeCaller := basemocks.NewMockFacadeCaller(ctrl)
   790  	mockFacadeCaller.EXPECT().FacadeCall("UpdatePool", gomock.AssignableToTypeOf(params.StoragePoolArgs{}), result).SetArg(2, results).Return(errors.New(msg))
   791  
   792  	storageClient := storage.NewClientFromCaller(mockFacadeCaller)
   793  	err := storageClient.UpdatePool("", "", nil)
   794  	c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
   795  }