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