github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/storageprovisioner/storageprovisioner_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storageprovisioner_test
     5  
     6  import (
     7  	"sort"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/apiserver/storageprovisioner"
    17  	apiservertesting "github.com/juju/juju/apiserver/testing"
    18  	"github.com/juju/juju/environs/tags"
    19  	"github.com/juju/juju/instance"
    20  	jujutesting "github.com/juju/juju/juju/testing"
    21  	"github.com/juju/juju/state"
    22  	statetesting "github.com/juju/juju/state/testing"
    23  	"github.com/juju/juju/storage"
    24  	"github.com/juju/juju/storage/provider/dummy"
    25  	"github.com/juju/juju/storage/provider/registry"
    26  	"github.com/juju/juju/testing"
    27  	"github.com/juju/juju/testing/factory"
    28  )
    29  
    30  var _ = gc.Suite(&provisionerSuite{})
    31  
    32  type provisionerSuite struct {
    33  	// TODO(wallyworld) remove JujuConnSuite
    34  	jujutesting.JujuConnSuite
    35  
    36  	factory    *factory.Factory
    37  	resources  *common.Resources
    38  	authorizer *apiservertesting.FakeAuthorizer
    39  	api        *storageprovisioner.StorageProvisionerAPI
    40  }
    41  
    42  func (s *provisionerSuite) SetUpSuite(c *gc.C) {
    43  	s.JujuConnSuite.SetUpSuite(c)
    44  
    45  	registry.RegisterProvider("environscoped", &dummy.StorageProvider{
    46  		StorageScope: storage.ScopeEnviron,
    47  	})
    48  	registry.RegisterProvider("machinescoped", &dummy.StorageProvider{
    49  		StorageScope: storage.ScopeMachine,
    50  	})
    51  	registry.RegisterEnvironStorageProviders(
    52  		"dummy", "environscoped", "machinescoped",
    53  	)
    54  	s.AddCleanup(func(c *gc.C) {
    55  		registry.RegisterProvider("environscoped", nil)
    56  		registry.RegisterProvider("machinescoped", nil)
    57  	})
    58  }
    59  
    60  func (s *provisionerSuite) SetUpTest(c *gc.C) {
    61  	s.JujuConnSuite.SetUpTest(c)
    62  	s.factory = factory.NewFactory(s.State)
    63  	s.resources = common.NewResources()
    64  	// Create the resource registry separately to track invocations to
    65  	// Register.
    66  	s.resources = common.NewResources()
    67  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    68  
    69  	var err error
    70  	s.authorizer = &apiservertesting.FakeAuthorizer{
    71  		Tag:            names.NewMachineTag("0"),
    72  		EnvironManager: true,
    73  	}
    74  	s.api, err = storageprovisioner.NewStorageProvisionerAPI(s.State, s.resources, s.authorizer)
    75  	c.Assert(err, jc.ErrorIsNil)
    76  }
    77  
    78  func (s *provisionerSuite) TestNewStorageProvisionerAPINonMachine(c *gc.C) {
    79  	tag := names.NewUnitTag("mysql/0")
    80  	authorizer := &apiservertesting.FakeAuthorizer{Tag: tag}
    81  	_, err := storageprovisioner.NewStorageProvisionerAPI(s.State, common.NewResources(), authorizer)
    82  	c.Assert(err, gc.ErrorMatches, "permission denied")
    83  }
    84  
    85  func (s *provisionerSuite) setupVolumes(c *gc.C) {
    86  	s.factory.MakeMachine(c, &factory.MachineParams{
    87  		InstanceId: instance.Id("inst-id"),
    88  		Volumes: []state.MachineVolumeParams{
    89  			{Volume: state.VolumeParams{Pool: "machinescoped", Size: 1024}},
    90  			{Volume: state.VolumeParams{Pool: "environscoped", Size: 2048}},
    91  			{Volume: state.VolumeParams{Pool: "environscoped", Size: 4096}},
    92  			{
    93  				Volume: state.VolumeParams{Pool: "environscoped", Size: 4096},
    94  				Attachment: state.VolumeAttachmentParams{
    95  					ReadOnly: true,
    96  				},
    97  			},
    98  		},
    99  	})
   100  	// Only provision the first and third volumes.
   101  	err := s.State.SetVolumeInfo(names.NewVolumeTag("0/0"), state.VolumeInfo{
   102  		HardwareId: "123",
   103  		VolumeId:   "abc",
   104  		Size:       1024,
   105  		Persistent: true,
   106  	})
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	err = s.State.SetVolumeInfo(names.NewVolumeTag("2"), state.VolumeInfo{
   109  		HardwareId: "456",
   110  		VolumeId:   "def",
   111  		Size:       4096,
   112  	})
   113  	c.Assert(err, jc.ErrorIsNil)
   114  
   115  	// Make a machine without storage for tests to use.
   116  	s.factory.MakeMachine(c, nil)
   117  
   118  	// Make an unprovisioned machine with storage for tests to use.
   119  	// TODO(axw) extend testing/factory to allow creating unprovisioned
   120  	// machines.
   121  	_, err = s.State.AddOneMachine(state.MachineTemplate{
   122  		Series: "quantal",
   123  		Jobs:   []state.MachineJob{state.JobHostUnits},
   124  		Volumes: []state.MachineVolumeParams{
   125  			{Volume: state.VolumeParams{Pool: "environscoped", Size: 2048}},
   126  		},
   127  	})
   128  	c.Assert(err, jc.ErrorIsNil)
   129  }
   130  
   131  func (s *provisionerSuite) setupFilesystems(c *gc.C) {
   132  	s.factory.MakeMachine(c, &factory.MachineParams{
   133  		InstanceId: instance.Id("inst-id"),
   134  		Filesystems: []state.MachineFilesystemParams{{
   135  			Filesystem: state.FilesystemParams{Pool: "machinescoped", Size: 1024},
   136  			Attachment: state.FilesystemAttachmentParams{
   137  				Location: "/srv",
   138  				ReadOnly: true,
   139  			},
   140  		}, {
   141  			Filesystem: state.FilesystemParams{Pool: "environscoped", Size: 2048},
   142  		}, {
   143  			Filesystem: state.FilesystemParams{Pool: "environscoped", Size: 4096},
   144  		}},
   145  	})
   146  
   147  	// Only provision the first and third filesystems.
   148  	err := s.State.SetFilesystemInfo(names.NewFilesystemTag("0/0"), state.FilesystemInfo{
   149  		FilesystemId: "abc",
   150  		Size:         1024,
   151  	})
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	err = s.State.SetFilesystemInfo(names.NewFilesystemTag("2"), state.FilesystemInfo{
   154  		FilesystemId: "def",
   155  		Size:         4096,
   156  	})
   157  	c.Assert(err, jc.ErrorIsNil)
   158  
   159  	// Make a machine without storage for tests to use.
   160  	s.factory.MakeMachine(c, nil)
   161  
   162  	// Make an unprovisioned machine with storage for tests to use.
   163  	// TODO(axw) extend testing/factory to allow creating unprovisioned
   164  	// machines.
   165  	_, err = s.State.AddOneMachine(state.MachineTemplate{
   166  		Series: "quantal",
   167  		Jobs:   []state.MachineJob{state.JobHostUnits},
   168  		Filesystems: []state.MachineFilesystemParams{{
   169  			Filesystem: state.FilesystemParams{Pool: "environscoped", Size: 2048},
   170  		}},
   171  	})
   172  	c.Assert(err, jc.ErrorIsNil)
   173  }
   174  
   175  func (s *provisionerSuite) TestVolumesMachine(c *gc.C) {
   176  	s.setupVolumes(c)
   177  	s.authorizer.EnvironManager = false
   178  
   179  	results, err := s.api.Volumes(params.Entities{
   180  		Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}},
   181  	})
   182  	c.Assert(err, jc.ErrorIsNil)
   183  	c.Assert(results, gc.DeepEquals, params.VolumeResults{
   184  		Results: []params.VolumeResult{
   185  			{Result: params.Volume{
   186  				VolumeTag: "volume-0-0",
   187  				Info: params.VolumeInfo{
   188  					VolumeId:   "abc",
   189  					HardwareId: "123",
   190  					Size:       1024,
   191  					Persistent: true,
   192  				},
   193  			}},
   194  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   195  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   196  		},
   197  	})
   198  }
   199  
   200  func (s *provisionerSuite) TestVolumesEnviron(c *gc.C) {
   201  	s.setupVolumes(c)
   202  	s.authorizer.Tag = names.NewMachineTag("2") // neither 0 nor 1
   203  
   204  	results, err := s.api.Volumes(params.Entities{
   205  		Entities: []params.Entity{
   206  			{"volume-0-0"},
   207  			{"volume-1"},
   208  			{"volume-2"},
   209  			{"volume-42"},
   210  		},
   211  	})
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	c.Assert(results, gc.DeepEquals, params.VolumeResults{
   214  		Results: []params.VolumeResult{
   215  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   216  			{Error: common.ServerError(errors.NotProvisionedf(`volume "1"`))},
   217  			{Result: params.Volume{
   218  				VolumeTag: "volume-2",
   219  				Info: params.VolumeInfo{
   220  					VolumeId:   "def",
   221  					HardwareId: "456",
   222  					Size:       4096,
   223  				},
   224  			}},
   225  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   226  		},
   227  	})
   228  }
   229  
   230  func (s *provisionerSuite) TestVolumesEmptyArgs(c *gc.C) {
   231  	results, err := s.api.Volumes(params.Entities{})
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	c.Assert(results.Results, gc.HasLen, 0)
   234  }
   235  
   236  func (s *provisionerSuite) TestFilesystems(c *gc.C) {
   237  	s.setupFilesystems(c)
   238  	s.authorizer.Tag = names.NewMachineTag("2") // neither 0 nor 1
   239  
   240  	results, err := s.api.Filesystems(params.Entities{
   241  		Entities: []params.Entity{
   242  			{"filesystem-0-0"},
   243  			{"filesystem-1"},
   244  			{"filesystem-2"},
   245  			{"filesystem-42"},
   246  		},
   247  	})
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	c.Assert(results, jc.DeepEquals, params.FilesystemResults{
   250  		Results: []params.FilesystemResult{
   251  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   252  			{Error: common.ServerError(errors.NotProvisionedf(`filesystem "1"`))},
   253  			{Result: params.Filesystem{
   254  				FilesystemTag: "filesystem-2",
   255  				Info: params.FilesystemInfo{
   256  					FilesystemId: "def",
   257  					Size:         4096,
   258  				},
   259  			}},
   260  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   261  		},
   262  	})
   263  }
   264  
   265  func (s *provisionerSuite) TestVolumeAttachments(c *gc.C) {
   266  	s.setupVolumes(c)
   267  	s.authorizer.EnvironManager = false
   268  
   269  	err := s.State.SetVolumeAttachmentInfo(
   270  		names.NewMachineTag("0"),
   271  		names.NewVolumeTag("0/0"),
   272  		state.VolumeAttachmentInfo{DeviceName: "xvdf1"},
   273  	)
   274  	c.Assert(err, jc.ErrorIsNil)
   275  
   276  	results, err := s.api.VolumeAttachments(params.MachineStorageIds{
   277  		Ids: []params.MachineStorageId{{
   278  			MachineTag:    "machine-0",
   279  			AttachmentTag: "volume-0-0",
   280  		}, {
   281  			MachineTag:    "machine-0",
   282  			AttachmentTag: "volume-2", // volume attachment not provisioned
   283  		}, {
   284  			MachineTag:    "machine-0",
   285  			AttachmentTag: "volume-42",
   286  		}},
   287  	})
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(results, jc.DeepEquals, params.VolumeAttachmentResults{
   290  		Results: []params.VolumeAttachmentResult{
   291  			{Result: params.VolumeAttachment{
   292  				VolumeTag:  "volume-0-0",
   293  				MachineTag: "machine-0",
   294  				Info: params.VolumeAttachmentInfo{
   295  					DeviceName: "xvdf1",
   296  				},
   297  			}},
   298  			{Error: &params.Error{
   299  				Code:    params.CodeNotProvisioned,
   300  				Message: `volume attachment "2" on "0" not provisioned`,
   301  			}},
   302  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   303  		},
   304  	})
   305  }
   306  
   307  func (s *provisionerSuite) TestFilesystemAttachments(c *gc.C) {
   308  	s.setupFilesystems(c)
   309  	s.authorizer.EnvironManager = false
   310  
   311  	err := s.State.SetFilesystemAttachmentInfo(
   312  		names.NewMachineTag("0"),
   313  		names.NewFilesystemTag("0/0"),
   314  		state.FilesystemAttachmentInfo{
   315  			MountPoint: "/srv",
   316  			ReadOnly:   true,
   317  		},
   318  	)
   319  	c.Assert(err, jc.ErrorIsNil)
   320  
   321  	results, err := s.api.FilesystemAttachments(params.MachineStorageIds{
   322  		Ids: []params.MachineStorageId{{
   323  			MachineTag:    "machine-0",
   324  			AttachmentTag: "filesystem-0-0",
   325  		}, {
   326  			MachineTag:    "machine-0",
   327  			AttachmentTag: "filesystem-2", // filesystem attachment not provisioned
   328  		}, {
   329  			MachineTag:    "machine-0",
   330  			AttachmentTag: "filesystem-42",
   331  		}},
   332  	})
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentResults{
   335  		Results: []params.FilesystemAttachmentResult{
   336  			{Result: params.FilesystemAttachment{
   337  				FilesystemTag: "filesystem-0-0",
   338  				MachineTag:    "machine-0",
   339  				Info: params.FilesystemAttachmentInfo{
   340  					MountPoint: "/srv",
   341  					ReadOnly:   true,
   342  				},
   343  			}},
   344  			{Error: &params.Error{
   345  				Code:    params.CodeNotProvisioned,
   346  				Message: `filesystem attachment "2" on "0" not provisioned`,
   347  			}},
   348  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   349  		},
   350  	})
   351  }
   352  
   353  func (s *provisionerSuite) TestVolumeParams(c *gc.C) {
   354  	s.setupVolumes(c)
   355  	results, err := s.api.VolumeParams(params.Entities{
   356  		Entities: []params.Entity{
   357  			{"volume-0-0"},
   358  			{"volume-1"},
   359  			{"volume-3"},
   360  			{"volume-42"},
   361  		},
   362  	})
   363  	c.Assert(err, jc.ErrorIsNil)
   364  	c.Assert(results, jc.DeepEquals, params.VolumeParamsResults{
   365  		Results: []params.VolumeParamsResult{
   366  			{Result: params.VolumeParams{
   367  				VolumeTag: "volume-0-0",
   368  				Size:      1024,
   369  				Provider:  "machinescoped",
   370  				Tags: map[string]string{
   371  					tags.JujuController: testing.ModelTag.Id(),
   372  					tags.JujuModel:      testing.ModelTag.Id(),
   373  				},
   374  				Attachment: &params.VolumeAttachmentParams{
   375  					MachineTag: "machine-0",
   376  					VolumeTag:  "volume-0-0",
   377  					Provider:   "machinescoped",
   378  					InstanceId: "inst-id",
   379  				},
   380  			}},
   381  			{Result: params.VolumeParams{
   382  				VolumeTag: "volume-1",
   383  				Size:      2048,
   384  				Provider:  "environscoped",
   385  				Tags: map[string]string{
   386  					tags.JujuController: testing.ModelTag.Id(),
   387  					tags.JujuModel:      testing.ModelTag.Id(),
   388  				},
   389  				Attachment: &params.VolumeAttachmentParams{
   390  					MachineTag: "machine-0",
   391  					VolumeTag:  "volume-1",
   392  					Provider:   "environscoped",
   393  					InstanceId: "inst-id",
   394  				},
   395  			}},
   396  			{Result: params.VolumeParams{
   397  				VolumeTag: "volume-3",
   398  				Size:      4096,
   399  				Provider:  "environscoped",
   400  				Tags: map[string]string{
   401  					tags.JujuController: testing.ModelTag.Id(),
   402  					tags.JujuModel:      testing.ModelTag.Id(),
   403  				},
   404  				Attachment: &params.VolumeAttachmentParams{
   405  					MachineTag: "machine-0",
   406  					VolumeTag:  "volume-3",
   407  					Provider:   "environscoped",
   408  					InstanceId: "inst-id",
   409  					ReadOnly:   true,
   410  				},
   411  			}},
   412  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   413  		},
   414  	})
   415  }
   416  
   417  func (s *provisionerSuite) TestVolumeParamsEmptyArgs(c *gc.C) {
   418  	results, err := s.api.VolumeParams(params.Entities{})
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	c.Assert(results.Results, gc.HasLen, 0)
   421  }
   422  
   423  func (s *provisionerSuite) TestFilesystemParams(c *gc.C) {
   424  	s.setupFilesystems(c)
   425  	results, err := s.api.FilesystemParams(params.Entities{
   426  		Entities: []params.Entity{{"filesystem-0-0"}, {"filesystem-1"}, {"filesystem-42"}},
   427  	})
   428  	c.Assert(err, jc.ErrorIsNil)
   429  	c.Assert(results, jc.DeepEquals, params.FilesystemParamsResults{
   430  		Results: []params.FilesystemParamsResult{
   431  			{Result: params.FilesystemParams{
   432  				FilesystemTag: "filesystem-0-0",
   433  				Size:          1024,
   434  				Provider:      "machinescoped",
   435  				Tags: map[string]string{
   436  					tags.JujuController: testing.ModelTag.Id(),
   437  					tags.JujuModel:      testing.ModelTag.Id(),
   438  				},
   439  			}},
   440  			{Result: params.FilesystemParams{
   441  				FilesystemTag: "filesystem-1",
   442  				Size:          2048,
   443  				Provider:      "environscoped",
   444  				Tags: map[string]string{
   445  					tags.JujuController: testing.ModelTag.Id(),
   446  					tags.JujuModel:      testing.ModelTag.Id(),
   447  				},
   448  			}},
   449  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   450  		},
   451  	})
   452  }
   453  
   454  func (s *provisionerSuite) TestVolumeAttachmentParams(c *gc.C) {
   455  	s.setupVolumes(c)
   456  
   457  	err := s.State.SetVolumeInfo(names.NewVolumeTag("3"), state.VolumeInfo{
   458  		HardwareId: "123",
   459  		VolumeId:   "xyz",
   460  		Size:       1024,
   461  	})
   462  	c.Assert(err, jc.ErrorIsNil)
   463  
   464  	err = s.State.SetVolumeAttachmentInfo(
   465  		names.NewMachineTag("0"),
   466  		names.NewVolumeTag("3"),
   467  		state.VolumeAttachmentInfo{
   468  			DeviceName: "xvdf1",
   469  			ReadOnly:   true,
   470  		},
   471  	)
   472  	c.Assert(err, jc.ErrorIsNil)
   473  
   474  	results, err := s.api.VolumeAttachmentParams(params.MachineStorageIds{
   475  		Ids: []params.MachineStorageId{{
   476  			MachineTag:    "machine-0",
   477  			AttachmentTag: "volume-0-0",
   478  		}, {
   479  			MachineTag:    "machine-0",
   480  			AttachmentTag: "volume-1",
   481  		}, {
   482  			MachineTag:    "machine-0",
   483  			AttachmentTag: "volume-3",
   484  		}, {
   485  			MachineTag:    "machine-2",
   486  			AttachmentTag: "volume-4",
   487  		}, {
   488  			MachineTag:    "machine-0",
   489  			AttachmentTag: "volume-42",
   490  		}},
   491  	})
   492  	c.Assert(err, jc.ErrorIsNil)
   493  	c.Assert(results, jc.DeepEquals, params.VolumeAttachmentParamsResults{
   494  		Results: []params.VolumeAttachmentParamsResult{
   495  			{Result: params.VolumeAttachmentParams{
   496  				MachineTag: "machine-0",
   497  				VolumeTag:  "volume-0-0",
   498  				InstanceId: "inst-id",
   499  				VolumeId:   "abc",
   500  				Provider:   "machinescoped",
   501  			}},
   502  			{Result: params.VolumeAttachmentParams{
   503  				MachineTag: "machine-0",
   504  				VolumeTag:  "volume-1",
   505  				InstanceId: "inst-id",
   506  				Provider:   "environscoped",
   507  			}},
   508  			{Result: params.VolumeAttachmentParams{
   509  				MachineTag: "machine-0",
   510  				VolumeTag:  "volume-3",
   511  				InstanceId: "inst-id",
   512  				VolumeId:   "xyz",
   513  				Provider:   "environscoped",
   514  				ReadOnly:   true,
   515  			}},
   516  			{Result: params.VolumeAttachmentParams{
   517  				MachineTag: "machine-2",
   518  				VolumeTag:  "volume-4",
   519  				Provider:   "environscoped",
   520  			}},
   521  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   522  		},
   523  	})
   524  }
   525  
   526  func (s *provisionerSuite) TestFilesystemAttachmentParams(c *gc.C) {
   527  	s.setupFilesystems(c)
   528  
   529  	err := s.State.SetFilesystemInfo(names.NewFilesystemTag("1"), state.FilesystemInfo{
   530  		FilesystemId: "fsid",
   531  		Size:         1024,
   532  	})
   533  	c.Assert(err, jc.ErrorIsNil)
   534  
   535  	err = s.State.SetFilesystemAttachmentInfo(
   536  		names.NewMachineTag("0"),
   537  		names.NewFilesystemTag("1"),
   538  		state.FilesystemAttachmentInfo{
   539  			MountPoint: "/in/the/place",
   540  		},
   541  	)
   542  	c.Assert(err, jc.ErrorIsNil)
   543  
   544  	results, err := s.api.FilesystemAttachmentParams(params.MachineStorageIds{
   545  		Ids: []params.MachineStorageId{{
   546  			MachineTag:    "machine-0",
   547  			AttachmentTag: "filesystem-0-0",
   548  		}, {
   549  			MachineTag:    "machine-0",
   550  			AttachmentTag: "filesystem-1",
   551  		}, {
   552  			MachineTag:    "machine-2",
   553  			AttachmentTag: "filesystem-3",
   554  		}, {
   555  			MachineTag:    "machine-0",
   556  			AttachmentTag: "filesystem-42",
   557  		}},
   558  	})
   559  	c.Assert(err, jc.ErrorIsNil)
   560  	c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentParamsResults{
   561  		Results: []params.FilesystemAttachmentParamsResult{
   562  			{Result: params.FilesystemAttachmentParams{
   563  				MachineTag:    "machine-0",
   564  				FilesystemTag: "filesystem-0-0",
   565  				InstanceId:    "inst-id",
   566  				FilesystemId:  "abc",
   567  				Provider:      "machinescoped",
   568  				MountPoint:    "/srv",
   569  				ReadOnly:      true,
   570  			}},
   571  			{Result: params.FilesystemAttachmentParams{
   572  				MachineTag:    "machine-0",
   573  				FilesystemTag: "filesystem-1",
   574  				InstanceId:    "inst-id",
   575  				FilesystemId:  "fsid",
   576  				Provider:      "environscoped",
   577  				MountPoint:    "/in/the/place",
   578  			}},
   579  			{Result: params.FilesystemAttachmentParams{
   580  				MachineTag:    "machine-2",
   581  				FilesystemTag: "filesystem-3",
   582  				Provider:      "environscoped",
   583  			}},
   584  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   585  		},
   586  	})
   587  }
   588  
   589  func (s *provisionerSuite) TestSetVolumeAttachmentInfo(c *gc.C) {
   590  	s.setupVolumes(c)
   591  
   592  	err := s.State.SetVolumeInfo(names.NewVolumeTag("4"), state.VolumeInfo{
   593  		VolumeId: "whatever",
   594  		Size:     1024,
   595  	})
   596  	c.Assert(err, jc.ErrorIsNil)
   597  
   598  	results, err := s.api.SetVolumeAttachmentInfo(params.VolumeAttachments{
   599  		VolumeAttachments: []params.VolumeAttachment{{
   600  			MachineTag: "machine-0",
   601  			VolumeTag:  "volume-0-0",
   602  			Info: params.VolumeAttachmentInfo{
   603  				DeviceName: "sda",
   604  				ReadOnly:   true,
   605  			},
   606  		}, {
   607  			MachineTag: "machine-0",
   608  			VolumeTag:  "volume-1",
   609  			Info: params.VolumeAttachmentInfo{
   610  				DeviceName: "sdb",
   611  			},
   612  		}, {
   613  			MachineTag: "machine-2",
   614  			VolumeTag:  "volume-4",
   615  			Info: params.VolumeAttachmentInfo{
   616  				DeviceName: "sdc",
   617  			},
   618  		}, {
   619  			MachineTag: "machine-0",
   620  			VolumeTag:  "volume-42",
   621  			Info: params.VolumeAttachmentInfo{
   622  				DeviceName: "sdd",
   623  			},
   624  		}},
   625  	})
   626  	c.Assert(err, jc.ErrorIsNil)
   627  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
   628  		Results: []params.ErrorResult{
   629  			{},
   630  			{Error: &params.Error{Message: `cannot set info for volume attachment 1:0: volume "1" not provisioned`, Code: "not provisioned"}},
   631  			{Error: &params.Error{Message: `cannot set info for volume attachment 4:2: machine 2 not provisioned`, Code: "not provisioned"}},
   632  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   633  		},
   634  	})
   635  }
   636  
   637  func (s *provisionerSuite) TestSetFilesystemAttachmentInfo(c *gc.C) {
   638  	s.setupFilesystems(c)
   639  
   640  	err := s.State.SetFilesystemInfo(names.NewFilesystemTag("3"), state.FilesystemInfo{
   641  		FilesystemId: "whatever",
   642  		Size:         1024,
   643  	})
   644  	c.Assert(err, jc.ErrorIsNil)
   645  
   646  	results, err := s.api.SetFilesystemAttachmentInfo(params.FilesystemAttachments{
   647  		FilesystemAttachments: []params.FilesystemAttachment{{
   648  			MachineTag:    "machine-0",
   649  			FilesystemTag: "filesystem-0-0",
   650  			Info: params.FilesystemAttachmentInfo{
   651  				MountPoint: "/srv/a",
   652  				ReadOnly:   true,
   653  			},
   654  		}, {
   655  			MachineTag:    "machine-0",
   656  			FilesystemTag: "filesystem-1",
   657  			Info: params.FilesystemAttachmentInfo{
   658  				MountPoint: "/srv/b",
   659  			},
   660  		}, {
   661  			MachineTag:    "machine-2",
   662  			FilesystemTag: "filesystem-3",
   663  			Info: params.FilesystemAttachmentInfo{
   664  				MountPoint: "/srv/c",
   665  			},
   666  		}, {
   667  			MachineTag:    "machine-0",
   668  			FilesystemTag: "filesystem-42",
   669  			Info: params.FilesystemAttachmentInfo{
   670  				MountPoint: "/srv/d",
   671  			},
   672  		}},
   673  	})
   674  	c.Assert(err, jc.ErrorIsNil)
   675  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
   676  		Results: []params.ErrorResult{
   677  			{},
   678  			{Error: &params.Error{Message: `cannot set info for filesystem attachment 1:0: filesystem "1" not provisioned`, Code: "not provisioned"}},
   679  			{Error: &params.Error{Message: `cannot set info for filesystem attachment 3:2: machine 2 not provisioned`, Code: "not provisioned"}},
   680  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   681  		},
   682  	})
   683  }
   684  
   685  func (s *provisionerSuite) TestWatchVolumes(c *gc.C) {
   686  	s.setupVolumes(c)
   687  	s.factory.MakeMachine(c, nil)
   688  	c.Assert(s.resources.Count(), gc.Equals, 0)
   689  
   690  	args := params.Entities{Entities: []params.Entity{
   691  		{"machine-0"},
   692  		{s.State.ModelTag().String()},
   693  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   694  		{"machine-1"},
   695  		{"machine-42"}},
   696  	}
   697  	result, err := s.api.WatchVolumes(args)
   698  	c.Assert(err, jc.ErrorIsNil)
   699  	sort.Strings(result.Results[1].Changes)
   700  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   701  		Results: []params.StringsWatchResult{
   702  			{StringsWatcherId: "1", Changes: []string{"0/0"}},
   703  			{StringsWatcherId: "2", Changes: []string{"1", "2", "3", "4"}},
   704  			{Error: apiservertesting.ErrUnauthorized},
   705  			{Error: apiservertesting.ErrUnauthorized},
   706  			{Error: apiservertesting.ErrUnauthorized},
   707  		},
   708  	})
   709  
   710  	// Verify the resources were registered and stop them when done.
   711  	c.Assert(s.resources.Count(), gc.Equals, 2)
   712  	v0Watcher := s.resources.Get("1")
   713  	defer statetesting.AssertStop(c, v0Watcher)
   714  	v1Watcher := s.resources.Get("2")
   715  	defer statetesting.AssertStop(c, v1Watcher)
   716  
   717  	// Check that the Watch has consumed the initial events ("returned" in
   718  	// the Watch call)
   719  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   720  	wc.AssertNoChange()
   721  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   722  	wc.AssertNoChange()
   723  }
   724  
   725  func (s *provisionerSuite) TestWatchVolumeAttachments(c *gc.C) {
   726  	s.setupVolumes(c)
   727  	s.factory.MakeMachine(c, nil)
   728  	c.Assert(s.resources.Count(), gc.Equals, 0)
   729  
   730  	args := params.Entities{Entities: []params.Entity{
   731  		{"machine-0"},
   732  		{s.State.ModelTag().String()},
   733  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   734  		{"machine-1"},
   735  		{"machine-42"}},
   736  	}
   737  	result, err := s.api.WatchVolumeAttachments(args)
   738  	c.Assert(err, jc.ErrorIsNil)
   739  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
   740  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
   741  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
   742  		Results: []params.MachineStorageIdsWatchResult{
   743  			{
   744  				MachineStorageIdsWatcherId: "1",
   745  				Changes: []params.MachineStorageId{{
   746  					MachineTag:    "machine-0",
   747  					AttachmentTag: "volume-0-0",
   748  				}},
   749  			},
   750  			{
   751  				MachineStorageIdsWatcherId: "2",
   752  				Changes: []params.MachineStorageId{{
   753  					MachineTag:    "machine-0",
   754  					AttachmentTag: "volume-1",
   755  				}, {
   756  					MachineTag:    "machine-0",
   757  					AttachmentTag: "volume-2",
   758  				}, {
   759  					MachineTag:    "machine-0",
   760  					AttachmentTag: "volume-3",
   761  				}, {
   762  					MachineTag:    "machine-2",
   763  					AttachmentTag: "volume-4",
   764  				}},
   765  			},
   766  			{Error: apiservertesting.ErrUnauthorized},
   767  			{Error: apiservertesting.ErrUnauthorized},
   768  			{Error: apiservertesting.ErrUnauthorized},
   769  		},
   770  	})
   771  
   772  	// Verify the resources were registered and stop them when done.
   773  	c.Assert(s.resources.Count(), gc.Equals, 2)
   774  	v0Watcher := s.resources.Get("1")
   775  	defer statetesting.AssertStop(c, v0Watcher)
   776  	v1Watcher := s.resources.Get("2")
   777  	defer statetesting.AssertStop(c, v1Watcher)
   778  
   779  	// Check that the Watch has consumed the initial events ("returned" in
   780  	// the Watch call)
   781  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   782  	wc.AssertNoChange()
   783  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   784  	wc.AssertNoChange()
   785  }
   786  
   787  func (s *provisionerSuite) TestWatchFilesystems(c *gc.C) {
   788  	s.setupFilesystems(c)
   789  	c.Assert(s.resources.Count(), gc.Equals, 0)
   790  
   791  	args := params.Entities{Entities: []params.Entity{
   792  		{"machine-0"},
   793  		{s.State.ModelTag().String()},
   794  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   795  		{"machine-1"},
   796  		{"machine-42"}},
   797  	}
   798  	result, err := s.api.WatchFilesystems(args)
   799  	c.Assert(err, jc.ErrorIsNil)
   800  	sort.Strings(result.Results[1].Changes)
   801  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   802  		Results: []params.StringsWatchResult{
   803  			{
   804  				StringsWatcherId: "1",
   805  				Changes:          []string{"0/0"},
   806  			},
   807  			{
   808  				StringsWatcherId: "2",
   809  				Changes:          []string{"1", "2", "3"},
   810  			},
   811  			{Error: apiservertesting.ErrUnauthorized},
   812  			{Error: apiservertesting.ErrUnauthorized},
   813  			{Error: apiservertesting.ErrUnauthorized},
   814  		},
   815  	})
   816  
   817  	// Verify the resources were registered and stop them when done.
   818  	c.Assert(s.resources.Count(), gc.Equals, 2)
   819  	v0Watcher := s.resources.Get("1")
   820  	defer statetesting.AssertStop(c, v0Watcher)
   821  	v1Watcher := s.resources.Get("2")
   822  	defer statetesting.AssertStop(c, v1Watcher)
   823  
   824  	// Check that the Watch has consumed the initial events ("returned" in
   825  	// the Watch call)
   826  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   827  	wc.AssertNoChange()
   828  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   829  	wc.AssertNoChange()
   830  }
   831  
   832  func (s *provisionerSuite) TestWatchFilesystemAttachments(c *gc.C) {
   833  	s.setupFilesystems(c)
   834  	c.Assert(s.resources.Count(), gc.Equals, 0)
   835  
   836  	args := params.Entities{Entities: []params.Entity{
   837  		{"machine-0"},
   838  		{s.State.ModelTag().String()},
   839  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   840  		{"machine-1"},
   841  		{"machine-42"}},
   842  	}
   843  	result, err := s.api.WatchFilesystemAttachments(args)
   844  	c.Assert(err, jc.ErrorIsNil)
   845  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
   846  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
   847  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
   848  		Results: []params.MachineStorageIdsWatchResult{
   849  			{
   850  				MachineStorageIdsWatcherId: "1",
   851  				Changes: []params.MachineStorageId{{
   852  					MachineTag:    "machine-0",
   853  					AttachmentTag: "filesystem-0-0",
   854  				}},
   855  			},
   856  			{
   857  				MachineStorageIdsWatcherId: "2",
   858  				Changes: []params.MachineStorageId{{
   859  					MachineTag:    "machine-0",
   860  					AttachmentTag: "filesystem-1",
   861  				}, {
   862  					MachineTag:    "machine-0",
   863  					AttachmentTag: "filesystem-2",
   864  				}, {
   865  					MachineTag:    "machine-2",
   866  					AttachmentTag: "filesystem-3",
   867  				}},
   868  			},
   869  			{Error: apiservertesting.ErrUnauthorized},
   870  			{Error: apiservertesting.ErrUnauthorized},
   871  			{Error: apiservertesting.ErrUnauthorized},
   872  		},
   873  	})
   874  
   875  	// Verify the resources were registered and stop them when done.
   876  	c.Assert(s.resources.Count(), gc.Equals, 2)
   877  	v0Watcher := s.resources.Get("1")
   878  	defer statetesting.AssertStop(c, v0Watcher)
   879  	v1Watcher := s.resources.Get("2")
   880  	defer statetesting.AssertStop(c, v1Watcher)
   881  
   882  	// Check that the Watch has consumed the initial events ("returned" in
   883  	// the Watch call)
   884  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   885  	wc.AssertNoChange()
   886  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   887  	wc.AssertNoChange()
   888  }
   889  
   890  func (s *provisionerSuite) TestWatchBlockDevices(c *gc.C) {
   891  	s.factory.MakeMachine(c, nil)
   892  	c.Assert(s.resources.Count(), gc.Equals, 0)
   893  
   894  	args := params.Entities{Entities: []params.Entity{
   895  		{"machine-0"},
   896  		{"service-mysql"},
   897  		{"machine-1"},
   898  		{"machine-42"}},
   899  	}
   900  	results, err := s.api.WatchBlockDevices(args)
   901  	c.Assert(err, jc.ErrorIsNil)
   902  	c.Assert(results, jc.DeepEquals, params.NotifyWatchResults{
   903  		Results: []params.NotifyWatchResult{
   904  			{NotifyWatcherId: "1"},
   905  			{Error: &params.Error{Message: `"service-mysql" is not a valid machine tag`}},
   906  			{Error: apiservertesting.ErrUnauthorized},
   907  			{Error: apiservertesting.ErrUnauthorized},
   908  		},
   909  	})
   910  
   911  	// Verify the resources were registered and stop them when done.
   912  	c.Assert(s.resources.Count(), gc.Equals, 1)
   913  	watcher := s.resources.Get("1")
   914  	defer statetesting.AssertStop(c, watcher)
   915  
   916  	// Check that the Watch has consumed the initial event.
   917  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
   918  	wc.AssertNoChange()
   919  
   920  	m, err := s.State.Machine("0")
   921  	c.Assert(err, jc.ErrorIsNil)
   922  	err = m.SetMachineBlockDevices(state.BlockDeviceInfo{
   923  		DeviceName: "sda",
   924  		Size:       123,
   925  	})
   926  	c.Assert(err, jc.ErrorIsNil)
   927  	wc.AssertOneChange()
   928  }
   929  
   930  func (s *provisionerSuite) TestVolumeBlockDevices(c *gc.C) {
   931  	s.setupVolumes(c)
   932  	s.factory.MakeMachine(c, nil)
   933  
   934  	err := s.State.SetVolumeAttachmentInfo(
   935  		names.NewMachineTag("0"),
   936  		names.NewVolumeTag("0/0"),
   937  		state.VolumeAttachmentInfo{},
   938  	)
   939  	c.Assert(err, jc.ErrorIsNil)
   940  
   941  	machine0, err := s.State.Machine("0")
   942  	c.Assert(err, jc.ErrorIsNil)
   943  	err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{
   944  		DeviceName: "sda",
   945  		Size:       123,
   946  		HardwareId: "123", // matches volume-0/0
   947  	})
   948  	c.Assert(err, jc.ErrorIsNil)
   949  
   950  	args := params.MachineStorageIds{Ids: []params.MachineStorageId{
   951  		{MachineTag: "machine-0", AttachmentTag: "volume-0-0"},
   952  		{MachineTag: "machine-0", AttachmentTag: "volume-0-1"},
   953  		{MachineTag: "machine-0", AttachmentTag: "volume-0-2"},
   954  		{MachineTag: "machine-1", AttachmentTag: "volume-1"},
   955  		{MachineTag: "machine-42", AttachmentTag: "volume-42"},
   956  		{MachineTag: "service-mysql", AttachmentTag: "volume-1"},
   957  	}}
   958  	results, err := s.api.VolumeBlockDevices(args)
   959  	c.Assert(err, jc.ErrorIsNil)
   960  	c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{
   961  		Results: []params.BlockDeviceResult{
   962  			{Result: storage.BlockDevice{
   963  				DeviceName: "sda",
   964  				Size:       123,
   965  				HardwareId: "123",
   966  			}},
   967  			{Error: apiservertesting.ErrUnauthorized},
   968  			{Error: apiservertesting.ErrUnauthorized},
   969  			{Error: apiservertesting.ErrUnauthorized},
   970  			{Error: apiservertesting.ErrUnauthorized},
   971  			{Error: &params.Error{Message: `"service-mysql" is not a valid machine tag`}},
   972  		},
   973  	})
   974  }
   975  
   976  func (s *provisionerSuite) TestLife(c *gc.C) {
   977  	s.setupVolumes(c)
   978  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
   979  	result, err := s.api.Life(args)
   980  	c.Assert(err, jc.ErrorIsNil)
   981  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   982  		Results: []params.LifeResult{
   983  			{Life: params.Alive},
   984  			{Life: params.Alive},
   985  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
   986  		},
   987  	})
   988  }
   989  
   990  func (s *provisionerSuite) TestAttachmentLife(c *gc.C) {
   991  	s.setupVolumes(c)
   992  
   993  	// TODO(axw) test filesystem attachment life
   994  	// TODO(axw) test Dying
   995  
   996  	results, err := s.api.AttachmentLife(params.MachineStorageIds{
   997  		Ids: []params.MachineStorageId{{
   998  			MachineTag:    "machine-0",
   999  			AttachmentTag: "volume-0-0",
  1000  		}, {
  1001  			MachineTag:    "machine-0",
  1002  			AttachmentTag: "volume-1",
  1003  		}, {
  1004  			MachineTag:    "machine-2",
  1005  			AttachmentTag: "volume-4",
  1006  		}, {
  1007  			MachineTag:    "machine-0",
  1008  			AttachmentTag: "volume-42",
  1009  		}},
  1010  	})
  1011  	c.Assert(err, jc.ErrorIsNil)
  1012  	c.Assert(results, jc.DeepEquals, params.LifeResults{
  1013  		Results: []params.LifeResult{
  1014  			{Life: params.Alive},
  1015  			{Life: params.Alive},
  1016  			{Life: params.Alive},
  1017  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1018  		},
  1019  	})
  1020  }
  1021  
  1022  func (s *provisionerSuite) TestEnsureDead(c *gc.C) {
  1023  	s.setupVolumes(c)
  1024  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
  1025  	result, err := s.api.EnsureDead(args)
  1026  	c.Assert(err, jc.ErrorIsNil)
  1027  	// TODO(wallyworld) - this test will be updated when EnsureDead is supported
  1028  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1029  		Results: []params.ErrorResult{
  1030  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("0/0"), "ensuring death"))},
  1031  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("1"), "ensuring death"))},
  1032  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
  1033  		},
  1034  	})
  1035  }
  1036  
  1037  func (s *provisionerSuite) TestWatchForModelConfigChanges(c *gc.C) {
  1038  	result, err := s.api.WatchForModelConfigChanges()
  1039  	c.Assert(err, jc.ErrorIsNil)
  1040  	c.Assert(result.NotifyWatcherId, gc.Equals, "1")
  1041  
  1042  	// Verify the resource was registered and stop it when done.
  1043  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1044  	watcher := s.resources.Get("1")
  1045  	defer statetesting.AssertStop(c, watcher)
  1046  
  1047  	// Check that the Watch has consumed the initial events ("returned" in
  1048  	// the Watch call)
  1049  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
  1050  	wc.AssertNoChange()
  1051  
  1052  	// Updating config should trigger the watcher.
  1053  	err = s.State.UpdateModelConfig(map[string]interface{}{"what": "ever"}, nil, nil)
  1054  	c.Assert(err, jc.ErrorIsNil)
  1055  	wc.AssertOneChange()
  1056  }
  1057  
  1058  func (s *provisionerSuite) TestModelConfig(c *gc.C) {
  1059  	stateModelConfig, err := s.State.ModelConfig()
  1060  	c.Assert(err, jc.ErrorIsNil)
  1061  
  1062  	result, err := s.api.ModelConfig()
  1063  	c.Assert(err, jc.ErrorIsNil)
  1064  	c.Assert(result.Config, jc.DeepEquals, params.ModelConfig(stateModelConfig.AllAttrs()))
  1065  }
  1066  
  1067  func (s *provisionerSuite) TestRemoveVolumesEnvironManager(c *gc.C) {
  1068  	s.setupVolumes(c)
  1069  	args := params.Entities{Entities: []params.Entity{
  1070  		{"volume-1-0"}, {"volume-1"}, {"volume-2"}, {"volume-42"},
  1071  		{"volume-invalid"}, {"machine-0"},
  1072  	}}
  1073  
  1074  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1075  	c.Assert(err, jc.ErrorIsNil)
  1076  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1077  	c.Assert(err, jc.ErrorIsNil)
  1078  	err = s.State.DestroyVolume(names.NewVolumeTag("1"))
  1079  	c.Assert(err, jc.ErrorIsNil)
  1080  
  1081  	result, err := s.api.Remove(args)
  1082  	c.Assert(err, jc.ErrorIsNil)
  1083  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1084  		Results: []params.ErrorResult{
  1085  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1086  			{Error: nil},
  1087  			{Error: &params.Error{Message: "removing volume 2: volume is not dead"}},
  1088  			{Error: nil},
  1089  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1090  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1091  		},
  1092  	})
  1093  }
  1094  
  1095  func (s *provisionerSuite) TestRemoveFilesystemsEnvironManager(c *gc.C) {
  1096  	s.setupFilesystems(c)
  1097  	args := params.Entities{Entities: []params.Entity{
  1098  		{"filesystem-1-0"}, {"filesystem-1"}, {"filesystem-2"}, {"filesystem-42"},
  1099  		{"filesystem-invalid"}, {"machine-0"},
  1100  	}}
  1101  
  1102  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1103  	c.Assert(err, jc.ErrorIsNil)
  1104  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1105  	c.Assert(err, jc.ErrorIsNil)
  1106  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("1"))
  1107  	c.Assert(err, jc.ErrorIsNil)
  1108  
  1109  	result, err := s.api.Remove(args)
  1110  	c.Assert(err, jc.ErrorIsNil)
  1111  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1112  		Results: []params.ErrorResult{
  1113  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1114  			{Error: nil},
  1115  			{Error: &params.Error{Message: "removing filesystem 2: filesystem is not dead"}},
  1116  			{Error: nil},
  1117  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1118  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1119  		},
  1120  	})
  1121  }
  1122  
  1123  func (s *provisionerSuite) TestRemoveVolumesMachineAgent(c *gc.C) {
  1124  	s.setupVolumes(c)
  1125  	s.authorizer.EnvironManager = false
  1126  	args := params.Entities{Entities: []params.Entity{
  1127  		{"volume-0-0"}, {"volume-0-42"}, {"volume-42"},
  1128  		{"volume-invalid"}, {"machine-0"},
  1129  	}}
  1130  
  1131  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1132  	c.Assert(err, jc.ErrorIsNil)
  1133  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1134  	c.Assert(err, jc.ErrorIsNil)
  1135  	err = s.State.DestroyVolume(names.NewVolumeTag("0/0"))
  1136  	c.Assert(err, jc.ErrorIsNil)
  1137  
  1138  	result, err := s.api.Remove(args)
  1139  	c.Assert(err, jc.ErrorIsNil)
  1140  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1141  		Results: []params.ErrorResult{
  1142  			{Error: nil},
  1143  			{Error: nil},
  1144  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1145  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1146  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1147  		},
  1148  	})
  1149  }
  1150  
  1151  func (s *provisionerSuite) TestRemoveFilesystemsMachineAgent(c *gc.C) {
  1152  	s.setupFilesystems(c)
  1153  	s.authorizer.EnvironManager = false
  1154  	args := params.Entities{Entities: []params.Entity{
  1155  		{"filesystem-0-0"}, {"filesystem-0-42"}, {"filesystem-42"},
  1156  		{"filesystem-invalid"}, {"machine-0"},
  1157  	}}
  1158  
  1159  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1160  	c.Assert(err, jc.ErrorIsNil)
  1161  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1162  	c.Assert(err, jc.ErrorIsNil)
  1163  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("0/0"))
  1164  	c.Assert(err, jc.ErrorIsNil)
  1165  
  1166  	result, err := s.api.Remove(args)
  1167  	c.Assert(err, jc.ErrorIsNil)
  1168  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1169  		Results: []params.ErrorResult{
  1170  			{Error: nil},
  1171  			{Error: nil},
  1172  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1173  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1174  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1175  		},
  1176  	})
  1177  }
  1178  
  1179  func (s *provisionerSuite) TestRemoveVolumeAttachments(c *gc.C) {
  1180  	s.setupVolumes(c)
  1181  	s.authorizer.EnvironManager = false
  1182  
  1183  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1184  	c.Assert(err, jc.ErrorIsNil)
  1185  
  1186  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1187  		Ids: []params.MachineStorageId{{
  1188  			MachineTag:    "machine-0",
  1189  			AttachmentTag: "volume-0-0",
  1190  		}, {
  1191  			MachineTag:    "machine-0",
  1192  			AttachmentTag: "volume-1",
  1193  		}, {
  1194  			MachineTag:    "machine-2",
  1195  			AttachmentTag: "volume-4",
  1196  		}, {
  1197  			MachineTag:    "machine-0",
  1198  			AttachmentTag: "volume-42",
  1199  		}},
  1200  	})
  1201  	c.Assert(err, jc.ErrorIsNil)
  1202  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1203  		Results: []params.ErrorResult{
  1204  			{Error: &params.Error{Message: "removing attachment of volume 0/0 from machine 0: volume attachment is not dying"}},
  1205  			{Error: nil},
  1206  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1207  			{Error: &params.Error{Message: `removing attachment of volume 42 from machine 0: volume "42" on machine "0" not found`, Code: "not found"}},
  1208  		},
  1209  	})
  1210  }
  1211  
  1212  func (s *provisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) {
  1213  	s.setupFilesystems(c)
  1214  	s.authorizer.EnvironManager = false
  1215  
  1216  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1217  	c.Assert(err, jc.ErrorIsNil)
  1218  
  1219  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1220  		Ids: []params.MachineStorageId{{
  1221  			MachineTag:    "machine-0",
  1222  			AttachmentTag: "filesystem-0-0",
  1223  		}, {
  1224  			MachineTag:    "machine-0",
  1225  			AttachmentTag: "filesystem-1",
  1226  		}, {
  1227  			MachineTag:    "machine-2",
  1228  			AttachmentTag: "filesystem-4",
  1229  		}, {
  1230  			MachineTag:    "machine-0",
  1231  			AttachmentTag: "filesystem-42",
  1232  		}},
  1233  	})
  1234  	c.Assert(err, jc.ErrorIsNil)
  1235  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1236  		Results: []params.ErrorResult{
  1237  			{Error: &params.Error{Message: "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying"}},
  1238  			{Error: nil},
  1239  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1240  			{Error: &params.Error{Message: `removing attachment of filesystem 42 from machine 0: filesystem "42" on machine "0" not found`, Code: "not found"}},
  1241  		},
  1242  	})
  1243  }
  1244  
  1245  type byMachineAndEntity []params.MachineStorageId
  1246  
  1247  func (b byMachineAndEntity) Len() int {
  1248  	return len(b)
  1249  }
  1250  
  1251  func (b byMachineAndEntity) Less(i, j int) bool {
  1252  	if b[i].MachineTag == b[j].MachineTag {
  1253  		return b[i].AttachmentTag < b[j].AttachmentTag
  1254  	}
  1255  	return b[i].MachineTag < b[j].MachineTag
  1256  }
  1257  
  1258  func (b byMachineAndEntity) Swap(i, j int) {
  1259  	b[i], b[j] = b[j], b[i]
  1260  }