github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/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/facades/agent/storageprovisioner"
    16  	"github.com/juju/juju/apiserver/params"
    17  	apiservertesting "github.com/juju/juju/apiserver/testing"
    18  	"github.com/juju/juju/caas"
    19  	"github.com/juju/juju/core/instance"
    20  	"github.com/juju/juju/environs"
    21  	"github.com/juju/juju/environs/tags"
    22  	jujutesting "github.com/juju/juju/juju/testing"
    23  	"github.com/juju/juju/state"
    24  	"github.com/juju/juju/state/stateenvirons"
    25  	statetesting "github.com/juju/juju/state/testing"
    26  	"github.com/juju/juju/storage"
    27  	"github.com/juju/juju/storage/poolmanager"
    28  	"github.com/juju/juju/testing"
    29  	"github.com/juju/juju/testing/factory"
    30  )
    31  
    32  var _ = gc.Suite(&iaasProvisionerSuite{})
    33  var _ = gc.Suite(&caasProvisionerSuite{})
    34  
    35  type iaasProvisionerSuite struct {
    36  	provisionerSuite
    37  }
    38  
    39  type caasProvisionerSuite struct {
    40  	provisionerSuite
    41  }
    42  
    43  type storageSetUp interface {
    44  	setupVolumes(c *gc.C)
    45  	setupFilesystems(c *gc.C)
    46  }
    47  
    48  type provisionerSuite struct {
    49  	// TODO(wallyworld) remove JujuConnSuite
    50  	jujutesting.JujuConnSuite
    51  
    52  	storageSetUp
    53  
    54  	resources      *common.Resources
    55  	authorizer     *apiservertesting.FakeAuthorizer
    56  	api            *storageprovisioner.StorageProvisionerAPIv4
    57  	storageBackend storageprovisioner.StorageBackend
    58  }
    59  
    60  func (s *provisionerSuite) SetUpTest(c *gc.C) {
    61  	s.JujuConnSuite.SetUpTest(c)
    62  }
    63  
    64  func (s *iaasProvisionerSuite) SetUpTest(c *gc.C) {
    65  	s.provisionerSuite.SetUpTest(c)
    66  	s.provisionerSuite.storageSetUp = s
    67  
    68  	// Create the resource registry separately to track invocations to
    69  	// Register.
    70  	s.resources = common.NewResources()
    71  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    72  
    73  	env, err := stateenvirons.GetNewEnvironFunc(environs.New)(s.State)
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	registry := stateenvirons.NewStorageProviderRegistry(env)
    76  	pm := poolmanager.New(state.NewStateSettings(s.State), registry)
    77  
    78  	s.authorizer = &apiservertesting.FakeAuthorizer{
    79  		Tag:        names.NewMachineTag("0"),
    80  		Controller: true,
    81  	}
    82  	backend, storageBackend, err := storageprovisioner.NewStateBackends(s.State)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	s.storageBackend = storageBackend
    85  	v3, err := storageprovisioner.NewStorageProvisionerAPIv3(backend, storageBackend, s.resources, s.authorizer, registry, pm)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	s.api = storageprovisioner.NewStorageProvisionerAPIv4(v3)
    88  }
    89  
    90  func (s *caasProvisionerSuite) SetUpTest(c *gc.C) {
    91  	s.provisionerSuite.SetUpTest(c)
    92  	s.provisionerSuite.storageSetUp = s
    93  
    94  	caasSt := s.Factory.MakeCAASModel(c, nil)
    95  	s.AddCleanup(func(_ *gc.C) { caasSt.Close() })
    96  	s.State = caasSt
    97  	var err error
    98  	s.Model, err = caasSt.Model()
    99  	c.Assert(err, jc.ErrorIsNil)
   100  
   101  	s.Factory = factory.NewFactory(s.State, s.StatePool)
   102  	s.resources = common.NewResources()
   103  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
   104  
   105  	broker, err := stateenvirons.GetNewCAASBrokerFunc(caas.New)(s.State)
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	registry := stateenvirons.NewStorageProviderRegistry(broker)
   108  	pm := poolmanager.New(state.NewStateSettings(s.State), registry)
   109  
   110  	s.authorizer = &apiservertesting.FakeAuthorizer{
   111  		Tag:        names.NewMachineTag("0"),
   112  		Controller: true,
   113  	}
   114  	backend, storageBackend, err := storageprovisioner.NewStateBackends(s.State)
   115  	c.Assert(err, jc.ErrorIsNil)
   116  	s.storageBackend = storageBackend
   117  	v3, err := storageprovisioner.NewStorageProvisionerAPIv3(backend, storageBackend, s.resources, s.authorizer, registry, pm)
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	s.api = storageprovisioner.NewStorageProvisionerAPIv4(v3)
   120  }
   121  
   122  func (s *provisionerSuite) TestNewStorageProvisionerAPINonMachine(c *gc.C) {
   123  	tag := names.NewUnitTag("mysql/0")
   124  	authorizer := &apiservertesting.FakeAuthorizer{Tag: tag}
   125  	backend, storageBackend, err := storageprovisioner.NewStateBackends(s.State)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	_, err = storageprovisioner.NewStorageProvisionerAPIv3(backend, storageBackend, common.NewResources(), authorizer, nil, nil)
   128  	c.Assert(err, gc.ErrorMatches, "permission denied")
   129  }
   130  
   131  func (s *iaasProvisionerSuite) setupVolumes(c *gc.C) {
   132  	s.Factory.MakeMachine(c, &factory.MachineParams{
   133  		InstanceId: instance.Id("inst-id"),
   134  		Volumes: []state.HostVolumeParams{
   135  			{Volume: state.VolumeParams{Pool: "machinescoped", Size: 1024}},
   136  			{Volume: state.VolumeParams{Pool: "modelscoped", Size: 2048}},
   137  			{Volume: state.VolumeParams{Pool: "modelscoped", Size: 4096}},
   138  			{
   139  				Volume: state.VolumeParams{Pool: "modelscoped", Size: 4096},
   140  				Attachment: state.VolumeAttachmentParams{
   141  					ReadOnly: true,
   142  				},
   143  			},
   144  		},
   145  	})
   146  	// Only provision the first and third volumes.
   147  	err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0/0"), state.VolumeInfo{
   148  		HardwareId: "123",
   149  		VolumeId:   "abc",
   150  		Size:       1024,
   151  		Persistent: true,
   152  	})
   153  	c.Assert(err, jc.ErrorIsNil)
   154  	err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("2"), state.VolumeInfo{
   155  		HardwareId: "456",
   156  		VolumeId:   "def",
   157  		Size:       4096,
   158  	})
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	// Make a machine without storage for tests to use.
   162  	s.Factory.MakeMachine(c, nil)
   163  
   164  	// Make an unprovisioned machine with storage for tests to use.
   165  	// TODO(axw) extend testing/factory to allow creating unprovisioned
   166  	// machines.
   167  	_, err = s.State.AddOneMachine(state.MachineTemplate{
   168  		Series: "quantal",
   169  		Jobs:   []state.MachineJob{state.JobHostUnits},
   170  		Volumes: []state.HostVolumeParams{
   171  			{Volume: state.VolumeParams{Pool: "modelscoped", Size: 2048}},
   172  		},
   173  	})
   174  	c.Assert(err, jc.ErrorIsNil)
   175  }
   176  
   177  func (s *iaasProvisionerSuite) setupFilesystems(c *gc.C) {
   178  	s.Factory.MakeMachine(c, &factory.MachineParams{
   179  		InstanceId: instance.Id("inst-id"),
   180  		Filesystems: []state.HostFilesystemParams{{
   181  			Filesystem: state.FilesystemParams{Pool: "machinescoped", Size: 1024},
   182  			Attachment: state.FilesystemAttachmentParams{
   183  				Location: "/srv",
   184  				ReadOnly: true,
   185  			},
   186  		}, {
   187  			Filesystem: state.FilesystemParams{Pool: "modelscoped", Size: 2048},
   188  		}, {
   189  			Filesystem: state.FilesystemParams{Pool: "modelscoped", Size: 4096},
   190  		}},
   191  	})
   192  
   193  	// Only provision the first and third filesystems.
   194  	err := s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("0/0"), state.FilesystemInfo{
   195  		FilesystemId: "abc",
   196  		Size:         1024,
   197  	})
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	err = s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("2"), state.FilesystemInfo{
   200  		FilesystemId: "def",
   201  		Size:         4096,
   202  	})
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	// Make a machine without storage for tests to use.
   206  	s.Factory.MakeMachine(c, nil)
   207  
   208  	// Make an unprovisioned machine with storage for tests to use.
   209  	// TODO(axw) extend testing/factory to allow creating unprovisioned
   210  	// machines.
   211  	_, err = s.State.AddOneMachine(state.MachineTemplate{
   212  		Series: "quantal",
   213  		Jobs:   []state.MachineJob{state.JobHostUnits},
   214  		Filesystems: []state.HostFilesystemParams{{
   215  			Filesystem: state.FilesystemParams{Pool: "modelscoped", Size: 2048},
   216  		}},
   217  	})
   218  	c.Assert(err, jc.ErrorIsNil)
   219  }
   220  
   221  func (s *caasProvisionerSuite) setupFilesystems(c *gc.C) {
   222  	ch := s.Factory.MakeCharm(c, &factory.CharmParams{
   223  		Name:   "storage-filesystem",
   224  		Series: "kubernetes",
   225  	})
   226  	app := s.Factory.MakeApplication(c, &factory.ApplicationParams{
   227  		Charm: ch,
   228  		Name:  "mariadb",
   229  		Storage: map[string]state.StorageConstraints{
   230  			"data":  {Count: 1, Size: 1024},
   231  			"cache": {Count: 2, Size: 1024},
   232  		},
   233  	})
   234  	s.Factory.MakeUnit(c, &factory.UnitParams{Application: app})
   235  
   236  	// Only provision the first and third backing volumes.
   237  	err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("0"), state.VolumeInfo{
   238  		HardwareId: "123",
   239  		VolumeId:   "abc",
   240  		Size:       1024,
   241  		Persistent: true,
   242  	})
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	err = s.storageBackend.SetVolumeAttachmentInfo(
   245  		names.NewUnitTag("mariadb/0"),
   246  		names.NewVolumeTag("0"),
   247  		state.VolumeAttachmentInfo{ReadOnly: false},
   248  	)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  
   251  	err = s.storageBackend.SetVolumeInfo(names.NewVolumeTag("2"), state.VolumeInfo{
   252  		HardwareId: "456",
   253  		VolumeId:   "def",
   254  		Size:       4096,
   255  	})
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	err = s.storageBackend.SetVolumeAttachmentInfo(
   258  		names.NewUnitTag("mariadb/0"),
   259  		names.NewVolumeTag("2"),
   260  		state.VolumeAttachmentInfo{ReadOnly: false},
   261  	)
   262  	c.Assert(err, jc.ErrorIsNil)
   263  
   264  	// Only provision the first and third filesystems.
   265  	err = s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("0"), state.FilesystemInfo{
   266  		FilesystemId: "abc",
   267  		Size:         1024,
   268  	})
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	err = s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("2"), state.FilesystemInfo{
   271  		FilesystemId: "def",
   272  		Size:         4096,
   273  	})
   274  	c.Assert(err, jc.ErrorIsNil)
   275  }
   276  
   277  func (s *iaasProvisionerSuite) TestHostedVolumes(c *gc.C) {
   278  	// Only IAAS models support block storage right now.
   279  	s.setupVolumes(c)
   280  	s.authorizer.Controller = false
   281  
   282  	results, err := s.api.Volumes(params.Entities{
   283  		Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}},
   284  	})
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	c.Assert(results, gc.DeepEquals, params.VolumeResults{
   287  		Results: []params.VolumeResult{
   288  			{Result: params.Volume{
   289  				VolumeTag: "volume-0-0",
   290  				Info: params.VolumeInfo{
   291  					VolumeId:   "abc",
   292  					HardwareId: "123",
   293  					Size:       1024,
   294  					Persistent: true,
   295  					Pool:       "machinescoped",
   296  				},
   297  			}},
   298  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   299  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   300  		},
   301  	})
   302  }
   303  
   304  func (s *iaasProvisionerSuite) TestVolumesModel(c *gc.C) {
   305  	// Only IAAS models support block storage right now.
   306  	s.setupVolumes(c)
   307  	s.authorizer.Tag = names.NewMachineTag("2") // neither 0 nor 1
   308  
   309  	results, err := s.api.Volumes(params.Entities{
   310  		Entities: []params.Entity{
   311  			{"volume-0-0"},
   312  			{"volume-1"},
   313  			{"volume-2"},
   314  			{"volume-42"},
   315  		},
   316  	})
   317  	c.Assert(err, jc.ErrorIsNil)
   318  	c.Assert(results, gc.DeepEquals, params.VolumeResults{
   319  		Results: []params.VolumeResult{
   320  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   321  			{Error: common.ServerError(errors.NotProvisionedf(`volume "1"`))},
   322  			{Result: params.Volume{
   323  				VolumeTag: "volume-2",
   324  				Info: params.VolumeInfo{
   325  					VolumeId:   "def",
   326  					HardwareId: "456",
   327  					Size:       4096,
   328  					Pool:       "modelscoped",
   329  				},
   330  			}},
   331  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   332  		},
   333  	})
   334  }
   335  
   336  func (s *provisionerSuite) TestVolumesEmptyArgs(c *gc.C) {
   337  	results, err := s.api.Volumes(params.Entities{})
   338  	c.Assert(err, jc.ErrorIsNil)
   339  	c.Assert(results.Results, gc.HasLen, 0)
   340  }
   341  
   342  func (s *iaasProvisionerSuite) TestFilesystems(c *gc.C) {
   343  	s.setupFilesystems(c)
   344  	s.authorizer.Tag = names.NewMachineTag("2") // neither 0 nor 1
   345  
   346  	results, err := s.api.Filesystems(params.Entities{
   347  		Entities: []params.Entity{
   348  			{"filesystem-0-0"},
   349  			{"filesystem-1"},
   350  			{"filesystem-2"},
   351  			{"filesystem-42"},
   352  		},
   353  	})
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	c.Assert(results, jc.DeepEquals, params.FilesystemResults{
   356  		Results: []params.FilesystemResult{
   357  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   358  			{Error: common.ServerError(errors.NotProvisionedf(`filesystem "1"`))},
   359  			{Result: params.Filesystem{
   360  				FilesystemTag: "filesystem-2",
   361  				Info: params.FilesystemInfo{
   362  					FilesystemId: "def",
   363  					Size:         4096,
   364  					Pool:         "modelscoped",
   365  				},
   366  			}},
   367  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   368  		},
   369  	})
   370  }
   371  
   372  func (s *iaasProvisionerSuite) TestVolumeAttachments(c *gc.C) {
   373  	// Only IAAS models support block storage right now.
   374  	s.setupVolumes(c)
   375  	s.authorizer.Controller = false
   376  
   377  	err := s.storageBackend.SetVolumeAttachmentInfo(
   378  		names.NewMachineTag("0"),
   379  		names.NewVolumeTag("0/0"),
   380  		state.VolumeAttachmentInfo{DeviceName: "xvdf1"},
   381  	)
   382  	c.Assert(err, jc.ErrorIsNil)
   383  
   384  	results, err := s.api.VolumeAttachments(params.MachineStorageIds{
   385  		Ids: []params.MachineStorageId{{
   386  			MachineTag:    "machine-0",
   387  			AttachmentTag: "volume-0-0",
   388  		}, {
   389  			MachineTag:    "machine-0",
   390  			AttachmentTag: "volume-2", // volume attachment not provisioned
   391  		}, {
   392  			MachineTag:    "machine-0",
   393  			AttachmentTag: "volume-42",
   394  		}},
   395  	})
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	c.Assert(results, jc.DeepEquals, params.VolumeAttachmentResults{
   398  		Results: []params.VolumeAttachmentResult{
   399  			{Result: params.VolumeAttachment{
   400  				VolumeTag:  "volume-0-0",
   401  				MachineTag: "machine-0",
   402  				Info: params.VolumeAttachmentInfo{
   403  					DeviceName: "xvdf1",
   404  				},
   405  			}},
   406  			{Error: &params.Error{
   407  				Code:    params.CodeNotProvisioned,
   408  				Message: `volume attachment "2" on "machine 0" not provisioned`,
   409  			}},
   410  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   411  		},
   412  	})
   413  }
   414  
   415  func (s *iaasProvisionerSuite) TestFilesystemAttachments(c *gc.C) {
   416  	s.setupFilesystems(c)
   417  	s.authorizer.Controller = false
   418  
   419  	err := s.storageBackend.SetFilesystemAttachmentInfo(
   420  		names.NewMachineTag("0"),
   421  		names.NewFilesystemTag("0/0"),
   422  		state.FilesystemAttachmentInfo{
   423  			MountPoint: "/srv",
   424  			ReadOnly:   true,
   425  		},
   426  	)
   427  	c.Assert(err, jc.ErrorIsNil)
   428  
   429  	results, err := s.api.FilesystemAttachments(params.MachineStorageIds{
   430  		Ids: []params.MachineStorageId{{
   431  			MachineTag:    "machine-0",
   432  			AttachmentTag: "filesystem-0-0",
   433  		}, {
   434  			MachineTag:    "machine-0",
   435  			AttachmentTag: "filesystem-2", // filesystem attachment not provisioned
   436  		}, {
   437  			MachineTag:    "machine-0",
   438  			AttachmentTag: "filesystem-42",
   439  		}},
   440  	})
   441  	c.Assert(err, jc.ErrorIsNil)
   442  	c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentResults{
   443  		Results: []params.FilesystemAttachmentResult{
   444  			{Result: params.FilesystemAttachment{
   445  				FilesystemTag: "filesystem-0-0",
   446  				MachineTag:    "machine-0",
   447  				Info: params.FilesystemAttachmentInfo{
   448  					MountPoint: "/srv",
   449  					ReadOnly:   true,
   450  				},
   451  			}},
   452  			{Error: &params.Error{
   453  				Code:    params.CodeNotProvisioned,
   454  				Message: `filesystem attachment "2" on "machine 0" not provisioned`,
   455  			}},
   456  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   457  		},
   458  	})
   459  }
   460  
   461  func (s *iaasProvisionerSuite) TestVolumeParams(c *gc.C) {
   462  	// Only IAAS models support block storage right now.
   463  	s.setupVolumes(c)
   464  	results, err := s.api.VolumeParams(params.Entities{
   465  		Entities: []params.Entity{
   466  			{"volume-0-0"},
   467  			{"volume-1"},
   468  			{"volume-3"},
   469  			{"volume-42"},
   470  		},
   471  	})
   472  	c.Assert(err, jc.ErrorIsNil)
   473  	c.Assert(results, jc.DeepEquals, params.VolumeParamsResults{
   474  		Results: []params.VolumeParamsResult{
   475  			{Result: params.VolumeParams{
   476  				VolumeTag: "volume-0-0",
   477  				Size:      1024,
   478  				Provider:  "machinescoped",
   479  				Tags: map[string]string{
   480  					tags.JujuController: testing.ControllerTag.Id(),
   481  					tags.JujuModel:      testing.ModelTag.Id(),
   482  				},
   483  				Attachment: &params.VolumeAttachmentParams{
   484  					MachineTag: "machine-0",
   485  					VolumeTag:  "volume-0-0",
   486  					Provider:   "machinescoped",
   487  					InstanceId: "inst-id",
   488  				},
   489  			}},
   490  			{Result: params.VolumeParams{
   491  				VolumeTag: "volume-1",
   492  				Size:      2048,
   493  				Provider:  "modelscoped",
   494  				Tags: map[string]string{
   495  					tags.JujuController: testing.ControllerTag.Id(),
   496  					tags.JujuModel:      testing.ModelTag.Id(),
   497  				},
   498  				Attachment: &params.VolumeAttachmentParams{
   499  					MachineTag: "machine-0",
   500  					VolumeTag:  "volume-1",
   501  					Provider:   "modelscoped",
   502  					InstanceId: "inst-id",
   503  				},
   504  			}},
   505  			{Result: params.VolumeParams{
   506  				VolumeTag: "volume-3",
   507  				Size:      4096,
   508  				Provider:  "modelscoped",
   509  				Tags: map[string]string{
   510  					tags.JujuController: testing.ControllerTag.Id(),
   511  					tags.JujuModel:      testing.ModelTag.Id(),
   512  				},
   513  				Attachment: &params.VolumeAttachmentParams{
   514  					MachineTag: "machine-0",
   515  					VolumeTag:  "volume-3",
   516  					Provider:   "modelscoped",
   517  					InstanceId: "inst-id",
   518  					ReadOnly:   true,
   519  				},
   520  			}},
   521  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   522  		},
   523  	})
   524  }
   525  
   526  func (s *provisionerSuite) TestVolumeParamsEmptyArgs(c *gc.C) {
   527  	results, err := s.api.VolumeParams(params.Entities{})
   528  	c.Assert(err, jc.ErrorIsNil)
   529  	c.Assert(results.Results, gc.HasLen, 0)
   530  }
   531  
   532  func (s *iaasProvisionerSuite) TestRemoveVolumeParams(c *gc.C) {
   533  	// Only IAAS models support block storage right now.
   534  	s.setupVolumes(c)
   535  
   536  	// Deploy an application that will create a storage instance,
   537  	// so we can release the storage and show the effects on the
   538  	// RemoveVolumeParams.
   539  	application := s.Factory.MakeApplication(c, &factory.ApplicationParams{
   540  		Charm: s.Factory.MakeCharm(c, &factory.CharmParams{
   541  			Name: "storage-block",
   542  		}),
   543  		Storage: map[string]state.StorageConstraints{
   544  			"data": {
   545  				Count: 1,
   546  				Size:  1,
   547  				Pool:  "modelscoped",
   548  			},
   549  		},
   550  	})
   551  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{
   552  		Application: application,
   553  	})
   554  	storage, err := s.storageBackend.AllStorageInstances()
   555  	c.Assert(err, jc.ErrorIsNil)
   556  	c.Assert(storage, gc.HasLen, 1)
   557  	storageVolume, err := s.storageBackend.StorageInstanceVolume(storage[0].StorageTag())
   558  	c.Assert(err, jc.ErrorIsNil)
   559  	err = s.storageBackend.SetVolumeInfo(storageVolume.VolumeTag(), state.VolumeInfo{
   560  		VolumeId:   "zing",
   561  		Size:       1,
   562  		Persistent: true,
   563  	})
   564  	c.Assert(err, jc.ErrorIsNil)
   565  
   566  	// Make volumes 0/0 and 3 Dead.
   567  	for _, volumeId := range []string{"0/0", "3"} {
   568  		volumeTag := names.NewVolumeTag(volumeId)
   569  		machineTag := names.NewMachineTag("0")
   570  		err = s.storageBackend.DestroyVolume(volumeTag)
   571  		c.Assert(err, jc.ErrorIsNil)
   572  		err = s.storageBackend.DetachVolume(machineTag, volumeTag)
   573  		c.Assert(err, jc.ErrorIsNil)
   574  		err = s.storageBackend.RemoveVolumeAttachment(machineTag, volumeTag)
   575  		c.Assert(err, jc.ErrorIsNil)
   576  	}
   577  
   578  	// Make the "data" storage volume Dead, releasing.
   579  	err = unit.Destroy()
   580  	c.Assert(err, jc.ErrorIsNil)
   581  	err = s.storageBackend.ReleaseStorageInstance(storage[0].StorageTag(), true)
   582  	c.Assert(err, jc.ErrorIsNil)
   583  	err = s.storageBackend.DetachStorage(storage[0].StorageTag(), unit.UnitTag())
   584  	c.Assert(err, jc.ErrorIsNil)
   585  	unitMachineId, err := unit.AssignedMachineId()
   586  	c.Assert(err, jc.ErrorIsNil)
   587  	unitMachineTag := names.NewMachineTag(unitMachineId)
   588  	err = s.storageBackend.DetachVolume(unitMachineTag, storageVolume.VolumeTag())
   589  	c.Assert(err, jc.ErrorIsNil)
   590  	err = s.storageBackend.RemoveVolumeAttachment(unitMachineTag, storageVolume.VolumeTag())
   591  	c.Assert(err, jc.ErrorIsNil)
   592  
   593  	results, err := s.api.RemoveVolumeParams(params.Entities{
   594  		Entities: []params.Entity{
   595  			{"volume-0-0"},
   596  			{storageVolume.Tag().String()},
   597  			{"volume-1"},
   598  			{"volume-3"},
   599  			{"volume-42"},
   600  		},
   601  	})
   602  	c.Assert(err, jc.ErrorIsNil)
   603  	c.Assert(results, jc.DeepEquals, params.RemoveVolumeParamsResults{
   604  		Results: []params.RemoveVolumeParamsResult{{
   605  			Result: params.RemoveVolumeParams{
   606  				Provider: "machinescoped",
   607  				VolumeId: "abc",
   608  				Destroy:  true,
   609  			},
   610  		}, {
   611  			Result: params.RemoveVolumeParams{
   612  				Provider: "modelscoped",
   613  				VolumeId: "zing",
   614  				Destroy:  false,
   615  			},
   616  		}, {
   617  			Error: &params.Error{Message: `volume 1 is not dead (alive)`},
   618  		}, {
   619  			Error: &params.Error{Message: `volume "3" not provisioned`, Code: "not provisioned"},
   620  		}, {
   621  			Error: &params.Error{Message: "permission denied", Code: "unauthorized access"},
   622  		}},
   623  	})
   624  }
   625  
   626  func (s *iaasProvisionerSuite) TestFilesystemParams(c *gc.C) {
   627  	s.setupFilesystems(c)
   628  	results, err := s.api.FilesystemParams(params.Entities{
   629  		Entities: []params.Entity{{"filesystem-0-0"}, {"filesystem-1"}, {"filesystem-42"}},
   630  	})
   631  	c.Assert(err, jc.ErrorIsNil)
   632  	c.Assert(results, jc.DeepEquals, params.FilesystemParamsResults{
   633  		Results: []params.FilesystemParamsResult{
   634  			{Result: params.FilesystemParams{
   635  				FilesystemTag: "filesystem-0-0",
   636  				Size:          1024,
   637  				Provider:      "machinescoped",
   638  				Tags: map[string]string{
   639  					tags.JujuController: testing.ControllerTag.Id(),
   640  					tags.JujuModel:      testing.ModelTag.Id(),
   641  				},
   642  			}},
   643  			{Result: params.FilesystemParams{
   644  				FilesystemTag: "filesystem-1",
   645  				Size:          2048,
   646  				Provider:      "modelscoped",
   647  				Tags: map[string]string{
   648  					tags.JujuController: testing.ControllerTag.Id(),
   649  					tags.JujuModel:      testing.ModelTag.Id(),
   650  				},
   651  			}},
   652  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   653  		},
   654  	})
   655  }
   656  
   657  func (s *iaasProvisionerSuite) TestRemoveFilesystemParams(c *gc.C) {
   658  	s.setupFilesystems(c)
   659  
   660  	// Deploy an application that will create a storage instance,
   661  	// so we can release the storage and show the effects on the
   662  	// RemoveFilesystemParams.
   663  	application := s.Factory.MakeApplication(c, &factory.ApplicationParams{
   664  		Charm: s.Factory.MakeCharm(c, &factory.CharmParams{
   665  			Name: "storage-filesystem",
   666  		}),
   667  		Storage: map[string]state.StorageConstraints{
   668  			"data": {
   669  				Count: 1,
   670  				Size:  1,
   671  				Pool:  "modelscoped",
   672  			},
   673  		},
   674  	})
   675  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{
   676  		Application: application,
   677  	})
   678  	storage, err := s.storageBackend.AllStorageInstances()
   679  	c.Assert(err, jc.ErrorIsNil)
   680  	c.Assert(storage, gc.HasLen, 1)
   681  	storageFilesystem, err := s.storageBackend.StorageInstanceFilesystem(storage[0].StorageTag())
   682  	c.Assert(err, jc.ErrorIsNil)
   683  	err = s.storageBackend.SetFilesystemInfo(storageFilesystem.FilesystemTag(), state.FilesystemInfo{
   684  		FilesystemId: "zing",
   685  		Size:         1,
   686  	})
   687  	c.Assert(err, jc.ErrorIsNil)
   688  
   689  	// Make filesystems 0/0 and 1 Dead.
   690  	for _, filesystemId := range []string{"0/0", "1"} {
   691  		filesystemTag := names.NewFilesystemTag(filesystemId)
   692  		machineTag := names.NewMachineTag("0")
   693  		err = s.storageBackend.DestroyFilesystem(filesystemTag)
   694  		c.Assert(err, jc.ErrorIsNil)
   695  		err = s.storageBackend.DetachFilesystem(machineTag, filesystemTag)
   696  		c.Assert(err, jc.ErrorIsNil)
   697  		err = s.storageBackend.RemoveFilesystemAttachment(machineTag, filesystemTag)
   698  		c.Assert(err, jc.ErrorIsNil)
   699  	}
   700  
   701  	// Make the "data" storage filesystem Dead, releasing.
   702  	err = unit.Destroy()
   703  	c.Assert(err, jc.ErrorIsNil)
   704  	err = s.storageBackend.ReleaseStorageInstance(storage[0].StorageTag(), true)
   705  	c.Assert(err, jc.ErrorIsNil)
   706  	err = s.storageBackend.DetachStorage(storage[0].StorageTag(), unit.UnitTag())
   707  	c.Assert(err, jc.ErrorIsNil)
   708  	unitMachineId, err := unit.AssignedMachineId()
   709  	c.Assert(err, jc.ErrorIsNil)
   710  	unitMachineTag := names.NewMachineTag(unitMachineId)
   711  	err = s.storageBackend.DetachFilesystem(unitMachineTag, storageFilesystem.FilesystemTag())
   712  	c.Assert(err, jc.ErrorIsNil)
   713  	err = s.storageBackend.RemoveFilesystemAttachment(unitMachineTag, storageFilesystem.FilesystemTag())
   714  	c.Assert(err, jc.ErrorIsNil)
   715  
   716  	results, err := s.api.RemoveFilesystemParams(params.Entities{
   717  		Entities: []params.Entity{
   718  			{"filesystem-0-0"},
   719  			{storageFilesystem.Tag().String()},
   720  			{"filesystem-1"},
   721  			{"filesystem-2"},
   722  			{"filesystem-42"},
   723  		},
   724  	})
   725  	c.Assert(err, jc.ErrorIsNil)
   726  	c.Assert(results, jc.DeepEquals, params.RemoveFilesystemParamsResults{
   727  		Results: []params.RemoveFilesystemParamsResult{{
   728  			Result: params.RemoveFilesystemParams{
   729  				Provider:     "machinescoped",
   730  				FilesystemId: "abc",
   731  				Destroy:      true,
   732  			},
   733  		}, {
   734  			Result: params.RemoveFilesystemParams{
   735  				Provider:     "modelscoped",
   736  				FilesystemId: "zing",
   737  				Destroy:      false,
   738  			},
   739  		}, {
   740  			Error: &params.Error{Message: `filesystem "1" not provisioned`, Code: "not provisioned"},
   741  		}, {
   742  			Error: &params.Error{Message: `filesystem 2 is not dead (alive)`},
   743  		}, {
   744  			Error: &params.Error{Message: "permission denied", Code: "unauthorized access"},
   745  		}},
   746  	})
   747  }
   748  
   749  func (s *iaasProvisionerSuite) TestVolumeAttachmentParams(c *gc.C) {
   750  	// Only IAAS models support block storage right now.
   751  	s.setupVolumes(c)
   752  
   753  	err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("3"), state.VolumeInfo{
   754  		HardwareId: "123",
   755  		VolumeId:   "xyz",
   756  		Size:       1024,
   757  	})
   758  	c.Assert(err, jc.ErrorIsNil)
   759  
   760  	err = s.storageBackend.SetVolumeAttachmentInfo(
   761  		names.NewMachineTag("0"),
   762  		names.NewVolumeTag("3"),
   763  		state.VolumeAttachmentInfo{
   764  			DeviceName: "xvdf1",
   765  			ReadOnly:   true,
   766  		},
   767  	)
   768  	c.Assert(err, jc.ErrorIsNil)
   769  
   770  	results, err := s.api.VolumeAttachmentParams(params.MachineStorageIds{
   771  		Ids: []params.MachineStorageId{{
   772  			MachineTag:    "machine-0",
   773  			AttachmentTag: "volume-0-0",
   774  		}, {
   775  			MachineTag:    "machine-0",
   776  			AttachmentTag: "volume-1",
   777  		}, {
   778  			MachineTag:    "machine-0",
   779  			AttachmentTag: "volume-3",
   780  		}, {
   781  			MachineTag:    "machine-2",
   782  			AttachmentTag: "volume-4",
   783  		}, {
   784  			MachineTag:    "machine-0",
   785  			AttachmentTag: "volume-42",
   786  		}},
   787  	})
   788  	c.Assert(err, jc.ErrorIsNil)
   789  	c.Assert(results, jc.DeepEquals, params.VolumeAttachmentParamsResults{
   790  		Results: []params.VolumeAttachmentParamsResult{
   791  			{Result: params.VolumeAttachmentParams{
   792  				MachineTag: "machine-0",
   793  				VolumeTag:  "volume-0-0",
   794  				InstanceId: "inst-id",
   795  				VolumeId:   "abc",
   796  				Provider:   "machinescoped",
   797  			}},
   798  			{Result: params.VolumeAttachmentParams{
   799  				MachineTag: "machine-0",
   800  				VolumeTag:  "volume-1",
   801  				InstanceId: "inst-id",
   802  				Provider:   "modelscoped",
   803  			}},
   804  			{Result: params.VolumeAttachmentParams{
   805  				MachineTag: "machine-0",
   806  				VolumeTag:  "volume-3",
   807  				InstanceId: "inst-id",
   808  				VolumeId:   "xyz",
   809  				Provider:   "modelscoped",
   810  				ReadOnly:   true,
   811  			}},
   812  			{Result: params.VolumeAttachmentParams{
   813  				MachineTag: "machine-2",
   814  				VolumeTag:  "volume-4",
   815  				Provider:   "modelscoped",
   816  			}},
   817  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   818  		},
   819  	})
   820  }
   821  
   822  func (s *iaasProvisionerSuite) TestFilesystemAttachmentParams(c *gc.C) {
   823  	s.setupFilesystems(c)
   824  
   825  	err := s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("1"), state.FilesystemInfo{
   826  		FilesystemId: "fsid",
   827  		Size:         1024,
   828  	})
   829  	c.Assert(err, jc.ErrorIsNil)
   830  
   831  	err = s.storageBackend.SetFilesystemAttachmentInfo(
   832  		names.NewMachineTag("0"),
   833  		names.NewFilesystemTag("1"),
   834  		state.FilesystemAttachmentInfo{
   835  			MountPoint: "/in/the/place",
   836  		},
   837  	)
   838  	c.Assert(err, jc.ErrorIsNil)
   839  
   840  	results, err := s.api.FilesystemAttachmentParams(params.MachineStorageIds{
   841  		Ids: []params.MachineStorageId{{
   842  			MachineTag:    "machine-0",
   843  			AttachmentTag: "filesystem-0-0",
   844  		}, {
   845  			MachineTag:    "machine-0",
   846  			AttachmentTag: "filesystem-1",
   847  		}, {
   848  			MachineTag:    "machine-2",
   849  			AttachmentTag: "filesystem-3",
   850  		}, {
   851  			MachineTag:    "machine-0",
   852  			AttachmentTag: "filesystem-42",
   853  		}},
   854  	})
   855  	c.Assert(err, jc.ErrorIsNil)
   856  	c.Assert(results, jc.DeepEquals, params.FilesystemAttachmentParamsResults{
   857  		Results: []params.FilesystemAttachmentParamsResult{
   858  			{Result: params.FilesystemAttachmentParams{
   859  				MachineTag:    "machine-0",
   860  				FilesystemTag: "filesystem-0-0",
   861  				InstanceId:    "inst-id",
   862  				FilesystemId:  "abc",
   863  				Provider:      "machinescoped",
   864  				MountPoint:    "/srv",
   865  				ReadOnly:      true,
   866  			}},
   867  			{Result: params.FilesystemAttachmentParams{
   868  				MachineTag:    "machine-0",
   869  				FilesystemTag: "filesystem-1",
   870  				InstanceId:    "inst-id",
   871  				FilesystemId:  "fsid",
   872  				Provider:      "modelscoped",
   873  				MountPoint:    "/in/the/place",
   874  			}},
   875  			{Result: params.FilesystemAttachmentParams{
   876  				MachineTag:    "machine-2",
   877  				FilesystemTag: "filesystem-3",
   878  				Provider:      "modelscoped",
   879  			}},
   880  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   881  		},
   882  	})
   883  }
   884  
   885  func (s *iaasProvisionerSuite) TestSetVolumeAttachmentInfo(c *gc.C) {
   886  	// Only IAAS models support block storage right now.
   887  	s.setupVolumes(c)
   888  
   889  	err := s.storageBackend.SetVolumeInfo(names.NewVolumeTag("4"), state.VolumeInfo{
   890  		VolumeId: "whatever",
   891  		Size:     1024,
   892  	})
   893  	c.Assert(err, jc.ErrorIsNil)
   894  
   895  	results, err := s.api.SetVolumeAttachmentInfo(params.VolumeAttachments{
   896  		VolumeAttachments: []params.VolumeAttachment{{
   897  			MachineTag: "machine-0",
   898  			VolumeTag:  "volume-0-0",
   899  			Info: params.VolumeAttachmentInfo{
   900  				DeviceName: "sda",
   901  				ReadOnly:   true,
   902  			},
   903  		}, {
   904  			MachineTag: "machine-0",
   905  			VolumeTag:  "volume-1",
   906  			Info: params.VolumeAttachmentInfo{
   907  				DeviceName: "sdb",
   908  			},
   909  		}, {
   910  			MachineTag: "machine-2",
   911  			VolumeTag:  "volume-4",
   912  			Info: params.VolumeAttachmentInfo{
   913  				DeviceName: "sdc",
   914  			},
   915  		}, {
   916  			MachineTag: "machine-0",
   917  			VolumeTag:  "volume-42",
   918  			Info: params.VolumeAttachmentInfo{
   919  				DeviceName: "sdd",
   920  			},
   921  		}},
   922  	})
   923  	c.Assert(err, jc.ErrorIsNil)
   924  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
   925  		Results: []params.ErrorResult{
   926  			{},
   927  			{Error: &params.Error{Message: `cannot set info for volume attachment 1:0: volume "1" not provisioned`, Code: "not provisioned"}},
   928  			{Error: &params.Error{Message: `cannot set info for volume attachment 4:2: machine 2 not provisioned`, Code: "not provisioned"}},
   929  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   930  		},
   931  	})
   932  }
   933  
   934  func (s *iaasProvisionerSuite) TestSetFilesystemAttachmentInfo(c *gc.C) {
   935  	s.setupFilesystems(c)
   936  
   937  	err := s.storageBackend.SetFilesystemInfo(names.NewFilesystemTag("3"), state.FilesystemInfo{
   938  		FilesystemId: "whatever",
   939  		Size:         1024,
   940  	})
   941  	c.Assert(err, jc.ErrorIsNil)
   942  
   943  	results, err := s.api.SetFilesystemAttachmentInfo(params.FilesystemAttachments{
   944  		FilesystemAttachments: []params.FilesystemAttachment{{
   945  			MachineTag:    "machine-0",
   946  			FilesystemTag: "filesystem-0-0",
   947  			Info: params.FilesystemAttachmentInfo{
   948  				MountPoint: "/srv/a",
   949  				ReadOnly:   true,
   950  			},
   951  		}, {
   952  			MachineTag:    "machine-0",
   953  			FilesystemTag: "filesystem-1",
   954  			Info: params.FilesystemAttachmentInfo{
   955  				MountPoint: "/srv/b",
   956  			},
   957  		}, {
   958  			MachineTag:    "machine-2",
   959  			FilesystemTag: "filesystem-3",
   960  			Info: params.FilesystemAttachmentInfo{
   961  				MountPoint: "/srv/c",
   962  			},
   963  		}, {
   964  			MachineTag:    "machine-0",
   965  			FilesystemTag: "filesystem-42",
   966  			Info: params.FilesystemAttachmentInfo{
   967  				MountPoint: "/srv/d",
   968  			},
   969  		}},
   970  	})
   971  	c.Assert(err, jc.ErrorIsNil)
   972  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
   973  		Results: []params.ErrorResult{
   974  			{},
   975  			{Error: &params.Error{Message: `cannot set info for filesystem attachment 1:0: filesystem "1" not provisioned`, Code: "not provisioned"}},
   976  			{Error: &params.Error{Message: `cannot set info for filesystem attachment 3:2: machine 2 not provisioned`, Code: "not provisioned"}},
   977  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
   978  		},
   979  	})
   980  }
   981  
   982  func (s *caasProvisionerSuite) TestWatchApplications(c *gc.C) {
   983  	ch := s.Factory.MakeCharm(c, &factory.CharmParams{
   984  		Name:   "storage-filesystem",
   985  		Series: "kubernetes",
   986  	})
   987  	s.Factory.MakeApplication(c, &factory.ApplicationParams{
   988  		Charm: ch,
   989  		Name:  "mariadb",
   990  		Storage: map[string]state.StorageConstraints{
   991  			"data": {Count: 1, Size: 1024},
   992  		},
   993  	})
   994  
   995  	result, err := s.api.WatchApplications()
   996  	c.Assert(err, jc.ErrorIsNil)
   997  	c.Assert(result.StringsWatcherId, gc.Equals, "1")
   998  	c.Assert(result.Changes, jc.DeepEquals, []string{"mariadb"})
   999  
  1000  	w := s.resources.Get("1").(state.StringsWatcher)
  1001  	s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1002  		Charm: ch,
  1003  		Name:  "mysql",
  1004  		Storage: map[string]state.StorageConstraints{
  1005  			"data": {Count: 1, Size: 1024},
  1006  		},
  1007  	})
  1008  	wc := statetesting.NewStringsWatcherC(c, s.State, w)
  1009  	wc.AssertChange("mysql")
  1010  }
  1011  
  1012  func (s *iaasProvisionerSuite) TestWatchVolumes(c *gc.C) {
  1013  	// Only IAAS models support block storage right now.
  1014  	s.setupVolumes(c)
  1015  	s.Factory.MakeMachine(c, nil)
  1016  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1017  
  1018  	args := params.Entities{Entities: []params.Entity{
  1019  		{"machine-0"},
  1020  		{s.Model.ModelTag().String()},
  1021  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
  1022  		{"machine-1"},
  1023  		{"machine-42"}},
  1024  	}
  1025  	result, err := s.api.WatchVolumes(args)
  1026  	c.Assert(err, jc.ErrorIsNil)
  1027  	sort.Strings(result.Results[1].Changes)
  1028  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
  1029  		Results: []params.StringsWatchResult{
  1030  			{StringsWatcherId: "1", Changes: []string{"0/0"}},
  1031  			{StringsWatcherId: "2", Changes: []string{"1", "2", "3", "4"}},
  1032  			{Error: apiservertesting.ErrUnauthorized},
  1033  			{Error: apiservertesting.ErrUnauthorized},
  1034  			{Error: apiservertesting.ErrUnauthorized},
  1035  		},
  1036  	})
  1037  
  1038  	// Verify the resources were registered and stop them when done.
  1039  	c.Assert(s.resources.Count(), gc.Equals, 2)
  1040  	v0Watcher := s.resources.Get("1")
  1041  	defer statetesting.AssertStop(c, v0Watcher)
  1042  	v1Watcher := s.resources.Get("2")
  1043  	defer statetesting.AssertStop(c, v1Watcher)
  1044  
  1045  	// Check that the Watch has consumed the initial events ("returned" in
  1046  	// the Watch call)
  1047  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
  1048  	wc.AssertNoChange()
  1049  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
  1050  	wc.AssertNoChange()
  1051  }
  1052  
  1053  func (s *iaasProvisionerSuite) TestWatchVolumeAttachments(c *gc.C) {
  1054  	// Only IAAS models support block storage right now.
  1055  	s.setupVolumes(c)
  1056  	s.Factory.MakeMachine(c, nil)
  1057  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1058  
  1059  	args := params.Entities{Entities: []params.Entity{
  1060  		{"machine-0"},
  1061  		{s.Model.ModelTag().String()},
  1062  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
  1063  		{"machine-1"},
  1064  		{"machine-42"}},
  1065  	}
  1066  	result, err := s.api.WatchVolumeAttachments(args)
  1067  	c.Assert(err, jc.ErrorIsNil)
  1068  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
  1069  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
  1070  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
  1071  		Results: []params.MachineStorageIdsWatchResult{
  1072  			{
  1073  				MachineStorageIdsWatcherId: "1",
  1074  				Changes: []params.MachineStorageId{{
  1075  					MachineTag:    "machine-0",
  1076  					AttachmentTag: "volume-0-0",
  1077  				}},
  1078  			},
  1079  			{
  1080  				MachineStorageIdsWatcherId: "2",
  1081  				Changes: []params.MachineStorageId{{
  1082  					MachineTag:    "machine-0",
  1083  					AttachmentTag: "volume-1",
  1084  				}, {
  1085  					MachineTag:    "machine-0",
  1086  					AttachmentTag: "volume-2",
  1087  				}, {
  1088  					MachineTag:    "machine-0",
  1089  					AttachmentTag: "volume-3",
  1090  				}, {
  1091  					MachineTag:    "machine-2",
  1092  					AttachmentTag: "volume-4",
  1093  				}},
  1094  			},
  1095  			{Error: apiservertesting.ErrUnauthorized},
  1096  			{Error: apiservertesting.ErrUnauthorized},
  1097  			{Error: apiservertesting.ErrUnauthorized},
  1098  		},
  1099  	})
  1100  
  1101  	// Verify the resources were registered and stop them when done.
  1102  	c.Assert(s.resources.Count(), gc.Equals, 2)
  1103  	v0Watcher := s.resources.Get("1")
  1104  	defer statetesting.AssertStop(c, v0Watcher)
  1105  	v1Watcher := s.resources.Get("2")
  1106  	defer statetesting.AssertStop(c, v1Watcher)
  1107  
  1108  	// Check that the Watch has consumed the initial events ("returned" in
  1109  	// the Watch call)
  1110  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
  1111  	wc.AssertNoChange()
  1112  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
  1113  	wc.AssertNoChange()
  1114  }
  1115  
  1116  func (s *iaasProvisionerSuite) TestWatchFilesystems(c *gc.C) {
  1117  	s.setupFilesystems(c)
  1118  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1119  
  1120  	args := params.Entities{Entities: []params.Entity{
  1121  		{"machine-0"},
  1122  		{s.Model.ModelTag().String()},
  1123  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
  1124  		{"machine-1"},
  1125  		{"machine-42"}},
  1126  	}
  1127  	result, err := s.api.WatchFilesystems(args)
  1128  	c.Assert(err, jc.ErrorIsNil)
  1129  	sort.Strings(result.Results[1].Changes)
  1130  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
  1131  		Results: []params.StringsWatchResult{
  1132  			{
  1133  				StringsWatcherId: "1",
  1134  				Changes:          []string{"0/0"},
  1135  			},
  1136  			{
  1137  				StringsWatcherId: "2",
  1138  				Changes:          []string{"1", "2", "3"},
  1139  			},
  1140  			{Error: apiservertesting.ErrUnauthorized},
  1141  			{Error: apiservertesting.ErrUnauthorized},
  1142  			{Error: apiservertesting.ErrUnauthorized},
  1143  		},
  1144  	})
  1145  
  1146  	// Verify the resources were registered and stop them when done.
  1147  	c.Assert(s.resources.Count(), gc.Equals, 2)
  1148  	v0Watcher := s.resources.Get("1")
  1149  	defer statetesting.AssertStop(c, v0Watcher)
  1150  	v1Watcher := s.resources.Get("2")
  1151  	defer statetesting.AssertStop(c, v1Watcher)
  1152  
  1153  	// Check that the Watch has consumed the initial events ("returned" in
  1154  	// the Watch call)
  1155  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
  1156  	wc.AssertNoChange()
  1157  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
  1158  	wc.AssertNoChange()
  1159  }
  1160  
  1161  func (s *iaasProvisionerSuite) TestWatchFilesystemAttachments(c *gc.C) {
  1162  	s.setupFilesystems(c)
  1163  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1164  
  1165  	args := params.Entities{Entities: []params.Entity{
  1166  		{"machine-0"},
  1167  		{s.Model.ModelTag().String()},
  1168  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
  1169  		{"machine-1"},
  1170  		{"machine-42"}},
  1171  	}
  1172  	result, err := s.api.WatchFilesystemAttachments(args)
  1173  	c.Assert(err, jc.ErrorIsNil)
  1174  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
  1175  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
  1176  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
  1177  		Results: []params.MachineStorageIdsWatchResult{
  1178  			{
  1179  				MachineStorageIdsWatcherId: "1",
  1180  				Changes: []params.MachineStorageId{{
  1181  					MachineTag:    "machine-0",
  1182  					AttachmentTag: "filesystem-0-0",
  1183  				}},
  1184  			},
  1185  			{
  1186  				MachineStorageIdsWatcherId: "2",
  1187  				Changes: []params.MachineStorageId{{
  1188  					MachineTag:    "machine-0",
  1189  					AttachmentTag: "filesystem-1",
  1190  				}, {
  1191  					MachineTag:    "machine-0",
  1192  					AttachmentTag: "filesystem-2",
  1193  				}, {
  1194  					MachineTag:    "machine-2",
  1195  					AttachmentTag: "filesystem-3",
  1196  				}},
  1197  			},
  1198  			{Error: apiservertesting.ErrUnauthorized},
  1199  			{Error: apiservertesting.ErrUnauthorized},
  1200  			{Error: apiservertesting.ErrUnauthorized},
  1201  		},
  1202  	})
  1203  
  1204  	// Verify the resources were registered and stop them when done.
  1205  	c.Assert(s.resources.Count(), gc.Equals, 2)
  1206  	v0Watcher := s.resources.Get("1")
  1207  	defer statetesting.AssertStop(c, v0Watcher)
  1208  	v1Watcher := s.resources.Get("2")
  1209  	defer statetesting.AssertStop(c, v1Watcher)
  1210  
  1211  	// Check that the Watch has consumed the initial events ("returned" in
  1212  	// the Watch call)
  1213  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
  1214  	wc.AssertNoChange()
  1215  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
  1216  	wc.AssertNoChange()
  1217  }
  1218  
  1219  func (s *iaasProvisionerSuite) TestWatchBlockDevices(c *gc.C) {
  1220  	s.Factory.MakeMachine(c, nil)
  1221  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1222  
  1223  	args := params.Entities{Entities: []params.Entity{
  1224  		{"machine-0"},
  1225  		{"application-mysql"},
  1226  		{"machine-1"},
  1227  		{"machine-42"}},
  1228  	}
  1229  	results, err := s.api.WatchBlockDevices(args)
  1230  	c.Assert(err, jc.ErrorIsNil)
  1231  	c.Assert(results, jc.DeepEquals, params.NotifyWatchResults{
  1232  		Results: []params.NotifyWatchResult{
  1233  			{NotifyWatcherId: "1"},
  1234  			{Error: &params.Error{Message: `"application-mysql" is not a valid machine tag`}},
  1235  			{Error: apiservertesting.ErrUnauthorized},
  1236  			{Error: apiservertesting.ErrUnauthorized},
  1237  		},
  1238  	})
  1239  
  1240  	// Verify the resources were registered and stop them when done.
  1241  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1242  	watcher := s.resources.Get("1")
  1243  	defer statetesting.AssertStop(c, watcher)
  1244  
  1245  	// Check that the Watch has consumed the initial event.
  1246  	wc := statetesting.NewNotifyWatcherC(c, s.State, watcher.(state.NotifyWatcher))
  1247  	wc.AssertNoChange()
  1248  
  1249  	m, err := s.State.Machine("0")
  1250  	c.Assert(err, jc.ErrorIsNil)
  1251  	err = m.SetMachineBlockDevices(state.BlockDeviceInfo{
  1252  		DeviceName: "sda",
  1253  		Size:       123,
  1254  	})
  1255  	c.Assert(err, jc.ErrorIsNil)
  1256  	wc.AssertOneChange()
  1257  }
  1258  
  1259  func (s *iaasProvisionerSuite) TestVolumeBlockDevices(c *gc.C) {
  1260  	s.setupVolumes(c)
  1261  	s.Factory.MakeMachine(c, nil)
  1262  
  1263  	err := s.storageBackend.SetVolumeAttachmentInfo(
  1264  		names.NewMachineTag("0"),
  1265  		names.NewVolumeTag("0/0"),
  1266  		state.VolumeAttachmentInfo{},
  1267  	)
  1268  	c.Assert(err, jc.ErrorIsNil)
  1269  
  1270  	machine0, err := s.State.Machine("0")
  1271  	c.Assert(err, jc.ErrorIsNil)
  1272  	err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{
  1273  		DeviceName: "sda",
  1274  		Size:       123,
  1275  		HardwareId: "123", // matches volume-0/0
  1276  	})
  1277  	c.Assert(err, jc.ErrorIsNil)
  1278  
  1279  	args := params.MachineStorageIds{Ids: []params.MachineStorageId{
  1280  		{MachineTag: "machine-0", AttachmentTag: "volume-0-0"},
  1281  		{MachineTag: "machine-0", AttachmentTag: "volume-0-1"},
  1282  		{MachineTag: "machine-0", AttachmentTag: "volume-0-2"},
  1283  		{MachineTag: "machine-1", AttachmentTag: "volume-1"},
  1284  		{MachineTag: "machine-42", AttachmentTag: "volume-42"},
  1285  		{MachineTag: "application-mysql", AttachmentTag: "volume-1"},
  1286  	}}
  1287  	results, err := s.api.VolumeBlockDevices(args)
  1288  	c.Assert(err, jc.ErrorIsNil)
  1289  	c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{
  1290  		Results: []params.BlockDeviceResult{
  1291  			{Result: storage.BlockDevice{
  1292  				DeviceName: "sda",
  1293  				Size:       123,
  1294  				HardwareId: "123",
  1295  			}},
  1296  			{Error: apiservertesting.ErrUnauthorized},
  1297  			{Error: apiservertesting.ErrUnauthorized},
  1298  			{Error: apiservertesting.ErrUnauthorized},
  1299  			{Error: apiservertesting.ErrUnauthorized},
  1300  			{Error: &params.Error{Message: `volume attachment host tag "application-mysql" not valid`}},
  1301  		},
  1302  	})
  1303  }
  1304  
  1305  func (s *iaasProvisionerSuite) TestVolumeBlockDevicesPlanBlockInfoSet(c *gc.C) {
  1306  	s.setupVolumes(c)
  1307  	s.Factory.MakeMachine(c, nil)
  1308  
  1309  	err := s.storageBackend.SetVolumeAttachmentInfo(
  1310  		names.NewMachineTag("0"),
  1311  		names.NewVolumeTag("0/0"),
  1312  		state.VolumeAttachmentInfo{},
  1313  	)
  1314  
  1315  	deviceAttrs := map[string]string{
  1316  		"iqn":         "bogusIQN",
  1317  		"address":     "192.168.1.1",
  1318  		"port":        "9999",
  1319  		"chap-user":   "example",
  1320  		"chap-secret": "supersecretpassword",
  1321  	}
  1322  
  1323  	attachmentPlanInfo := state.VolumeAttachmentPlanInfo{
  1324  		DeviceType:       storage.DeviceTypeISCSI,
  1325  		DeviceAttributes: deviceAttrs,
  1326  	}
  1327  
  1328  	err = s.storageBackend.CreateVolumeAttachmentPlan(
  1329  		names.NewMachineTag("0"), names.NewVolumeTag("0/0"), attachmentPlanInfo)
  1330  	c.Assert(err, jc.ErrorIsNil)
  1331  
  1332  	// The HardwareId set here should override the HardwareId in the volume info.
  1333  	blockInfo := state.BlockDeviceInfo{
  1334  		WWN: "testWWN",
  1335  		DeviceLinks: []string{
  1336  			"/dev/sda", "/dev/mapper/testDevice"},
  1337  		HardwareId: "test-id",
  1338  	}
  1339  	err = s.storageBackend.SetVolumeAttachmentPlanBlockInfo(
  1340  		names.NewMachineTag("0"), names.NewVolumeTag("0/0"), blockInfo)
  1341  	c.Assert(err, jc.ErrorIsNil)
  1342  
  1343  	machine0, err := s.State.Machine("0")
  1344  	c.Assert(err, jc.ErrorIsNil)
  1345  	err = machine0.SetMachineBlockDevices(state.BlockDeviceInfo{
  1346  		DeviceName: "sda",
  1347  		Size:       123,
  1348  		HardwareId: "test-id",
  1349  	})
  1350  	c.Assert(err, jc.ErrorIsNil)
  1351  
  1352  	args := params.MachineStorageIds{Ids: []params.MachineStorageId{
  1353  		{MachineTag: "machine-0", AttachmentTag: "volume-0-0"},
  1354  	}}
  1355  	results, err := s.api.VolumeBlockDevices(args)
  1356  	c.Assert(err, jc.ErrorIsNil)
  1357  	c.Assert(results, jc.DeepEquals, params.BlockDeviceResults{
  1358  		Results: []params.BlockDeviceResult{
  1359  			{Result: storage.BlockDevice{
  1360  				DeviceName: "sda",
  1361  				Size:       123,
  1362  				HardwareId: "test-id",
  1363  			}},
  1364  		},
  1365  	})
  1366  }
  1367  
  1368  func (s *iaasProvisionerSuite) TestLife(c *gc.C) {
  1369  	// Only IAAS models support block storage right now.
  1370  	s.setupVolumes(c)
  1371  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
  1372  	result, err := s.api.Life(args)
  1373  	c.Assert(err, jc.ErrorIsNil)
  1374  	c.Assert(result, gc.DeepEquals, params.LifeResults{
  1375  		Results: []params.LifeResult{
  1376  			{Life: params.Alive},
  1377  			{Life: params.Alive},
  1378  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
  1379  		},
  1380  	})
  1381  }
  1382  
  1383  func (s *iaasProvisionerSuite) TestAttachmentLife(c *gc.C) {
  1384  	// Only IAAS models support block storage right now.
  1385  	s.setupVolumes(c)
  1386  
  1387  	// TODO(axw) test filesystem attachment life
  1388  	// TODO(axw) test Dying
  1389  
  1390  	results, err := s.api.AttachmentLife(params.MachineStorageIds{
  1391  		Ids: []params.MachineStorageId{{
  1392  			MachineTag:    "machine-0",
  1393  			AttachmentTag: "volume-0-0",
  1394  		}, {
  1395  			MachineTag:    "machine-0",
  1396  			AttachmentTag: "volume-1",
  1397  		}, {
  1398  			MachineTag:    "machine-2",
  1399  			AttachmentTag: "volume-4",
  1400  		}, {
  1401  			MachineTag:    "machine-0",
  1402  			AttachmentTag: "volume-42",
  1403  		}},
  1404  	})
  1405  	c.Assert(err, jc.ErrorIsNil)
  1406  	c.Assert(results, jc.DeepEquals, params.LifeResults{
  1407  		Results: []params.LifeResult{
  1408  			{Life: params.Alive},
  1409  			{Life: params.Alive},
  1410  			{Life: params.Alive},
  1411  			{Error: &params.Error{Message: `volume "42" on "machine 0" not found`, Code: "not found"}},
  1412  		},
  1413  	})
  1414  }
  1415  
  1416  func (s *iaasProvisionerSuite) TestEnsureDead(c *gc.C) {
  1417  	// Only IAAS models support block storage right now.
  1418  	s.setupVolumes(c)
  1419  	args := params.Entities{Entities: []params.Entity{{"volume-0-0"}, {"volume-1"}, {"volume-42"}}}
  1420  	result, err := s.api.EnsureDead(args)
  1421  	c.Assert(err, jc.ErrorIsNil)
  1422  	// TODO(wallyworld) - this test will be updated when EnsureDead is supported
  1423  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1424  		Results: []params.ErrorResult{
  1425  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("0/0"), "ensuring death"))},
  1426  			{Error: common.ServerError(common.NotSupportedError(names.NewVolumeTag("1"), "ensuring death"))},
  1427  			{Error: common.ServerError(errors.NotFoundf(`volume "42"`))},
  1428  		},
  1429  	})
  1430  }
  1431  
  1432  func (s *iaasProvisionerSuite) TestRemoveVolumesController(c *gc.C) {
  1433  	// Only IAAS models support block storage right now.
  1434  	s.setupVolumes(c)
  1435  	args := params.Entities{Entities: []params.Entity{
  1436  		{"volume-1-0"}, {"volume-1"}, {"volume-2"}, {"volume-42"},
  1437  		{"volume-invalid"}, {"machine-0"},
  1438  	}}
  1439  
  1440  	err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1441  	c.Assert(err, jc.ErrorIsNil)
  1442  	err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1443  	c.Assert(err, jc.ErrorIsNil)
  1444  	err = s.storageBackend.DestroyVolume(names.NewVolumeTag("1"))
  1445  	c.Assert(err, jc.ErrorIsNil)
  1446  
  1447  	result, err := s.api.Remove(args)
  1448  	c.Assert(err, jc.ErrorIsNil)
  1449  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1450  		Results: []params.ErrorResult{
  1451  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1452  			{Error: nil},
  1453  			{Error: &params.Error{Message: "removing volume 2: volume is not dead"}},
  1454  			{Error: nil},
  1455  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1456  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1457  		},
  1458  	})
  1459  }
  1460  
  1461  func (s *iaasProvisionerSuite) TestRemoveFilesystemsController(c *gc.C) {
  1462  	s.setupFilesystems(c)
  1463  	args := params.Entities{Entities: []params.Entity{
  1464  		{"filesystem-1-0"}, {"filesystem-1"}, {"filesystem-2"}, {"filesystem-42"},
  1465  		{"filesystem-invalid"}, {"machine-0"},
  1466  	}}
  1467  
  1468  	err := s.storageBackend.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1469  	c.Assert(err, jc.ErrorIsNil)
  1470  	err = s.storageBackend.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1471  	c.Assert(err, jc.ErrorIsNil)
  1472  	err = s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("1"))
  1473  	c.Assert(err, jc.ErrorIsNil)
  1474  
  1475  	result, err := s.api.Remove(args)
  1476  	c.Assert(err, jc.ErrorIsNil)
  1477  	c.Assert(result, jc.DeepEquals, params.ErrorResults{
  1478  		Results: []params.ErrorResult{
  1479  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1480  			{Error: nil},
  1481  			{Error: &params.Error{Message: "removing filesystem 2: filesystem is not dead"}},
  1482  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1483  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1484  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1485  		},
  1486  	})
  1487  }
  1488  
  1489  func (s *iaasProvisionerSuite) TestRemoveVolumesMachineAgent(c *gc.C) {
  1490  	// Only IAAS models support block storage right now.
  1491  	s.setupVolumes(c)
  1492  	s.authorizer.Controller = false
  1493  	args := params.Entities{Entities: []params.Entity{
  1494  		{"volume-0-0"}, {"volume-0-42"}, {"volume-42"},
  1495  		{"volume-invalid"}, {"machine-0"},
  1496  	}}
  1497  
  1498  	err := s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"))
  1499  	c.Assert(err, jc.ErrorIsNil)
  1500  	err = s.storageBackend.RemoveVolumeAttachment(names.NewMachineTag("0"), names.NewVolumeTag("0/0"))
  1501  	c.Assert(err, jc.ErrorIsNil)
  1502  	err = s.storageBackend.DestroyVolume(names.NewVolumeTag("0/0"))
  1503  	c.Assert(err, jc.ErrorIsNil)
  1504  
  1505  	result, err := s.api.Remove(args)
  1506  	c.Assert(err, jc.ErrorIsNil)
  1507  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1508  		Results: []params.ErrorResult{
  1509  			{Error: nil},
  1510  			{Error: nil},
  1511  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1512  			{Error: &params.Error{Message: `"volume-invalid" is not a valid volume tag`}},
  1513  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1514  		},
  1515  	})
  1516  }
  1517  
  1518  func (s *iaasProvisionerSuite) TestRemoveFilesystemsMachineAgent(c *gc.C) {
  1519  	s.setupFilesystems(c)
  1520  	s.authorizer.Controller = false
  1521  	args := params.Entities{Entities: []params.Entity{
  1522  		{"filesystem-0-0"}, {"filesystem-0-42"}, {"filesystem-42"},
  1523  		{"filesystem-invalid"}, {"machine-0"},
  1524  	}}
  1525  
  1526  	err := s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0/0"))
  1527  	c.Assert(err, jc.ErrorIsNil)
  1528  	err = s.storageBackend.RemoveFilesystemAttachment(names.NewMachineTag("0"), names.NewFilesystemTag("0/0"))
  1529  	c.Assert(err, jc.ErrorIsNil)
  1530  
  1531  	result, err := s.api.Remove(args)
  1532  	c.Assert(err, jc.ErrorIsNil)
  1533  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1534  		Results: []params.ErrorResult{
  1535  			{Error: nil},
  1536  			{Error: nil},
  1537  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1538  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1539  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1540  		},
  1541  	})
  1542  }
  1543  
  1544  func (s *iaasProvisionerSuite) TestRemoveVolumeAttachments(c *gc.C) {
  1545  	// Only IAAS models support block storage right now.
  1546  	s.setupVolumes(c)
  1547  	s.authorizer.Controller = false
  1548  
  1549  	err := s.storageBackend.DetachVolume(names.NewMachineTag("0"), names.NewVolumeTag("1"))
  1550  	c.Assert(err, jc.ErrorIsNil)
  1551  
  1552  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1553  		Ids: []params.MachineStorageId{{
  1554  			MachineTag:    "machine-0",
  1555  			AttachmentTag: "volume-0-0",
  1556  		}, {
  1557  			MachineTag:    "machine-0",
  1558  			AttachmentTag: "volume-1",
  1559  		}, {
  1560  			MachineTag:    "machine-2",
  1561  			AttachmentTag: "volume-4",
  1562  		}, {
  1563  			MachineTag:    "machine-0",
  1564  			AttachmentTag: "volume-42",
  1565  		}},
  1566  	})
  1567  	c.Assert(err, jc.ErrorIsNil)
  1568  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1569  		Results: []params.ErrorResult{
  1570  			{Error: &params.Error{Message: "removing attachment of volume 0/0 from machine 0: volume attachment is not dying"}},
  1571  			{Error: nil},
  1572  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1573  			{Error: &params.Error{Message: `removing attachment of volume 42 from machine 0: volume "42" on "machine 0" not found`, Code: "not found"}},
  1574  		},
  1575  	})
  1576  }
  1577  
  1578  func (s *iaasProvisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) {
  1579  	s.setupFilesystems(c)
  1580  	s.authorizer.Controller = false
  1581  
  1582  	err := s.storageBackend.DetachFilesystem(names.NewMachineTag("0"), names.NewFilesystemTag("1"))
  1583  	c.Assert(err, jc.ErrorIsNil)
  1584  
  1585  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1586  		Ids: []params.MachineStorageId{{
  1587  			MachineTag:    "machine-0",
  1588  			AttachmentTag: "filesystem-0-0",
  1589  		}, {
  1590  			MachineTag:    "machine-0",
  1591  			AttachmentTag: "filesystem-1",
  1592  		}, {
  1593  			MachineTag:    "machine-2",
  1594  			AttachmentTag: "filesystem-4",
  1595  		}, {
  1596  			MachineTag:    "machine-0",
  1597  			AttachmentTag: "filesystem-42",
  1598  		}},
  1599  	})
  1600  	c.Assert(err, jc.ErrorIsNil)
  1601  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1602  		Results: []params.ErrorResult{
  1603  			{Error: &params.Error{Message: "removing attachment of filesystem 0/0 from machine 0: filesystem attachment is not dying"}},
  1604  			{Error: nil},
  1605  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1606  			{Error: &params.Error{Message: `removing attachment of filesystem 42 from machine 0: filesystem "42" on "machine 0" not found`, Code: "not found"}},
  1607  		},
  1608  	})
  1609  }
  1610  
  1611  type byMachineAndEntity []params.MachineStorageId
  1612  
  1613  func (b byMachineAndEntity) Len() int {
  1614  	return len(b)
  1615  }
  1616  
  1617  func (b byMachineAndEntity) Less(i, j int) bool {
  1618  	if b[i].MachineTag == b[j].MachineTag {
  1619  		return b[i].AttachmentTag < b[j].AttachmentTag
  1620  	}
  1621  	return b[i].MachineTag < b[j].MachineTag
  1622  }
  1623  
  1624  func (b byMachineAndEntity) Swap(i, j int) {
  1625  	b[i], b[j] = b[j], b[i]
  1626  }
  1627  
  1628  func (s *caasProvisionerSuite) TestWatchFilesystemAttachments(c *gc.C) {
  1629  	s.setupFilesystems(c)
  1630  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1631  
  1632  	args := params.Entities{Entities: []params.Entity{
  1633  		{"application-mariadb"},
  1634  		{s.Model.ModelTag().String()},
  1635  		{"environ-adb650da-b77b-4ee8-9cbb-d57a9a592847"},
  1636  		{"unit-mysql-0"}},
  1637  	}
  1638  	result, err := s.api.WatchFilesystemAttachments(args)
  1639  	c.Assert(err, jc.ErrorIsNil)
  1640  	sort.Sort(byMachineAndEntity(result.Results[0].Changes))
  1641  	sort.Sort(byMachineAndEntity(result.Results[1].Changes))
  1642  	c.Assert(result, jc.DeepEquals, params.MachineStorageIdsWatchResults{
  1643  		Results: []params.MachineStorageIdsWatchResult{
  1644  			{
  1645  				MachineStorageIdsWatcherId: "1",
  1646  				Changes: []params.MachineStorageId{{
  1647  					MachineTag:    "unit-mariadb-0",
  1648  					AttachmentTag: "filesystem-0",
  1649  				}, {
  1650  					MachineTag:    "unit-mariadb-0",
  1651  					AttachmentTag: "filesystem-1",
  1652  				}, {
  1653  					MachineTag:    "unit-mariadb-0",
  1654  					AttachmentTag: "filesystem-2",
  1655  				}},
  1656  			}, {
  1657  				MachineStorageIdsWatcherId: "2",
  1658  				Changes:                    []params.MachineStorageId{},
  1659  			},
  1660  			{Error: apiservertesting.ErrUnauthorized},
  1661  			{Error: apiservertesting.ErrUnauthorized},
  1662  		},
  1663  	})
  1664  
  1665  	// Verify the resources were registered and stop them when done.
  1666  	c.Assert(s.resources.Count(), gc.Equals, 2)
  1667  	v0Watcher := s.resources.Get("1")
  1668  	defer statetesting.AssertStop(c, v0Watcher)
  1669  	v1Watcher := s.resources.Get("2")
  1670  	defer statetesting.AssertStop(c, v1Watcher)
  1671  
  1672  	// Check that the Watch has consumed the initial events ("returned" in
  1673  	// the Watch call)
  1674  	wc := statetesting.NewStringsWatcherC(c, s.State, v0Watcher.(state.StringsWatcher))
  1675  	wc.AssertNoChange()
  1676  	wc = statetesting.NewStringsWatcherC(c, s.State, v1Watcher.(state.StringsWatcher))
  1677  	wc.AssertNoChange()
  1678  }
  1679  
  1680  func (s *caasProvisionerSuite) TestRemoveFilesystemAttachments(c *gc.C) {
  1681  	s.setupFilesystems(c)
  1682  
  1683  	err := s.storageBackend.DetachFilesystem(names.NewUnitTag("mariadb/0"), names.NewFilesystemTag("1"))
  1684  	c.Assert(err, jc.ErrorIsNil)
  1685  
  1686  	results, err := s.api.RemoveAttachment(params.MachineStorageIds{
  1687  		Ids: []params.MachineStorageId{{
  1688  			MachineTag:    "unit-mariadb-0",
  1689  			AttachmentTag: "filesystem-0",
  1690  		}, {
  1691  			MachineTag:    "unit-mariadb-0",
  1692  			AttachmentTag: "filesystem-1",
  1693  		}, {
  1694  			MachineTag:    "unit-mysql-2",
  1695  			AttachmentTag: "filesystem-4",
  1696  		}, {
  1697  			MachineTag:    "unit-mariadb-0",
  1698  			AttachmentTag: "filesystem-42",
  1699  		}},
  1700  	})
  1701  	c.Assert(err, jc.ErrorIsNil)
  1702  	c.Assert(results, jc.DeepEquals, params.ErrorResults{
  1703  		Results: []params.ErrorResult{
  1704  			{Error: &params.Error{Message: "removing attachment of filesystem 0 from unit mariadb/0: filesystem attachment is not dying"}},
  1705  			{Error: nil},
  1706  			{Error: &params.Error{Message: `removing attachment of filesystem 4 from unit mysql/2: filesystem "4" on "unit mysql/2" not found`, Code: "not found"}},
  1707  			{Error: &params.Error{Message: `removing attachment of filesystem 42 from unit mariadb/0: filesystem "42" on "unit mariadb/0" not found`, Code: "not found"}},
  1708  		},
  1709  	})
  1710  }
  1711  
  1712  func (s *caasProvisionerSuite) TestRemoveFilesystemsApplicationAgent(c *gc.C) {
  1713  	s.setupFilesystems(c)
  1714  	s.authorizer.Controller = false
  1715  	args := params.Entities{Entities: []params.Entity{
  1716  		{"filesystem-42"},
  1717  		{"filesystem-invalid"}, {"machine-0"},
  1718  	}}
  1719  
  1720  	err := s.storageBackend.DestroyFilesystem(names.NewFilesystemTag("0"))
  1721  	c.Assert(err, gc.ErrorMatches, "destroying filesystem 0: filesystem is assigned to storage cache/0")
  1722  	err = s.storageBackend.RemoveFilesystemAttachment(names.NewUnitTag("mariadb/0"), names.NewFilesystemTag("0"))
  1723  	c.Assert(err, gc.ErrorMatches, "removing attachment of filesystem 0 from unit mariadb/0: filesystem attachment is not dying")
  1724  
  1725  	result, err := s.api.Remove(args)
  1726  	c.Assert(err, jc.ErrorIsNil)
  1727  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1728  		Results: []params.ErrorResult{
  1729  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1730  			{Error: &params.Error{Message: `"filesystem-invalid" is not a valid filesystem tag`}},
  1731  			{Error: &params.Error{Message: "permission denied", Code: "unauthorized access"}},
  1732  		},
  1733  	})
  1734  }
  1735  
  1736  func (s *caasProvisionerSuite) TestFilesystemLife(c *gc.C) {
  1737  	s.setupFilesystems(c)
  1738  	args := params.Entities{Entities: []params.Entity{{"filesystem-0"}, {"filesystem-1"}, {"filesystem-42"}}}
  1739  	result, err := s.api.Life(args)
  1740  	c.Assert(err, jc.ErrorIsNil)
  1741  	c.Assert(result, gc.DeepEquals, params.LifeResults{
  1742  		Results: []params.LifeResult{
  1743  			{Life: params.Alive},
  1744  			{Life: params.Alive},
  1745  			{Error: apiservertesting.ErrUnauthorized},
  1746  		},
  1747  	})
  1748  }
  1749  
  1750  func (s caasProvisionerSuite) TestFilesystemAttachmentLife(c *gc.C) {
  1751  	s.setupFilesystems(c)
  1752  
  1753  	results, err := s.api.AttachmentLife(params.MachineStorageIds{
  1754  		Ids: []params.MachineStorageId{{
  1755  			MachineTag:    "unit-mariadb-0",
  1756  			AttachmentTag: "filesystem-0",
  1757  		}, {
  1758  			MachineTag:    "unit-mariadb-0",
  1759  			AttachmentTag: "filesystem-1",
  1760  		}, {
  1761  			MachineTag:    "unit-mariadb-0",
  1762  			AttachmentTag: "filesystem-42",
  1763  		}},
  1764  	})
  1765  	c.Assert(err, jc.ErrorIsNil)
  1766  	c.Assert(results, jc.DeepEquals, params.LifeResults{
  1767  		Results: []params.LifeResult{
  1768  			{Life: params.Alive},
  1769  			{Life: params.Alive},
  1770  			{Error: &params.Error{Message: `filesystem "42" on "unit mariadb/0" not found`, Code: "not found"}},
  1771  		},
  1772  	})
  1773  }