github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  			},
   745  			{
   746  				MachineStorageIdsWatcherId: "2",
   747  				Changes: []params.MachineStorageId{{
   748  					MachineTag:    "machine-0",
   749  					AttachmentTag: "volume-1",
   750  				}, {
   751  					MachineTag:    "machine-0",
   752  					AttachmentTag: "volume-2",
   753  				}, {
   754  					MachineTag:    "machine-0",
   755  					AttachmentTag: "volume-3",
   756  				}, {
   757  					MachineTag:    "machine-2",
   758  					AttachmentTag: "volume-4",
   759  				}},
   760  			},
   761  			{Error: apiservertesting.ErrUnauthorized},
   762  			{Error: apiservertesting.ErrUnauthorized},
   763  			{Error: apiservertesting.ErrUnauthorized},
   764  		},
   765  	})
   766  
   767  	// Verify the resources were registered and stop them when done.
   768  	c.Assert(s.resources.Count(), gc.Equals, 2)
   769  	v0Watcher := s.resources.Get("1")
   770  	defer statetesting.AssertStop(c, v0Watcher)
   771  	v1Watcher := s.resources.Get("2")
   772  	defer statetesting.AssertStop(c, v1Watcher)
   773  
   774  	// Check that the Watch has consumed the initial events ("returned" in
   775  	// the Watch call)
   776  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   777  	wc.AssertNoChange()
   778  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   779  	wc.AssertNoChange()
   780  }
   781  
   782  func (s *provisionerSuite) TestWatchFilesystems(c *gc.C) {
   783  	s.setupFilesystems(c)
   784  	c.Assert(s.resources.Count(), gc.Equals, 0)
   785  
   786  	args := params.Entities{Entities: []params.Entity{
   787  		{"machine-0"},
   788  		{s.State.EnvironTag().String()},
   789  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   790  		{"machine-1"},
   791  		{"machine-42"}},
   792  	}
   793  	result, err := s.api.WatchFilesystems(args)
   794  	c.Assert(err, jc.ErrorIsNil)
   795  	sort.Strings(result.Results[1].Changes)
   796  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   797  		Results: []params.StringsWatchResult{
   798  			{
   799  				StringsWatcherId: "1",
   800  				Changes:          []string{"0/0"},
   801  			},
   802  			{
   803  				StringsWatcherId: "2",
   804  				Changes:          []string{"1", "2", "3"},
   805  			},
   806  			{Error: apiservertesting.ErrUnauthorized},
   807  			{Error: apiservertesting.ErrUnauthorized},
   808  			{Error: apiservertesting.ErrUnauthorized},
   809  		},
   810  	})
   811  
   812  	// Verify the resources were registered and stop them when done.
   813  	c.Assert(s.resources.Count(), gc.Equals, 2)
   814  	v0Watcher := s.resources.Get("1")
   815  	defer statetesting.AssertStop(c, v0Watcher)
   816  	v1Watcher := s.resources.Get("2")
   817  	defer statetesting.AssertStop(c, v1Watcher)
   818  
   819  	// Check that the Watch has consumed the initial events ("returned" in
   820  	// the Watch call)
   821  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   822  	wc.AssertNoChange()
   823  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   824  	wc.AssertNoChange()
   825  }
   826  
   827  func (s *provisionerSuite) TestWatchFilesystemAttachments(c *gc.C) {
   828  	s.setupFilesystems(c)
   829  	c.Assert(s.resources.Count(), gc.Equals, 0)
   830  
   831  	args := params.Entities{Entities: []params.Entity{
   832  		{"machine-0"},
   833  		{s.State.EnvironTag().String()},
   834  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
   835  		{"machine-1"},
   836  		{"machine-42"}},
   837  	}
   838  	result, err := s.api.WatchFilesystemAttachments(args)
   839  	c.Assert(err, jc.ErrorIsNil)
   840  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
   841  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
   842  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
   843  		Results: []params.MachineStorageIdsWatchResult{
   844  			{
   845  				MachineStorageIdsWatcherId: "1",
   846  				Changes: []params.MachineStorageId{{
   847  					MachineTag:    "machine-0",
   848  					AttachmentTag: "filesystem-0-0",
   849  				}},
   850  			},
   851  			{
   852  				MachineStorageIdsWatcherId: "2",
   853  				Changes: []params.MachineStorageId{{
   854  					MachineTag:    "machine-0",
   855  					AttachmentTag: "filesystem-1",
   856  				}, {
   857  					MachineTag:    "machine-0",
   858  					AttachmentTag: "filesystem-2",
   859  				}, {
   860  					MachineTag:    "machine-2",
   861  					AttachmentTag: "filesystem-3",
   862  				}},
   863  			},
   864  			{Error: apiservertesting.ErrUnauthorized},
   865  			{Error: apiservertesting.ErrUnauthorized},
   866  			{Error: apiservertesting.ErrUnauthorized},
   867  		},
   868  	})
   869  
   870  	// Verify the resources were registered and stop them when done.
   871  	c.Assert(s.resources.Count(), gc.Equals, 2)
   872  	v0Watcher := s.resources.Get("1")
   873  	defer statetesting.AssertStop(c, v0Watcher)
   874  	v1Watcher := s.resources.Get("2")
   875  	defer statetesting.AssertStop(c, v1Watcher)
   876  
   877  	// Check that the Watch has consumed the initial events ("returned" in
   878  	// the Watch call)
   879  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
   880  	wc.AssertNoChange()
   881  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
   882  	wc.AssertNoChange()
   883  }
   884  
   885  func (s *provisionerSuite) TestWatchBlockDevices(c *gc.C) {
   886  	s.factory.MakeMachine(c, nil)
   887  	c.Assert(s.resources.Count(), gc.Equals, 0)
   888  
   889  	args := params.Entities{Entities: []params.Entity{
   890  		{"machine-0"},
   891  		{"service-mysql"},
   892  		{"machine-1"},
   893  		{"machine-42"}},
   894  	}
   895  	results, err := s.api.WatchBlockDevices(args)
   896  	c.Assert(err, jc.ErrorIsNil)
   897  	c.Assert(results, jc.DeepEquals, params.NotifyWatchResults{
   898  		Results: []params.NotifyWatchResult{
   899  			{NotifyWatcherId: "1"},
   900  			{Error: &params.Error{Message: `"service-mysql" is not a valid machine tag`}},
   901  			{Error: apiservertesting.ErrUnauthorized},
   902  			{Error: apiservertesting.ErrUnauthorized},
   903  		},
   904  	})
   905  
   906  	// Verify the resources were registered and stop them when done.
   907  	c.Assert(s.resources.Count(), gc.Equals, 1)
   908  	watcher := s.resources.Get("1")
   909  	defer statetesting.AssertStop(c, watcher)
   910  
   911  	// Check that the Watch has consumed the initial event.
   912  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
   913  	wc.AssertNoChange()
   914  
   915  	m, err := s.State.Machine("0")
   916  	c.Assert(err, jc.ErrorIsNil)
   917  	err = m.SetMachineBlockDevices(state.BlockDeviceInfo{
   918  		DeviceName: "sda",
   919  		Size:       123,
   920  	})
   921  	c.Assert(err, jc.ErrorIsNil)
   922  	wc.AssertOneChange()
   923  }
   924  
   925  func (s *provisionerSuite) TestVolumeBlockDevices(c *gc.C) {
   926  	s.setupVolumes(c)
   927  	s.factory.MakeMachine(c, nil)
   928  
   929  	err := s.State.SetVolumeAttachmentInfo(
   930  		names.NewMachineTag("0"),
   931  		names.NewVolumeTag("0/0"),
   932  		state.VolumeAttachmentInfo{},
   933  	)
   934  	c.Assert(err, jc.ErrorIsNil)
   935  
   936  	machine0, err := s.State.Machine("0")
   937  	c.Assert(err, jc.ErrorIsNil)
   938  	err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{
   939  		DeviceName: "sda",
   940  		Size:       123,
   941  		HardwareId: "123", // matches volume-0/0
   942  	})
   943  	c.Assert(err, jc.ErrorIsNil)
   944  
   945  	args := params.MachineStorageIds{Ids: []params.MachineStorageId{
   946  		{MachineTag: "machine-0", AttachmentTag: "volume-0-0"},
   947  		{MachineTag: "machine-0", AttachmentTag: "volume-0-1"},
   948  		{MachineTag: "machine-0", AttachmentTag: "volume-0-2"},
   949  		{MachineTag: "machine-1", AttachmentTag: "volume-1"},
   950  		{MachineTag: "machine-42", AttachmentTag: "volume-42"},
   951  		{MachineTag: "service-mysql", AttachmentTag: "volume-1"},
   952  	}}
   953  	results, err := s.api.VolumeBlockDevices(args)
   954  	c.Assert(err, jc.ErrorIsNil)
   955  	c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{
   956  		Results: []params.BlockDeviceResult{
   957  			{Result: storage.BlockDevice{
   958  				DeviceName: "sda",
   959  				Size:       123,
   960  				HardwareId: "123",
   961  			}},
   962  			{Error: apiservertesting.ErrUnauthorized},
   963  			{Error: apiservertesting.ErrUnauthorized},
   964  			{Error: apiservertesting.ErrUnauthorized},
   965  			{Error: apiservertesting.ErrUnauthorized},
   966  			{Error: &params.Error{Message: `"service-mysql" is not a valid machine tag`}},
   967  		},
   968  	})
   969  }
   970  
   971  func (s *provisionerSuite) TestLife(c *gc.C) {
   972  	s.setupVolumes(c)
   973  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
   974  	result, err := s.api.Life(args)
   975  	c.Assert(err, jc.ErrorIsNil)
   976  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   977  		Results: []params.LifeResult{
   978  			{Life: params.Alive},
   979  			{Life: params.Alive},
   980  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
   981  		},
   982  	})
   983  }
   984  
   985  func (s *provisionerSuite) TestAttachmentLife(c *gc.C) {
   986  	s.setupVolumes(c)
   987  
   988  	// TODO(axw) test filesystem attachment life
   989  	// TODO(axw) test Dying
   990  
   991  	results, err := s.api.AttachmentLife(params.MachineStorageIds{
   992  		Ids: []params.MachineStorageId{{
   993  			MachineTag:    "machine-0",
   994  			AttachmentTag: "volume-0-0",
   995  		}, {
   996  			MachineTag:    "machine-0",
   997  			AttachmentTag: "volume-1",
   998  		}, {
   999  			MachineTag:    "machine-2",
  1000  			AttachmentTag: "volume-4",
  1001  		}, {
  1002  			MachineTag:    "machine-0",
  1003  			AttachmentTag: "volume-42",
  1004  		}},
  1005  	})
  1006  	c.Assert(err, jc.ErrorIsNil)
  1007  	c.Assert(results, jc.DeepEquals, params.LifeResults{
  1008  		Results: []params.LifeResult{
  1009  			{Life: params.Alive},
  1010  			{Life: params.Alive},
  1011  			{Life: params.Alive},
  1012  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1013  		},
  1014  	})
  1015  }
  1016  
  1017  func (s *provisionerSuite) TestEnsureDead(c *gc.C) {
  1018  	s.setupVolumes(c)
  1019  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
  1020  	result, err := s.api.EnsureDead(args)
  1021  	c.Assert(err, jc.ErrorIsNil)
  1022  	// TODO(wallyworld) - this test will be updated when EnsureDead is supported
  1023  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1024  		Results: []params.ErrorResult{
  1025  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("0/0"), "ensuring death"))},
  1026  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("1"), "ensuring death"))},
  1027  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
  1028  		},
  1029  	})
  1030  }
  1031  
  1032  func (s *provisionerSuite) TestWatchForEnvironConfigChanges(c *gc.C) {
  1033  	result, err := s.api.WatchForEnvironConfigChanges()
  1034  	c.Assert(err, jc.ErrorIsNil)
  1035  	c.Assert(result.NotifyWatcherId, gc.Equals, "1")
  1036  
  1037  	// Verify the resource was registered and stop it when done.
  1038  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1039  	watcher := s.resources.Get("1")
  1040  	defer statetesting.AssertStop(c, watcher)
  1041  
  1042  	// Check that the Watch has consumed the initial events ("returned" in
  1043  	// the Watch call)
  1044  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
  1045  	wc.AssertNoChange()
  1046  
  1047  	// Updating config should trigger the watcher.
  1048  	err = s.State.UpdateEnvironConfig(map[string]interface{}{"what": "ever"}, nil, nil)
  1049  	c.Assert(err, jc.ErrorIsNil)
  1050  	wc.AssertOneChange()
  1051  }
  1052  
  1053  func (s *provisionerSuite) TestEnvironConfig(c *gc.C) {
  1054  	stateEnvironConfig, err := s.State.EnvironConfig()
  1055  	c.Assert(err, jc.ErrorIsNil)
  1056  
  1057  	result, err := s.api.EnvironConfig()
  1058  	c.Assert(err, jc.ErrorIsNil)
  1059  	c.Assert(result.Config, jc.DeepEquals, params.EnvironConfig(stateEnvironConfig.AllAttrs()))
  1060  }
  1061  
  1062  func (s *provisionerSuite) TestRemoveVolumesEnvironManager(c *gc.C) {
  1063  	s.setupVolumes(c)
  1064  	args := params.Entities{Entities: []params.Entity{
  1065  		{"volume-1-0"}, {"volume-1"}, {"volume-2"}, {"volume-42"},
  1066  		{"volume-invalid"}, {"machine-0"},
  1067  	}}
  1068  
  1069  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1070  	c.Assert(err, jc.ErrorIsNil)
  1071  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1072  	c.Assert(err, jc.ErrorIsNil)
  1073  	err = s.State.DestroyVolume(names.NewVolumeTag("1"))
  1074  	c.Assert(err, jc.ErrorIsNil)
  1075  
  1076  	result, err := s.api.Remove(args)
  1077  	c.Assert(err, jc.ErrorIsNil)
  1078  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1079  		Results: []params.ErrorResult{
  1080  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1081  			{Error: nil},
  1082  			{Error: &params.Error{Message: "removing volume 2: volume is not dead"}},
  1083  			{Error: nil},
  1084  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1085  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1086  		},
  1087  	})
  1088  }
  1089  
  1090  func (s *provisionerSuite) TestRemoveFilesystemsEnvironManager(c *gc.C) {
  1091  	s.setupFilesystems(c)
  1092  	args := params.Entities{Entities: []params.Entity{
  1093  		{"filesystem-1-0"}, {"filesystem-1"}, {"filesystem-2"}, {"filesystem-42"},
  1094  		{"filesystem-invalid"}, {"machine-0"},
  1095  	}}
  1096  
  1097  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1098  	c.Assert(err, jc.ErrorIsNil)
  1099  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1100  	c.Assert(err, jc.ErrorIsNil)
  1101  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("1"))
  1102  	c.Assert(err, jc.ErrorIsNil)
  1103  
  1104  	result, err := s.api.Remove(args)
  1105  	c.Assert(err, jc.ErrorIsNil)
  1106  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1107  		Results: []params.ErrorResult{
  1108  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1109  			{Error: nil},
  1110  			{Error: &params.Error{Message: "removing filesystem 2: filesystem is not dead"}},
  1111  			{Error: nil},
  1112  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1113  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1114  		},
  1115  	})
  1116  }
  1117  
  1118  func (s *provisionerSuite) TestRemoveVolumesMachineAgent(c *gc.C) {
  1119  	s.setupVolumes(c)
  1120  	s.authorizer.EnvironManager = false
  1121  	args := params.Entities{Entities: []params.Entity{
  1122  		{"volume-0-0"}, {"volume-0-42"}, {"volume-42"},
  1123  		{"volume-invalid"}, {"machine-0"},
  1124  	}}
  1125  
  1126  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1127  	c.Assert(err, jc.ErrorIsNil)
  1128  	err = s.State.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1129  	c.Assert(err, jc.ErrorIsNil)
  1130  	err = s.State.DestroyVolume(names.NewVolumeTag("0/0"))
  1131  	c.Assert(err, jc.ErrorIsNil)
  1132  
  1133  	result, err := s.api.Remove(args)
  1134  	c.Assert(err, jc.ErrorIsNil)
  1135  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1136  		Results: []params.ErrorResult{
  1137  			{Error: nil},
  1138  			{Error: nil},
  1139  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1140  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1141  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1142  		},
  1143  	})
  1144  }
  1145  
  1146  func (s *provisionerSuite) TestRemoveFilesystemsMachineAgent(c *gc.C) {
  1147  	s.setupFilesystems(c)
  1148  	s.authorizer.EnvironManager = false
  1149  	args := params.Entities{Entities: []params.Entity{
  1150  		{"filesystem-0-0"}, {"filesystem-0-42"}, {"filesystem-42"},
  1151  		{"filesystem-invalid"}, {"machine-0"},
  1152  	}}
  1153  
  1154  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1155  	c.Assert(err, jc.ErrorIsNil)
  1156  	err = s.State.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1157  	c.Assert(err, jc.ErrorIsNil)
  1158  	err = s.State.DestroyFilesystem(names.NewFilesystemTag("0/0"))
  1159  	c.Assert(err, jc.ErrorIsNil)
  1160  
  1161  	result, err := s.api.Remove(args)
  1162  	c.Assert(err, jc.ErrorIsNil)
  1163  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1164  		Results: []params.ErrorResult{
  1165  			{Error: nil},
  1166  			{Error: nil},
  1167  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1168  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1169  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1170  		},
  1171  	})
  1172  }
  1173  
  1174  func (s *provisionerSuite) TestRemoveVolumeAttachments(c *gc.C) {
  1175  	s.setupVolumes(c)
  1176  	s.authorizer.EnvironManager = false
  1177  
  1178  	err := s.State.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1179  	c.Assert(err, jc.ErrorIsNil)
  1180  
  1181  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1182  		Ids: []params.MachineStorageId{{
  1183  			MachineTag:    "machine-0",
  1184  			AttachmentTag: "volume-0-0",
  1185  		}, {
  1186  			MachineTag:    "machine-0",
  1187  			AttachmentTag: "volume-1",
  1188  		}, {
  1189  			MachineTag:    "machine-2",
  1190  			AttachmentTag: "volume-4",
  1191  		}, {
  1192  			MachineTag:    "machine-0",
  1193  			AttachmentTag: "volume-42",
  1194  		}},
  1195  	})
  1196  	c.Assert(err, jc.ErrorIsNil)
  1197  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1198  		Results: []params.ErrorResult{
  1199  			{Error: &params.Error{Message: "removing attachment of volume 0/0 from machine 0: volume attachment is not dying"}},
  1200  			{Error: nil},
  1201  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1202  			{Error: &params.Error{`removing attachment of volume 42 from machine 0: volume "42" on machine "0" not found`, "not found"}},
  1203  		},
  1204  	})
  1205  }
  1206  
  1207  func (s *provisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) {
  1208  	s.setupFilesystems(c)
  1209  	s.authorizer.EnvironManager = false
  1210  
  1211  	err := s.State.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1212  	c.Assert(err, jc.ErrorIsNil)
  1213  
  1214  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1215  		Ids: []params.MachineStorageId{{
  1216  			MachineTag:    "machine-0",
  1217  			AttachmentTag: "filesystem-0-0",
  1218  		}, {
  1219  			MachineTag:    "machine-0",
  1220  			AttachmentTag: "filesystem-1",
  1221  		}, {
  1222  			MachineTag:    "machine-2",
  1223  			AttachmentTag: "filesystem-4",
  1224  		}, {
  1225  			MachineTag:    "machine-0",
  1226  			AttachmentTag: "filesystem-42",
  1227  		}},
  1228  	})
  1229  	c.Assert(err, jc.ErrorIsNil)
  1230  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1231  		Results: []params.ErrorResult{
  1232  			{Error: &params.Error{Message: "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying"}},
  1233  			{Error: nil},
  1234  			{Error: &params.Error{"permission denied", "unauthorized access"}},
  1235  			{Error: &params.Error{`removing attachment of filesystem 42 from machine 0: filesystem "42" on machine "0" not found`, "not found"}},
  1236  		},
  1237  	})
  1238  }
  1239  
  1240  type byMachineAndEntity []params.MachineStorageId
  1241  
  1242  func (b byMachineAndEntity) Len() int {
  1243  	return len(b)
  1244  }
  1245  
  1246  func (b byMachineAndEntity) Less(i, j int) bool {
  1247  	if b[i].MachineTag == b[j].MachineTag {
  1248  		return b[i].AttachmentTag < b[j].AttachmentTag
  1249  	}
  1250  	return b[i].MachineTag < b[j].MachineTag
  1251  }
  1252  
  1253  func (b byMachineAndEntity) Swap(i, j int) {
  1254  	b[i], b[j] = b[j], b[i]
  1255  }