github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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.AddSuiteCleanup(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{"permission denied", "unauthorized access"}},
   195  			{Error: &params.Error{"permission denied", "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{"permission denied", "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{"permission denied", "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{"permission denied", "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{"permission denied", "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{"permission denied", "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{"permission denied", "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.JujuEnv: testing.EnvironmentTag.Id(),
   372  				},
   373  				Attachment: &params.VolumeAttachmentParams{
   374  					MachineTag: "machine-0",
   375  					VolumeTag:  "volume-0-0",
   376  					Provider:   "machinescoped",
   377  					InstanceId: "inst-id",
   378  				},
   379  			}},
   380  			{Result: params.VolumeParams{
   381  				VolumeTag: "volume-1",
   382  				Size:      2048,
   383  				Provider:  "environscoped",
   384  				Tags: map[string]string{
   385  					tags.JujuEnv: testing.EnvironmentTag.Id(),
   386  				},
   387  				Attachment: &params.VolumeAttachmentParams{
   388  					MachineTag: "machine-0",
   389  					VolumeTag:  "volume-1",
   390  					Provider:   "environscoped",
   391  					InstanceId: "inst-id",
   392  				},
   393  			}},
   394  			{Result: params.VolumeParams{
   395  				VolumeTag: "volume-3",
   396  				Size:      4096,
   397  				Provider:  "environscoped",
   398  				Tags: map[string]string{
   399  					tags.JujuEnv: testing.EnvironmentTag.Id(),
   400  				},
   401  				Attachment: &params.VolumeAttachmentParams{
   402  					MachineTag: "machine-0",
   403  					VolumeTag:  "volume-3",
   404  					Provider:   "environscoped",
   405  					InstanceId: "inst-id",
   406  					ReadOnly:   true,
   407  				},
   408  			}},
   409  			{Error: &params.Error{"permission denied", "unauthorized access"}},
   410  		},
   411  	})
   412  }
   413  
   414  func (s *provisionerSuite) TestVolumeParamsEmptyArgs(c *gc.C) {
   415  	results, err := s.api.VolumeParams(params.Entities{})
   416  	c.Assert(err, jc.ErrorIsNil)
   417  	c.Assert(results.Results, gc.HasLen, 0)
   418  }
   419  
   420  func (s *provisionerSuite) TestFilesystemParams(c *gc.C) {
   421  	s.setupFilesystems(c)
   422  	results, err := s.api.FilesystemParams(params.Entities{
   423  		Entities: []params.Entity{{"filesystem-0-0"}, {"filesystem-1"}, {"filesystem-42"}},
   424  	})
   425  	c.Assert(err, jc.ErrorIsNil)
   426  	c.Assert(results, jc.DeepEquals, params.FilesystemParamsResults{
   427  		Results: []params.FilesystemParamsResult{
   428  			{Result: params.FilesystemParams{
   429  				FilesystemTag: "filesystem-0-0",
   430  				Size:          1024,
   431  				Provider:      "machinescoped",
   432  				Tags: map[string]string{
   433  					tags.JujuEnv: testing.EnvironmentTag.Id(),
   434  				},
   435  			}},
   436  			{Result: params.FilesystemParams{
   437  				FilesystemTag: "filesystem-1",
   438  				Size:          2048,
   439  				Provider:      "environscoped",
   440  				Tags: map[string]string{
   441  					tags.JujuEnv: testing.EnvironmentTag.Id(),
   442  				},
   443  			}},
   444  			{Error: &params.Error{"permission denied", "unauthorized access"}},
   445  		},
   446  	})
   447  }
   448  
   449  func (s *provisionerSuite) TestVolumeAttachmentParams(c *gc.C) {
   450  	s.setupVolumes(c)
   451  
   452  	err := s.State.SetVolumeInfo(names.NewVolumeTag("3"), state.VolumeInfo{
   453  		HardwareId: "123",
   454  		VolumeId:   "xyz",
   455  		Size:       1024,
   456  	})
   457  	c.Assert(err, jc.ErrorIsNil)
   458  
   459  	err = s.State.SetVolumeAttachmentInfo(
   460  		names.NewMachineTag("0"),
   461  		names.NewVolumeTag("3"),
   462  		state.VolumeAttachmentInfo{
   463  			DeviceName: "xvdf1",
   464  			ReadOnly:   true,
   465  		},
   466  	)
   467  	c.Assert(err, jc.ErrorIsNil)
   468  
   469  	results, err := s.api.VolumeAttachmentParams(params.MachineStorageIds{
   470  		Ids: []params.MachineStorageId{{
   471  			MachineTag:    "machine-0",
   472  			AttachmentTag: "volume-0-0",
   473  		}, {
   474  			MachineTag:    "machine-0",
   475  			AttachmentTag: "volume-1",
   476  		}, {
   477  			MachineTag:    "machine-0",
   478  			AttachmentTag: "volume-3",
   479  		}, {
   480  			MachineTag:    "machine-2",
   481  			AttachmentTag: "volume-4",
   482  		}, {
   483  			MachineTag:    "machine-0",
   484  			AttachmentTag: "volume-42",
   485  		}},
   486  	})
   487  	c.Assert(err, jc.ErrorIsNil)
   488  	c.Assert(results, jc.DeepEquals, params.VolumeAttachmentParamsResults{
   489  		Results: []params.VolumeAttachmentParamsResult{
   490  			{Result: params.VolumeAttachmentParams{
   491  				MachineTag: "machine-0",
   492  				VolumeTag:  "volume-0-0",
   493  				InstanceId: "inst-id",
   494  				VolumeId:   "abc",
   495  				Provider:   "machinescoped",
   496  			}},
   497  			{Result: params.VolumeAttachmentParams{
   498  				MachineTag: "machine-0",
   499  				VolumeTag:  "volume-1",
   500  				InstanceId: "inst-id",
   501  				Provider:   "environscoped",
   502  			}},
   503  			{Result: params.VolumeAttachmentParams{
   504  				MachineTag: "machine-0",
   505  				VolumeTag:  "volume-3",
   506  				InstanceId: "inst-id",
   507  				VolumeId:   "xyz",
   508  				Provider:   "environscoped",
   509  				ReadOnly:   true,
   510  			}},
   511  			{Result: params.VolumeAttachmentParams{
   512  				MachineTag: "machine-2",
   513  				VolumeTag:  "volume-4",
   514  				Provider:   "environscoped",
   515  			}},
   516  			{Error: &params.Error{"permission denied", "unauthorized access"}},
   517  		},
   518  	})
   519  }
   520  
   521  func (s *provisionerSuite) TestFilesystemAttachmentParams(c *gc.C) {
   522  	s.setupFilesystems(c)
   523  
   524  	err := s.State.SetFilesystemInfo(names.NewFilesystemTag("1"), state.FilesystemInfo{
   525  		FilesystemId: "fsid",
   526  		Size:         1024,
   527  	})
   528  	c.Assert(err, jc.ErrorIsNil)
   529  
   530  	err = s.State.SetFilesystemAttachmentInfo(
   531  		names.NewMachineTag("0"),
   532  		names.NewFilesystemTag("1"),
   533  		state.FilesystemAttachmentInfo{
   534  			MountPoint: "/in/the/place",
   535  		},
   536  	)
   537  	c.Assert(err, jc.ErrorIsNil)
   538  
   539  	results, err := s.api.FilesystemAttachmentParams(params.MachineStorageIds{
   540  		Ids: []params.MachineStorageId{{
   541  			MachineTag:    "machine-0",
   542  			AttachmentTag: "filesystem-0-0",
   543  		}, {
   544  			MachineTag:    "machine-0",
   545  			AttachmentTag: "filesystem-1",
   546  		}, {
   547  			MachineTag:    "machine-2",
   548  			AttachmentTag: "filesystem-3",
   549  		}, {
   550  			MachineTag:    "machine-0",
   551  			AttachmentTag: "filesystem-42",
   552  		}},
   553  	})
   554  	c.Assert(err, jc.ErrorIsNil)
   555  	c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentParamsResults{
   556  		Results: []params.FilesystemAttachmentParamsResult{
   557  			{Result: params.FilesystemAttachmentParams{
   558  				MachineTag:    "machine-0",
   559  				FilesystemTag: "filesystem-0-0",
   560  				InstanceId:    "inst-id",
   561  				FilesystemId:  "abc",
   562  				Provider:      "machinescoped",
   563  				MountPoint:    "/srv",
   564  				ReadOnly:      true,
   565  			}},
   566  			{Result: params.FilesystemAttachmentParams{
   567  				MachineTag:    "machine-0",
   568  				FilesystemTag: "filesystem-1",
   569  				InstanceId:    "inst-id",
   570  				FilesystemId:  "fsid",
   571  				Provider:      "environscoped",
   572  				MountPoint:    "/in/the/place",
   573  			}},
   574  			{Result: params.FilesystemAttachmentParams{
   575  				MachineTag:    "machine-2",
   576  				FilesystemTag: "filesystem-3",
   577  				Provider:      "environscoped",
   578  			}},
   579  			{Error: &params.Error{"permission denied", "unauthorized access"}},
   580  		},
   581  	})
   582  }
   583  
   584  func (s *provisionerSuite) TestSetVolumeAttachmentInfo(c *gc.C) {
   585  	s.setupVolumes(c)
   586  
   587  	err := s.State.SetVolumeInfo(names.NewVolumeTag("4"), state.VolumeInfo{
   588  		VolumeId: "whatever",
   589  		Size:     1024,
   590  	})
   591  	c.Assert(err, jc.ErrorIsNil)
   592  
   593  	results, err := s.api.SetVolumeAttachmentInfo(params.VolumeAttachments{
   594  		VolumeAttachments: []params.VolumeAttachment{{
   595  			MachineTag: "machine-0",
   596  			VolumeTag:  "volume-0-0",
   597  			Info: params.VolumeAttachmentInfo{
   598  				DeviceName: "sda",
   599  				ReadOnly:   true,
   600  			},
   601  		}, {
   602  			MachineTag: "machine-0",
   603  			VolumeTag:  "volume-1",
   604  			Info: params.VolumeAttachmentInfo{
   605  				DeviceName: "sdb",
   606  			},
   607  		}, {
   608  			MachineTag: "machine-2",
   609  			VolumeTag:  "volume-4",
   610  			Info: params.VolumeAttachmentInfo{
   611  				DeviceName: "sdc",
   612  			},
   613  		}, {
   614  			MachineTag: "machine-0",
   615  			VolumeTag:  "volume-42",
   616  			Info: params.VolumeAttachmentInfo{
   617  				DeviceName: "sdd",
   618  			},
   619  		}},
   620  	})
   621  	c.Assert(err, jc.ErrorIsNil)
   622  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
   623  		Results: []params.ErrorResult{
   624  			{},
   625  			{Error: &params.Error{`cannot set info for volume attachment 1:0: volume "1" not provisioned`, "not provisioned"}},
   626  			{Error: &params.Error{`cannot set info for volume attachment 4:2: machine 2 not provisioned`, "not provisioned"}},
   627  			{Error: &params.Error{"permission denied", "unauthorized access"}},
   628  		},
   629  	})
   630  }
   631  
   632  func (s *provisionerSuite) TestSetFilesystemAttachmentInfo(c *gc.C) {
   633  	s.setupFilesystems(c)
   634  
   635  	err := s.State.SetFilesystemInfo(names.NewFilesystemTag("3"), state.FilesystemInfo{
   636  		FilesystemId: "whatever",
   637  		Size:         1024,
   638  	})
   639  	c.Assert(err, jc.ErrorIsNil)
   640  
   641  	results, err := s.api.SetFilesystemAttachmentInfo(params.FilesystemAttachments{
   642  		FilesystemAttachments: []params.FilesystemAttachment{{
   643  			MachineTag:    "machine-0",
   644  			FilesystemTag: "filesystem-0-0",
   645  			Info: params.FilesystemAttachmentInfo{
   646  				MountPoint: "/srv/a",
   647  				ReadOnly:   true,
   648  			},
   649  		}, {
   650  			MachineTag:    "machine-0",
   651  			FilesystemTag: "filesystem-1",
   652  			Info: params.FilesystemAttachmentInfo{
   653  				MountPoint: "/srv/b",
   654  			},
   655  		}, {
   656  			MachineTag:    "machine-2",
   657  			FilesystemTag: "filesystem-3",
   658  			Info: params.FilesystemAttachmentInfo{
   659  				MountPoint: "/srv/c",
   660  			},
   661  		}, {
   662  			MachineTag:    "machine-0",
   663  			FilesystemTag: "filesystem-42",
   664  			Info: params.FilesystemAttachmentInfo{
   665  				MountPoint: "/srv/d",
   666  			},
   667  		}},
   668  	})
   669  	c.Assert(err, jc.ErrorIsNil)
   670  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
   671  		Results: []params.ErrorResult{
   672  			{},
   673  			{Error: &params.Error{`cannot set info for filesystem attachment 1:0: filesystem "1" not provisioned`, "not provisioned"}},
   674  			{Error: &params.Error{`cannot set info for filesystem attachment 3:2: machine 2 not provisioned`, "not provisioned"}},
   675  			{Error: &params.Error{"permission denied", "unauthorized access"}},
   676  		},
   677  	})
   678  }
   679  
   680  func (s *provisionerSuite) TestWatchVolumes(c *gc.C) {
   681  	s.setupVolumes(c)
   682  	s.factory.MakeMachine(c, nil)
   683  	c.Assert(s.resources.Count(), gc.Equals, 0)
   684  
   685  	args := params.Entities{Entities: []params.Entity{
   686  		{"machine-0"},
   687  		{s.State.EnvironTag().String()},
   688  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   689  		{"machine-1"},
   690  		{"machine-42"}},
   691  	}
   692  	result, err := s.api.WatchVolumes(args)
   693  	c.Assert(err, jc.ErrorIsNil)
   694  	sort.Strings(result.Results[1].Changes)
   695  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   696  		Results: []params.StringsWatchResult{
   697  			{StringsWatcherId: "1", Changes: []string{"0/0"}},
   698  			{StringsWatcherId: "2", Changes: []string{"1", "2", "3", "4"}},
   699  			{Error: apiservertesting.ErrUnauthorized},
   700  			{Error: apiservertesting.ErrUnauthorized},
   701  			{Error: apiservertesting.ErrUnauthorized},
   702  		},
   703  	})
   704  
   705  	// Verify the resources were registered and stop them when done.
   706  	c.Assert(s.resources.Count(), gc.Equals, 2)
   707  	v0Watcher := s.resources.Get("1")
   708  	defer statetesting.AssertStop(c, v0Watcher)
   709  	v1Watcher := s.resources.Get("2")
   710  	defer statetesting.AssertStop(c, v1Watcher)
   711  
   712  	// Check that the Watch has consumed the initial events ("returned" in
   713  	// the Watch call)
   714  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   715  	wc.AssertNoChange()
   716  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   717  	wc.AssertNoChange()
   718  }
   719  
   720  func (s *provisionerSuite) TestWatchVolumeAttachments(c *gc.C) {
   721  	s.setupVolumes(c)
   722  	s.factory.MakeMachine(c, nil)
   723  	c.Assert(s.resources.Count(), gc.Equals, 0)
   724  
   725  	args := params.Entities{Entities: []params.Entity{
   726  		{"machine-0"},
   727  		{s.State.EnvironTag().String()},
   728  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   729  		{"machine-1"},
   730  		{"machine-42"}},
   731  	}
   732  	result, err := s.api.WatchVolumeAttachments(args)
   733  	c.Assert(err, jc.ErrorIsNil)
   734  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
   735  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
   736  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
   737  		Results: []params.MachineStorageIdsWatchResult{
   738  			{
   739  				MachineStorageIdsWatcherId: "1",
   740  				Changes: []params.MachineStorageId{{
   741  					MachineTag:    "machine-0",
   742  					AttachmentTag: "volume-0-0",
   743  				}, {
   744  					MachineTag:    "machine-0",
   745  					AttachmentTag: "volume-1",
   746  				}, {
   747  					MachineTag:    "machine-0",
   748  					AttachmentTag: "volume-2",
   749  				}, {
   750  					MachineTag:    "machine-0",
   751  					AttachmentTag: "volume-3",
   752  				}},
   753  			},
   754  			{
   755  				MachineStorageIdsWatcherId: "2",
   756  				Changes: []params.MachineStorageId{{
   757  					MachineTag:    "machine-0",
   758  					AttachmentTag: "volume-1",
   759  				}, {
   760  					MachineTag:    "machine-0",
   761  					AttachmentTag: "volume-2",
   762  				}, {
   763  					MachineTag:    "machine-0",
   764  					AttachmentTag: "volume-3",
   765  				}, {
   766  					MachineTag:    "machine-2",
   767  					AttachmentTag: "volume-4",
   768  				}},
   769  			},
   770  			{Error: apiservertesting.ErrUnauthorized},
   771  			{Error: apiservertesting.ErrUnauthorized},
   772  			{Error: apiservertesting.ErrUnauthorized},
   773  		},
   774  	})
   775  
   776  	// Verify the resources were registered and stop them when done.
   777  	c.Assert(s.resources.Count(), gc.Equals, 2)
   778  	v0Watcher := s.resources.Get("1")
   779  	defer statetesting.AssertStop(c, v0Watcher)
   780  	v1Watcher := s.resources.Get("2")
   781  	defer statetesting.AssertStop(c, v1Watcher)
   782  
   783  	// Check that the Watch has consumed the initial events ("returned" in
   784  	// the Watch call)
   785  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   786  	wc.AssertNoChange()
   787  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   788  	wc.AssertNoChange()
   789  }
   790  
   791  func (s *provisionerSuite) TestWatchFilesystems(c *gc.C) {
   792  	s.setupFilesystems(c)
   793  	c.Assert(s.resources.Count(), gc.Equals, 0)
   794  
   795  	args := params.Entities{Entities: []params.Entity{
   796  		{"machine-0"},
   797  		{s.State.EnvironTag().String()},
   798  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   799  		{"machine-1"},
   800  		{"machine-42"}},
   801  	}
   802  	result, err := s.api.WatchFilesystems(args)
   803  	c.Assert(err, jc.ErrorIsNil)
   804  	sort.Strings(result.Results[1].Changes)
   805  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   806  		Results: []params.StringsWatchResult{
   807  			{
   808  				StringsWatcherId: "1",
   809  				Changes:          []string{"0/0"},
   810  			},
   811  			{
   812  				StringsWatcherId: "2",
   813  				Changes:          []string{"1", "2", "3"},
   814  			},
   815  			{Error: apiservertesting.ErrUnauthorized},
   816  			{Error: apiservertesting.ErrUnauthorized},
   817  			{Error: apiservertesting.ErrUnauthorized},
   818  		},
   819  	})
   820  
   821  	// Verify the resources were registered and stop them when done.
   822  	c.Assert(s.resources.Count(), gc.Equals, 2)
   823  	v0Watcher := s.resources.Get("1")
   824  	defer statetesting.AssertStop(c, v0Watcher)
   825  	v1Watcher := s.resources.Get("2")
   826  	defer statetesting.AssertStop(c, v1Watcher)
   827  
   828  	// Check that the Watch has consumed the initial events ("returned" in
   829  	// the Watch call)
   830  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   831  	wc.AssertNoChange()
   832  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   833  	wc.AssertNoChange()
   834  }
   835  
   836  func (s *provisionerSuite) TestWatchFilesystemAttachments(c *gc.C) {
   837  	s.setupFilesystems(c)
   838  	c.Assert(s.resources.Count(), gc.Equals, 0)
   839  
   840  	args := params.Entities{Entities: []params.Entity{
   841  		{"machine-0"},
   842  		{s.State.EnvironTag().String()},
   843  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   844  		{"machine-1"},
   845  		{"machine-42"}},
   846  	}
   847  	result, err := s.api.WatchFilesystemAttachments(args)
   848  	c.Assert(err, jc.ErrorIsNil)
   849  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
   850  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
   851  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
   852  		Results: []params.MachineStorageIdsWatchResult{
   853  			{
   854  				MachineStorageIdsWatcherId: "1",
   855  				Changes: []params.MachineStorageId{{
   856  					MachineTag:    "machine-0",
   857  					AttachmentTag: "filesystem-0-0",
   858  				}, {
   859  					MachineTag:    "machine-0",
   860  					AttachmentTag: "filesystem-1",
   861  				}, {
   862  					MachineTag:    "machine-0",
   863  					AttachmentTag: "filesystem-2",
   864  				}},
   865  			},
   866  			{
   867  				MachineStorageIdsWatcherId: "2",
   868  				Changes: []params.MachineStorageId{{
   869  					MachineTag:    "machine-0",
   870  					AttachmentTag: "filesystem-1",
   871  				}, {
   872  					MachineTag:    "machine-0",
   873  					AttachmentTag: "filesystem-2",
   874  				}, {
   875  					MachineTag:    "machine-2",
   876  					AttachmentTag: "filesystem-3",
   877  				}},
   878  			},
   879  			{Error: apiservertesting.ErrUnauthorized},
   880  			{Error: apiservertesting.ErrUnauthorized},
   881  			{Error: apiservertesting.ErrUnauthorized},
   882  		},
   883  	})
   884  
   885  	// Verify the resources were registered and stop them when done.
   886  	c.Assert(s.resources.Count(), gc.Equals, 2)
   887  	v0Watcher := s.resources.Get("1")
   888  	defer statetesting.AssertStop(c, v0Watcher)
   889  	v1Watcher := s.resources.Get("2")
   890  	defer statetesting.AssertStop(c, v1Watcher)
   891  
   892  	// Check that the Watch has consumed the initial events ("returned" in
   893  	// the Watch call)
   894  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   895  	wc.AssertNoChange()
   896  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   897  	wc.AssertNoChange()
   898  }
   899  
   900  func (s *provisionerSuite) TestWatchBlockDevices(c *gc.C) {
   901  	s.factory.MakeMachine(c, nil)
   902  	c.Assert(s.resources.Count(), gc.Equals, 0)
   903  
   904  	args := params.Entities{Entities: []params.Entity{
   905  		{"machine-0"},
   906  		{"service-mysql"},
   907  		{"machine-1"},
   908  		{"machine-42"}},
   909  	}
   910  	results, err := s.api.WatchBlockDevices(args)
   911  	c.Assert(err, jc.ErrorIsNil)
   912  	c.Assert(results, jc.DeepEquals, params.NotifyWatchResults{
   913  		Results: []params.NotifyWatchResult{
   914  			{NotifyWatcherId: "1"},
   915  			{Error: &params.Error{Message: `"service-mysql" is not a valid machine tag`}},
   916  			{Error: apiservertesting.ErrUnauthorized},
   917  			{Error: apiservertesting.ErrUnauthorized},
   918  		},
   919  	})
   920  
   921  	// Verify the resources were registered and stop them when done.
   922  	c.Assert(s.resources.Count(), gc.Equals, 1)
   923  	watcher := s.resources.Get("1")
   924  	defer statetesting.AssertStop(c, watcher)
   925  
   926  	// Check that the Watch has consumed the initial event.
   927  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
   928  	wc.AssertNoChange()
   929  
   930  	m, err := s.State.Machine("0")
   931  	c.Assert(err, jc.ErrorIsNil)
   932  	err = m.SetMachineBlockDevices(state.BlockDeviceInfo{
   933  		DeviceName: "sda",
   934  		Size:       123,
   935  	})
   936  	c.Assert(err, jc.ErrorIsNil)
   937  	wc.AssertOneChange()
   938  }
   939  
   940  func (s *provisionerSuite) TestVolumeBlockDevices(c *gc.C) {
   941  	s.setupVolumes(c)
   942  	s.factory.MakeMachine(c, nil)
   943  
   944  	err := s.State.SetVolumeAttachmentInfo(
   945  		names.NewMachineTag("0"),
   946  		names.NewVolumeTag("0/0"),
   947  		state.VolumeAttachmentInfo{},
   948  	)
   949  	c.Assert(err, jc.ErrorIsNil)
   950  
   951  	machine0, err := s.State.Machine("0")
   952  	c.Assert(err, jc.ErrorIsNil)
   953  	err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{
   954  		DeviceName: "sda",
   955  		Size:       123,
   956  		HardwareId: "123", // matches volume-0/0
   957  	})
   958  	c.Assert(err, jc.ErrorIsNil)
   959  
   960  	args := params.MachineStorageIds{Ids: []params.MachineStorageId{
   961  		{MachineTag: "machine-0", AttachmentTag: "volume-0-0"},
   962  		{MachineTag: "machine-0", AttachmentTag: "volume-0-1"},
   963  		{MachineTag: "machine-0", AttachmentTag: "volume-0-2"},
   964  		{MachineTag: "machine-1", AttachmentTag: "volume-1"},
   965  		{MachineTag: "machine-42", AttachmentTag: "volume-42"},
   966  		{MachineTag: "service-mysql", AttachmentTag: "volume-1"},
   967  	}}
   968  	results, err := s.api.VolumeBlockDevices(args)
   969  	c.Assert(err, jc.ErrorIsNil)
   970  	c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{
   971  		Results: []params.BlockDeviceResult{
   972  			{Result: storage.BlockDevice{
   973  				DeviceName: "sda",
   974  				Size:       123,
   975  				HardwareId: "123",
   976  			}},
   977  			{Error: apiservertesting.ErrUnauthorized},
   978  			{Error: apiservertesting.ErrUnauthorized},
   979  			{Error: apiservertesting.ErrUnauthorized},
   980  			{Error: apiservertesting.ErrUnauthorized},
   981  			{Error: &params.Error{Message: `"service-mysql" is not a valid machine tag`}},
   982  		},
   983  	})
   984  }
   985  
   986  func (s *provisionerSuite) TestLife(c *gc.C) {
   987  	s.setupVolumes(c)
   988  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
   989  	result, err := s.api.Life(args)
   990  	c.Assert(err, jc.ErrorIsNil)
   991  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   992  		Results: []params.LifeResult{
   993  			{Life: params.Alive},
   994  			{Life: params.Alive},
   995  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
   996  		},
   997  	})
   998  }
   999  
  1000  func (s *provisionerSuite) TestAttachmentLife(c *gc.C) {
  1001  	s.setupVolumes(c)
  1002  
  1003  	// TODO(axw) test filesystem attachment life
  1004  	// TODO(axw) test Dying
  1005  
  1006  	results, err := s.api.AttachmentLife(params.MachineStorageIds{
  1007  		Ids: []params.MachineStorageId{{
  1008  			MachineTag:    "machine-0",
  1009  			AttachmentTag: "volume-0-0",
  1010  		}, {
  1011  			MachineTag:    "machine-0",
  1012  			AttachmentTag: "volume-1",
  1013  		}, {
  1014  			MachineTag:    "machine-2",
  1015  			AttachmentTag: "volume-4",
  1016  		}, {
  1017  			MachineTag:    "machine-0",
  1018  			AttachmentTag: "volume-42",
  1019  		}},
  1020  	})
  1021  	c.Assert(err, jc.ErrorIsNil)
  1022  	c.Assert(results, jc.DeepEquals, params.LifeResults{
  1023  		Results: []params.LifeResult{
  1024  			{Life: params.Alive},
  1025  			{Life: params.Alive},
  1026  			{Life: params.Alive},
  1027  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1028  		},
  1029  	})
  1030  }
  1031  
  1032  func (s *provisionerSuite) TestEnsureDead(c *gc.C) {
  1033  	s.setupVolumes(c)
  1034  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
  1035  	result, err := s.api.EnsureDead(args)
  1036  	c.Assert(err, jc.ErrorIsNil)
  1037  	// TODO(wallyworld) - this test will be updated when EnsureDead is supported
  1038  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1039  		Results: []params.ErrorResult{
  1040  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("0/0"), "ensuring death"))},
  1041  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("1"), "ensuring death"))},
  1042  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
  1043  		},
  1044  	})
  1045  }
  1046  
  1047  func (s *provisionerSuite) TestWatchForEnvironConfigChanges(c *gc.C) {
  1048  	result, err := s.api.WatchForEnvironConfigChanges()
  1049  	c.Assert(err, jc.ErrorIsNil)
  1050  	c.Assert(result.NotifyWatcherId, gc.Equals, "1")
  1051  
  1052  	// Verify the resource was registered and stop it when done.
  1053  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1054  	watcher := s.resources.Get("1")
  1055  	defer statetesting.AssertStop(c, watcher)
  1056  
  1057  	// Check that the Watch has consumed the initial events ("returned" in
  1058  	// the Watch call)
  1059  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
  1060  	wc.AssertNoChange()
  1061  
  1062  	// Updating config should trigger the watcher.
  1063  	err = s.State.UpdateEnvironConfig(map[string]interface{}{"what": "ever"}, nil, nil)
  1064  	c.Assert(err, jc.ErrorIsNil)
  1065  	wc.AssertOneChange()
  1066  }
  1067  
  1068  func (s *provisionerSuite) TestEnvironConfig(c *gc.C) {
  1069  	stateEnvironConfig, err := s.State.EnvironConfig()
  1070  	c.Assert(err, jc.ErrorIsNil)
  1071  
  1072  	result, err := s.api.EnvironConfig()
  1073  	c.Assert(err, jc.ErrorIsNil)
  1074  	c.Assert(result.Config, jc.DeepEquals, params.EnvironConfig(stateEnvironConfig.AllAttrs()))
  1075  }
  1076  
  1077  func (s *provisionerSuite) TestRemoveVolumesEnvironManager(c *gc.C) {
  1078  	s.setupVolumes(c)
  1079  	args := params.Entities{Entities: []params.Entity{
  1080  		{"volume-1-0"}, {"volume-1"}, {"volume-2"}, {"volume-42"},
  1081  		{"volume-invalid"}, {"machine-0"},
  1082  	}}
  1083  
  1084  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1085  	c.Assert(err, jc.ErrorIsNil)
  1086  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1087  	c.Assert(err, jc.ErrorIsNil)
  1088  	err = s.State.DestroyVolume(names.NewVolumeTag("1"))
  1089  	c.Assert(err, jc.ErrorIsNil)
  1090  
  1091  	result, err := s.api.Remove(args)
  1092  	c.Assert(err, jc.ErrorIsNil)
  1093  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1094  		Results: []params.ErrorResult{
  1095  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1096  			{Error: nil},
  1097  			{Error: &params.Error{Message: "removing volume 2: volume is not dead"}},
  1098  			{Error: nil},
  1099  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1100  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1101  		},
  1102  	})
  1103  }
  1104  
  1105  func (s *provisionerSuite) TestRemoveFilesystemsEnvironManager(c *gc.C) {
  1106  	s.setupFilesystems(c)
  1107  	args := params.Entities{Entities: []params.Entity{
  1108  		{"filesystem-1-0"}, {"filesystem-1"}, {"filesystem-2"}, {"filesystem-42"},
  1109  		{"filesystem-invalid"}, {"machine-0"},
  1110  	}}
  1111  
  1112  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1113  	c.Assert(err, jc.ErrorIsNil)
  1114  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1115  	c.Assert(err, jc.ErrorIsNil)
  1116  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("1"))
  1117  	c.Assert(err, jc.ErrorIsNil)
  1118  
  1119  	result, err := s.api.Remove(args)
  1120  	c.Assert(err, jc.ErrorIsNil)
  1121  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1122  		Results: []params.ErrorResult{
  1123  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1124  			{Error: nil},
  1125  			{Error: &params.Error{Message: "removing filesystem 2: filesystem is not dead"}},
  1126  			{Error: nil},
  1127  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1128  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1129  		},
  1130  	})
  1131  }
  1132  
  1133  func (s *provisionerSuite) TestRemoveVolumesMachineAgent(c *gc.C) {
  1134  	s.setupVolumes(c)
  1135  	s.authorizer.EnvironManager = false
  1136  	args := params.Entities{Entities: []params.Entity{
  1137  		{"volume-0-0"}, {"volume-0-42"}, {"volume-42"},
  1138  		{"volume-invalid"}, {"machine-0"},
  1139  	}}
  1140  
  1141  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1142  	c.Assert(err, jc.ErrorIsNil)
  1143  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1144  	c.Assert(err, jc.ErrorIsNil)
  1145  	err = s.State.DestroyVolume(names.NewVolumeTag("0/0"))
  1146  	c.Assert(err, jc.ErrorIsNil)
  1147  
  1148  	result, err := s.api.Remove(args)
  1149  	c.Assert(err, jc.ErrorIsNil)
  1150  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1151  		Results: []params.ErrorResult{
  1152  			{Error: nil},
  1153  			{Error: nil},
  1154  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1155  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1156  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1157  		},
  1158  	})
  1159  }
  1160  
  1161  func (s *provisionerSuite) TestRemoveFilesystemsMachineAgent(c *gc.C) {
  1162  	s.setupFilesystems(c)
  1163  	s.authorizer.EnvironManager = false
  1164  	args := params.Entities{Entities: []params.Entity{
  1165  		{"filesystem-0-0"}, {"filesystem-0-42"}, {"filesystem-42"},
  1166  		{"filesystem-invalid"}, {"machine-0"},
  1167  	}}
  1168  
  1169  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1170  	c.Assert(err, jc.ErrorIsNil)
  1171  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1172  	c.Assert(err, jc.ErrorIsNil)
  1173  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("0/0"))
  1174  	c.Assert(err, jc.ErrorIsNil)
  1175  
  1176  	result, err := s.api.Remove(args)
  1177  	c.Assert(err, jc.ErrorIsNil)
  1178  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1179  		Results: []params.ErrorResult{
  1180  			{Error: nil},
  1181  			{Error: nil},
  1182  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1183  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1184  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1185  		},
  1186  	})
  1187  }
  1188  
  1189  func (s *provisionerSuite) TestRemoveVolumeAttachments(c *gc.C) {
  1190  	s.setupVolumes(c)
  1191  	s.authorizer.EnvironManager = false
  1192  
  1193  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1194  	c.Assert(err, jc.ErrorIsNil)
  1195  
  1196  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1197  		Ids: []params.MachineStorageId{{
  1198  			MachineTag:    "machine-0",
  1199  			AttachmentTag: "volume-0-0",
  1200  		}, {
  1201  			MachineTag:    "machine-0",
  1202  			AttachmentTag: "volume-1",
  1203  		}, {
  1204  			MachineTag:    "machine-2",
  1205  			AttachmentTag: "volume-4",
  1206  		}, {
  1207  			MachineTag:    "machine-0",
  1208  			AttachmentTag: "volume-42",
  1209  		}},
  1210  	})
  1211  	c.Assert(err, jc.ErrorIsNil)
  1212  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1213  		Results: []params.ErrorResult{
  1214  			{Error: &params.Error{Message: "removing attachment of volume 0/0 from machine 0: volume attachment is not dying"}},
  1215  			{Error: nil},
  1216  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1217  			{Error: &params.Error{`removing attachment of volume 42 from machine 0: volume "42" on machine "0" not found`, "not found"}},
  1218  		},
  1219  	})
  1220  }
  1221  
  1222  func (s *provisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) {
  1223  	s.setupFilesystems(c)
  1224  	s.authorizer.EnvironManager = false
  1225  
  1226  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1227  	c.Assert(err, jc.ErrorIsNil)
  1228  
  1229  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1230  		Ids: []params.MachineStorageId{{
  1231  			MachineTag:    "machine-0",
  1232  			AttachmentTag: "filesystem-0-0",
  1233  		}, {
  1234  			MachineTag:    "machine-0",
  1235  			AttachmentTag: "filesystem-1",
  1236  		}, {
  1237  			MachineTag:    "machine-2",
  1238  			AttachmentTag: "filesystem-4",
  1239  		}, {
  1240  			MachineTag:    "machine-0",
  1241  			AttachmentTag: "filesystem-42",
  1242  		}},
  1243  	})
  1244  	c.Assert(err, jc.ErrorIsNil)
  1245  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1246  		Results: []params.ErrorResult{
  1247  			{Error: &params.Error{Message: "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying"}},
  1248  			{Error: nil},
  1249  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1250  			{Error: &params.Error{`removing attachment of filesystem 42 from machine 0: filesystem "42" on machine "0" not found`, "not found"}},
  1251  		},
  1252  	})
  1253  }
  1254  
  1255  type byMachineAndEntity []params.MachineStorageId
  1256  
  1257  func (b byMachineAndEntity) Len() int {
  1258  	return len(b)
  1259  }
  1260  
  1261  func (b byMachineAndEntity) Less(i, j int) bool {
  1262  	if b[i].MachineTag == b[j].MachineTag {
  1263  		return b[i].AttachmentTag < b[j].AttachmentTag
  1264  	}
  1265  	return b[i].MachineTag < b[j].MachineTag
  1266  }
  1267  
  1268  func (b byMachineAndEntity) Swap(i, j int) {
  1269  	b[i], b[j] = b[j], b[i]
  1270  }