github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/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  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/clock"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/environs/config"
    17  	"github.com/juju/juju/instance"
    18  	"github.com/juju/juju/storage"
    19  	"github.com/juju/juju/storage/provider/registry"
    20  	coretesting "github.com/juju/juju/testing"
    21  	"github.com/juju/juju/watcher"
    22  	"github.com/juju/juju/worker"
    23  	"github.com/juju/juju/worker/storageprovisioner"
    24  )
    25  
    26  type storageProvisionerSuite struct {
    27  	coretesting.BaseSuite
    28  	provider                *dummyProvider
    29  	managedFilesystemSource *mockManagedFilesystemSource
    30  }
    31  
    32  var _ = gc.Suite(&storageProvisionerSuite{})
    33  
    34  func (s *storageProvisionerSuite) SetUpTest(c *gc.C) {
    35  	s.BaseSuite.SetUpTest(c)
    36  	s.provider = &dummyProvider{dynamic: true}
    37  	registry.RegisterProvider("dummy", s.provider)
    38  	s.AddCleanup(func(*gc.C) {
    39  		registry.RegisterProvider("dummy", nil)
    40  	})
    41  
    42  	s.managedFilesystemSource = nil
    43  	s.PatchValue(
    44  		storageprovisioner.NewManagedFilesystemSource,
    45  		func(
    46  			blockDevices map[names.VolumeTag]storage.BlockDevice,
    47  			filesystems map[names.FilesystemTag]storage.Filesystem,
    48  		) storage.FilesystemSource {
    49  			s.managedFilesystemSource = &mockManagedFilesystemSource{
    50  				blockDevices: blockDevices,
    51  				filesystems:  filesystems,
    52  			}
    53  			return s.managedFilesystemSource
    54  		},
    55  	)
    56  }
    57  
    58  func (s *storageProvisionerSuite) TestStartStop(c *gc.C) {
    59  	worker, err := storageprovisioner.NewStorageProvisioner(storageprovisioner.Config{
    60  		Scope:       coretesting.ModelTag,
    61  		Volumes:     newMockVolumeAccessor(),
    62  		Filesystems: newMockFilesystemAccessor(),
    63  		Life:        &mockLifecycleManager{},
    64  		Environ:     newMockModelAccessor(c),
    65  		Machines:    newMockMachineAccessor(c),
    66  		Status:      &mockStatusSetter{},
    67  		Clock:       &mockClock{},
    68  	})
    69  	c.Assert(err, jc.ErrorIsNil)
    70  
    71  	worker.Kill()
    72  	c.Assert(worker.Wait(), gc.IsNil)
    73  }
    74  
    75  func (s *storageProvisionerSuite) TestInvalidConfig(c *gc.C) {
    76  	_, err := storageprovisioner.NewStorageProvisioner(almostValidConfig())
    77  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    78  }
    79  
    80  func (s *storageProvisionerSuite) TestVolumeAdded(c *gc.C) {
    81  	expectedVolumes := []params.Volume{{
    82  		VolumeTag: "volume-1",
    83  		Info: params.VolumeInfo{
    84  			VolumeId:   "id-1",
    85  			HardwareId: "serial-1",
    86  			Size:       1024,
    87  			Persistent: true,
    88  		},
    89  	}, {
    90  		VolumeTag: "volume-2",
    91  		Info: params.VolumeInfo{
    92  			VolumeId:   "id-2",
    93  			HardwareId: "serial-2",
    94  			Size:       1024,
    95  		},
    96  	}}
    97  	expectedVolumeAttachments := []params.VolumeAttachment{{
    98  		VolumeTag:  "volume-1",
    99  		MachineTag: "machine-1",
   100  		Info: params.VolumeAttachmentInfo{
   101  			DeviceName: "/dev/sda1",
   102  			ReadOnly:   true,
   103  		},
   104  	}, {
   105  		VolumeTag:  "volume-2",
   106  		MachineTag: "machine-1",
   107  		Info: params.VolumeAttachmentInfo{
   108  			DeviceName: "/dev/sda2",
   109  		},
   110  	}}
   111  
   112  	volumeInfoSet := make(chan interface{})
   113  	volumeAccessor := newMockVolumeAccessor()
   114  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   115  	volumeAccessor.setVolumeInfo = func(volumes []params.Volume) ([]params.ErrorResult, error) {
   116  		defer close(volumeInfoSet)
   117  		c.Assert(volumes, jc.SameContents, expectedVolumes)
   118  		return nil, nil
   119  	}
   120  
   121  	volumeAttachmentInfoSet := make(chan interface{})
   122  	volumeAccessor.setVolumeAttachmentInfo = func(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) {
   123  		defer close(volumeAttachmentInfoSet)
   124  		c.Assert(volumeAttachments, jc.SameContents, expectedVolumeAttachments)
   125  		return nil, nil
   126  	}
   127  
   128  	args := &workerArgs{volumes: volumeAccessor}
   129  	worker := newStorageProvisioner(c, args)
   130  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   131  	defer worker.Kill()
   132  
   133  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   134  		MachineTag: "machine-1", AttachmentTag: "volume-1",
   135  	}, {
   136  		MachineTag: "machine-1", AttachmentTag: "volume-2",
   137  	}}
   138  	assertNoEvent(c, volumeAttachmentInfoSet, "volume attachment set")
   139  
   140  	// The worker should create volumes according to ids "1" and "2".
   141  	volumeAccessor.volumesWatcher.changes <- []string{"1", "2"}
   142  	// ... but not until the environment config is available.
   143  	assertNoEvent(c, volumeInfoSet, "volume info set")
   144  	assertNoEvent(c, volumeAttachmentInfoSet, "volume attachment info set")
   145  	args.environ.watcher.changes <- struct{}{}
   146  	waitChannel(c, volumeInfoSet, "waiting for volume info to be set")
   147  	waitChannel(c, volumeAttachmentInfoSet, "waiting for volume attachments to be set")
   148  }
   149  
   150  func (s *storageProvisionerSuite) TestCreateVolumeCreatesAttachment(c *gc.C) {
   151  	volumeAccessor := newMockVolumeAccessor()
   152  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   153  
   154  	volumeAttachmentInfoSet := make(chan interface{})
   155  	volumeAccessor.setVolumeAttachmentInfo = func(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) {
   156  		defer close(volumeAttachmentInfoSet)
   157  		return make([]params.ErrorResult, len(volumeAttachments)), nil
   158  	}
   159  
   160  	s.provider.createVolumesFunc = func(args []storage.VolumeParams) ([]storage.CreateVolumesResult, error) {
   161  		volumeAccessor.provisionedAttachments[params.MachineStorageId{
   162  			MachineTag:    args[0].Attachment.Machine.String(),
   163  			AttachmentTag: args[0].Attachment.Volume.String(),
   164  		}] = params.VolumeAttachment{
   165  			VolumeTag:  args[0].Attachment.Volume.String(),
   166  			MachineTag: args[0].Attachment.Machine.String(),
   167  		}
   168  		return []storage.CreateVolumesResult{{
   169  			Volume: &storage.Volume{
   170  				Tag: args[0].Tag,
   171  				VolumeInfo: storage.VolumeInfo{
   172  					VolumeId: "vol-ume",
   173  				},
   174  			},
   175  			VolumeAttachment: &storage.VolumeAttachment{
   176  				Volume:  args[0].Attachment.Volume,
   177  				Machine: args[0].Attachment.Machine,
   178  			},
   179  		}}, nil
   180  	}
   181  
   182  	attachVolumesCalled := make(chan interface{})
   183  	s.provider.attachVolumesFunc = func(args []storage.VolumeAttachmentParams) ([]storage.AttachVolumesResult, error) {
   184  		defer close(attachVolumesCalled)
   185  		return nil, errors.New("should not be called")
   186  	}
   187  
   188  	args := &workerArgs{volumes: volumeAccessor}
   189  	worker := newStorageProvisioner(c, args)
   190  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   191  	defer worker.Kill()
   192  
   193  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   194  		MachineTag: "machine-1", AttachmentTag: "volume-1",
   195  	}}
   196  	assertNoEvent(c, volumeAttachmentInfoSet, "volume attachment set")
   197  
   198  	// The worker should create volumes according to ids "1".
   199  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
   200  	args.environ.watcher.changes <- struct{}{}
   201  	waitChannel(c, volumeAttachmentInfoSet, "waiting for volume attachments to be set")
   202  	assertNoEvent(c, attachVolumesCalled, "AttachVolumes called")
   203  }
   204  
   205  func (s *storageProvisionerSuite) TestCreateVolumeRetry(c *gc.C) {
   206  	volumeInfoSet := make(chan interface{})
   207  	volumeAccessor := newMockVolumeAccessor()
   208  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   209  	volumeAccessor.setVolumeInfo = func(volumes []params.Volume) ([]params.ErrorResult, error) {
   210  		defer close(volumeInfoSet)
   211  		return make([]params.ErrorResult, len(volumes)), nil
   212  	}
   213  
   214  	// mockFunc's After will progress the current time by the specified
   215  	// duration and signal the channel immediately.
   216  	clock := &mockClock{}
   217  	var createVolumeTimes []time.Time
   218  
   219  	s.provider.createVolumesFunc = func(args []storage.VolumeParams) ([]storage.CreateVolumesResult, error) {
   220  		createVolumeTimes = append(createVolumeTimes, clock.Now())
   221  		if len(createVolumeTimes) < 10 {
   222  			return []storage.CreateVolumesResult{{Error: errors.New("badness")}}, nil
   223  		}
   224  		return []storage.CreateVolumesResult{{
   225  			Volume: &storage.Volume{Tag: args[0].Tag},
   226  		}}, nil
   227  	}
   228  
   229  	args := &workerArgs{volumes: volumeAccessor, clock: clock}
   230  	worker := newStorageProvisioner(c, args)
   231  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   232  	defer worker.Kill()
   233  
   234  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   235  		MachineTag: "machine-1", AttachmentTag: "volume-1",
   236  	}}
   237  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
   238  	args.environ.watcher.changes <- struct{}{}
   239  	waitChannel(c, volumeInfoSet, "waiting for volume info to be set")
   240  	c.Assert(createVolumeTimes, gc.HasLen, 10)
   241  
   242  	// The first attempt should have been immediate: T0.
   243  	c.Assert(createVolumeTimes[0], gc.Equals, time.Time{})
   244  
   245  	delays := make([]time.Duration, len(createVolumeTimes)-1)
   246  	for i := range createVolumeTimes[1:] {
   247  		delays[i] = createVolumeTimes[i+1].Sub(createVolumeTimes[i])
   248  	}
   249  	c.Assert(delays, jc.DeepEquals, []time.Duration{
   250  		30 * time.Second,
   251  		1 * time.Minute,
   252  		2 * time.Minute,
   253  		4 * time.Minute,
   254  		8 * time.Minute,
   255  		16 * time.Minute,
   256  		30 * time.Minute, // ceiling reached
   257  		30 * time.Minute,
   258  		30 * time.Minute,
   259  	})
   260  
   261  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
   262  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   263  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   264  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   265  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   266  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   267  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   268  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   269  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   270  		{Tag: "volume-1", Status: "pending", Info: "badness"},
   271  		{Tag: "volume-1", Status: "attaching", Info: ""},
   272  	})
   273  }
   274  
   275  func (s *storageProvisionerSuite) TestCreateFilesystemRetry(c *gc.C) {
   276  	filesystemInfoSet := make(chan interface{})
   277  	filesystemAccessor := newMockFilesystemAccessor()
   278  	filesystemAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   279  	filesystemAccessor.setFilesystemInfo = func(filesystems []params.Filesystem) ([]params.ErrorResult, error) {
   280  		defer close(filesystemInfoSet)
   281  		return make([]params.ErrorResult, len(filesystems)), nil
   282  	}
   283  
   284  	// mockFunc's After will progress the current time by the specified
   285  	// duration and signal the channel immediately.
   286  	clock := &mockClock{}
   287  	var createFilesystemTimes []time.Time
   288  
   289  	s.provider.createFilesystemsFunc = func(args []storage.FilesystemParams) ([]storage.CreateFilesystemsResult, error) {
   290  		createFilesystemTimes = append(createFilesystemTimes, clock.Now())
   291  		if len(createFilesystemTimes) < 10 {
   292  			return []storage.CreateFilesystemsResult{{Error: errors.New("badness")}}, nil
   293  		}
   294  		return []storage.CreateFilesystemsResult{{
   295  			Filesystem: &storage.Filesystem{Tag: args[0].Tag},
   296  		}}, nil
   297  	}
   298  
   299  	args := &workerArgs{filesystems: filesystemAccessor, clock: clock}
   300  	worker := newStorageProvisioner(c, args)
   301  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   302  	defer worker.Kill()
   303  
   304  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   305  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
   306  	}}
   307  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1"}
   308  	args.environ.watcher.changes <- struct{}{}
   309  	waitChannel(c, filesystemInfoSet, "waiting for filesystem info to be set")
   310  	c.Assert(createFilesystemTimes, gc.HasLen, 10)
   311  
   312  	// The first attempt should have been immediate: T0.
   313  	c.Assert(createFilesystemTimes[0], gc.Equals, time.Time{})
   314  
   315  	delays := make([]time.Duration, len(createFilesystemTimes)-1)
   316  	for i := range createFilesystemTimes[1:] {
   317  		delays[i] = createFilesystemTimes[i+1].Sub(createFilesystemTimes[i])
   318  	}
   319  	c.Assert(delays, jc.DeepEquals, []time.Duration{
   320  		30 * time.Second,
   321  		1 * time.Minute,
   322  		2 * time.Minute,
   323  		4 * time.Minute,
   324  		8 * time.Minute,
   325  		16 * time.Minute,
   326  		30 * time.Minute, // ceiling reached
   327  		30 * time.Minute,
   328  		30 * time.Minute,
   329  	})
   330  
   331  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
   332  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   333  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   334  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   335  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   336  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   337  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   338  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   339  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   340  		{Tag: "filesystem-1", Status: "pending", Info: "badness"},
   341  		{Tag: "filesystem-1", Status: "attaching", Info: ""},
   342  	})
   343  }
   344  
   345  func (s *storageProvisionerSuite) TestAttachVolumeRetry(c *gc.C) {
   346  	volumeInfoSet := make(chan interface{})
   347  	volumeAccessor := newMockVolumeAccessor()
   348  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   349  	volumeAccessor.setVolumeInfo = func(volumes []params.Volume) ([]params.ErrorResult, error) {
   350  		defer close(volumeInfoSet)
   351  		return make([]params.ErrorResult, len(volumes)), nil
   352  	}
   353  	volumeAttachmentInfoSet := make(chan interface{})
   354  	volumeAccessor.setVolumeAttachmentInfo = func(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) {
   355  		defer close(volumeAttachmentInfoSet)
   356  		return make([]params.ErrorResult, len(volumeAttachments)), nil
   357  	}
   358  
   359  	// mockFunc's After will progress the current time by the specified
   360  	// duration and signal the channel immediately.
   361  	clock := &mockClock{}
   362  	var attachVolumeTimes []time.Time
   363  
   364  	s.provider.attachVolumesFunc = func(args []storage.VolumeAttachmentParams) ([]storage.AttachVolumesResult, error) {
   365  		attachVolumeTimes = append(attachVolumeTimes, clock.Now())
   366  		if len(attachVolumeTimes) < 10 {
   367  			return []storage.AttachVolumesResult{{Error: errors.New("badness")}}, nil
   368  		}
   369  		return []storage.AttachVolumesResult{{
   370  			VolumeAttachment: &storage.VolumeAttachment{
   371  				args[0].Volume,
   372  				args[0].Machine,
   373  				storage.VolumeAttachmentInfo{
   374  					DeviceName: "/dev/sda1",
   375  				},
   376  			},
   377  		}}, nil
   378  	}
   379  
   380  	args := &workerArgs{volumes: volumeAccessor, clock: clock}
   381  	worker := newStorageProvisioner(c, args)
   382  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   383  	defer worker.Kill()
   384  
   385  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   386  		MachineTag: "machine-1", AttachmentTag: "volume-1",
   387  	}}
   388  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
   389  	args.environ.watcher.changes <- struct{}{}
   390  	waitChannel(c, volumeInfoSet, "waiting for volume info to be set")
   391  	waitChannel(c, volumeAttachmentInfoSet, "waiting for volume attachments to be set")
   392  	c.Assert(attachVolumeTimes, gc.HasLen, 10)
   393  
   394  	// The first attempt should have been immediate: T0.
   395  	c.Assert(attachVolumeTimes[0], gc.Equals, time.Time{})
   396  
   397  	delays := make([]time.Duration, len(attachVolumeTimes)-1)
   398  	for i := range attachVolumeTimes[1:] {
   399  		delays[i] = attachVolumeTimes[i+1].Sub(attachVolumeTimes[i])
   400  	}
   401  	c.Assert(delays, jc.DeepEquals, []time.Duration{
   402  		30 * time.Second,
   403  		1 * time.Minute,
   404  		2 * time.Minute,
   405  		4 * time.Minute,
   406  		8 * time.Minute,
   407  		16 * time.Minute,
   408  		30 * time.Minute, // ceiling reached
   409  		30 * time.Minute,
   410  		30 * time.Minute,
   411  	})
   412  
   413  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
   414  		{Tag: "volume-1", Status: "attaching", Info: ""},        // CreateVolumes
   415  		{Tag: "volume-1", Status: "attaching", Info: "badness"}, // AttachVolumes
   416  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   417  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   418  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   419  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   420  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   421  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   422  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   423  		{Tag: "volume-1", Status: "attaching", Info: "badness"},
   424  		{Tag: "volume-1", Status: "attached", Info: ""},
   425  	})
   426  }
   427  
   428  func (s *storageProvisionerSuite) TestAttachFilesystemRetry(c *gc.C) {
   429  	filesystemInfoSet := make(chan interface{})
   430  	filesystemAccessor := newMockFilesystemAccessor()
   431  	filesystemAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   432  	filesystemAccessor.setFilesystemInfo = func(filesystems []params.Filesystem) ([]params.ErrorResult, error) {
   433  		defer close(filesystemInfoSet)
   434  		return make([]params.ErrorResult, len(filesystems)), nil
   435  	}
   436  	filesystemAttachmentInfoSet := make(chan interface{})
   437  	filesystemAccessor.setFilesystemAttachmentInfo = func(filesystemAttachments []params.FilesystemAttachment) ([]params.ErrorResult, error) {
   438  		defer close(filesystemAttachmentInfoSet)
   439  		return make([]params.ErrorResult, len(filesystemAttachments)), nil
   440  	}
   441  
   442  	// mockFunc's After will progress the current time by the specified
   443  	// duration and signal the channel immediately.
   444  	clock := &mockClock{}
   445  	var attachFilesystemTimes []time.Time
   446  
   447  	s.provider.attachFilesystemsFunc = func(args []storage.FilesystemAttachmentParams) ([]storage.AttachFilesystemsResult, error) {
   448  		attachFilesystemTimes = append(attachFilesystemTimes, clock.Now())
   449  		if len(attachFilesystemTimes) < 10 {
   450  			return []storage.AttachFilesystemsResult{{Error: errors.New("badness")}}, nil
   451  		}
   452  		return []storage.AttachFilesystemsResult{{
   453  			FilesystemAttachment: &storage.FilesystemAttachment{
   454  				args[0].Filesystem,
   455  				args[0].Machine,
   456  				storage.FilesystemAttachmentInfo{
   457  					Path: "/oh/over/there",
   458  				},
   459  			},
   460  		}}, nil
   461  	}
   462  
   463  	args := &workerArgs{filesystems: filesystemAccessor, clock: clock}
   464  	worker := newStorageProvisioner(c, args)
   465  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   466  	defer worker.Kill()
   467  
   468  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   469  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
   470  	}}
   471  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1"}
   472  	args.environ.watcher.changes <- struct{}{}
   473  	waitChannel(c, filesystemInfoSet, "waiting for filesystem info to be set")
   474  	waitChannel(c, filesystemAttachmentInfoSet, "waiting for filesystem attachments to be set")
   475  	c.Assert(attachFilesystemTimes, gc.HasLen, 10)
   476  
   477  	// The first attempt should have been immediate: T0.
   478  	c.Assert(attachFilesystemTimes[0], gc.Equals, time.Time{})
   479  
   480  	delays := make([]time.Duration, len(attachFilesystemTimes)-1)
   481  	for i := range attachFilesystemTimes[1:] {
   482  		delays[i] = attachFilesystemTimes[i+1].Sub(attachFilesystemTimes[i])
   483  	}
   484  	c.Assert(delays, jc.DeepEquals, []time.Duration{
   485  		30 * time.Second,
   486  		1 * time.Minute,
   487  		2 * time.Minute,
   488  		4 * time.Minute,
   489  		8 * time.Minute,
   490  		16 * time.Minute,
   491  		30 * time.Minute, // ceiling reached
   492  		30 * time.Minute,
   493  		30 * time.Minute,
   494  	})
   495  
   496  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
   497  		{Tag: "filesystem-1", Status: "attaching", Info: ""},        // CreateFilesystems
   498  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"}, // AttachFilesystems
   499  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   500  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   501  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   502  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   503  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   504  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   505  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   506  		{Tag: "filesystem-1", Status: "attaching", Info: "badness"},
   507  		{Tag: "filesystem-1", Status: "attached", Info: ""},
   508  	})
   509  }
   510  
   511  func (s *storageProvisionerSuite) TestValidateVolumeParams(c *gc.C) {
   512  	volumeAccessor := newMockVolumeAccessor()
   513  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   514  	volumeAccessor.provisionedVolumes["volume-3"] = params.Volume{VolumeTag: "volume-3"}
   515  	volumeAccessor.provisionedVolumes["volume-4"] = params.Volume{
   516  		VolumeTag: "volume-4",
   517  		Info:      params.VolumeInfo{VolumeId: "vol-ume"},
   518  	}
   519  
   520  	var validateCalls int
   521  	validated := make(chan interface{}, 1)
   522  	s.provider.validateVolumeParamsFunc = func(p storage.VolumeParams) error {
   523  		validateCalls++
   524  		validated <- p
   525  		switch p.Tag.String() {
   526  		case "volume-1", "volume-3":
   527  			return errors.New("something is wrong")
   528  		}
   529  		return nil
   530  	}
   531  
   532  	life := func(tags []names.Tag) ([]params.LifeResult, error) {
   533  		results := make([]params.LifeResult, len(tags))
   534  		for i := range results {
   535  			switch tags[i].String() {
   536  			case "volume-3", "volume-4":
   537  				results[i].Life = params.Dead
   538  			default:
   539  				results[i].Life = params.Alive
   540  			}
   541  		}
   542  		return results, nil
   543  	}
   544  
   545  	createdVolumes := make(chan interface{}, 1)
   546  	s.provider.createVolumesFunc = func(args []storage.VolumeParams) ([]storage.CreateVolumesResult, error) {
   547  		createdVolumes <- args
   548  		if len(args) != 1 {
   549  			return nil, errors.New("expected one argument")
   550  		}
   551  		return []storage.CreateVolumesResult{{
   552  			Volume: &storage.Volume{Tag: args[0].Tag},
   553  		}}, nil
   554  	}
   555  
   556  	destroyedVolumes := make(chan interface{}, 1)
   557  	s.provider.destroyVolumesFunc = func(volumeIds []string) ([]error, error) {
   558  		destroyedVolumes <- volumeIds
   559  		return make([]error, len(volumeIds)), nil
   560  	}
   561  
   562  	args := &workerArgs{
   563  		volumes: volumeAccessor,
   564  		life: &mockLifecycleManager{
   565  			life: life,
   566  		},
   567  	}
   568  	worker := newStorageProvisioner(c, args)
   569  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   570  	defer worker.Kill()
   571  
   572  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   573  		MachineTag: "machine-1", AttachmentTag: "volume-1",
   574  	}, {
   575  		MachineTag: "machine-1", AttachmentTag: "volume-2",
   576  	}}
   577  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
   578  	args.environ.watcher.changes <- struct{}{}
   579  	waitChannel(c, validated, "waiting for volume parameter validation")
   580  	assertNoEvent(c, createdVolumes, "volume created")
   581  	c.Assert(validateCalls, gc.Equals, 1)
   582  
   583  	// Failure to create volume-1 should not block creation volume-2.
   584  	volumeAccessor.volumesWatcher.changes <- []string{"2"}
   585  	waitChannel(c, validated, "waiting for volume parameter validation")
   586  	createVolumeParams := waitChannel(c, createdVolumes, "volume created").([]storage.VolumeParams)
   587  	c.Assert(createVolumeParams, gc.HasLen, 1)
   588  	c.Assert(createVolumeParams[0].Tag.String(), gc.Equals, "volume-2")
   589  	c.Assert(validateCalls, gc.Equals, 2)
   590  
   591  	volumeAccessor.volumesWatcher.changes <- []string{"3"}
   592  	waitChannel(c, validated, "waiting for volume parameter validation")
   593  	assertNoEvent(c, destroyedVolumes, "volume destroyed")
   594  	c.Assert(validateCalls, gc.Equals, 3)
   595  
   596  	// Failure to destroy volume-3 should not block creation of volume-4.
   597  	volumeAccessor.volumesWatcher.changes <- []string{"4"}
   598  	waitChannel(c, validated, "waiting for volume parameter validation")
   599  	destroyVolumeParams := waitChannel(c, destroyedVolumes, "volume destroyed").([]string)
   600  	c.Assert(destroyVolumeParams, jc.DeepEquals, []string{"vol-ume"})
   601  	c.Assert(validateCalls, gc.Equals, 4)
   602  
   603  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
   604  		{Tag: "volume-1", Status: "error", Info: "something is wrong"},
   605  		{Tag: "volume-2", Status: "attaching"},
   606  		{Tag: "volume-3", Status: "error", Info: "something is wrong"},
   607  		// destroyed volumes are removed immediately,
   608  		// so there is no status update.
   609  	})
   610  }
   611  
   612  func (s *storageProvisionerSuite) TestValidateFilesystemParams(c *gc.C) {
   613  	filesystemAccessor := newMockFilesystemAccessor()
   614  	filesystemAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   615  	filesystemAccessor.provisionedFilesystems["filesystem-3"] = params.Filesystem{FilesystemTag: "filesystem-3"}
   616  	filesystemAccessor.provisionedFilesystems["filesystem-4"] = params.Filesystem{
   617  		FilesystemTag: "filesystem-4",
   618  		Info:          params.FilesystemInfo{FilesystemId: "fs-id"},
   619  	}
   620  
   621  	var validateCalls int
   622  	validated := make(chan interface{}, 1)
   623  	s.provider.validateFilesystemParamsFunc = func(p storage.FilesystemParams) error {
   624  		validateCalls++
   625  		validated <- p
   626  		switch p.Tag.String() {
   627  		case "filesystem-1", "filesystem-3":
   628  			return errors.New("something is wrong")
   629  		}
   630  		return nil
   631  	}
   632  
   633  	life := func(tags []names.Tag) ([]params.LifeResult, error) {
   634  		results := make([]params.LifeResult, len(tags))
   635  		for i := range results {
   636  			switch tags[i].String() {
   637  			case "filesystem-3", "filesystem-4":
   638  				results[i].Life = params.Dead
   639  			default:
   640  				results[i].Life = params.Alive
   641  			}
   642  		}
   643  		return results, nil
   644  	}
   645  
   646  	createdFilesystems := make(chan interface{}, 1)
   647  	s.provider.createFilesystemsFunc = func(args []storage.FilesystemParams) ([]storage.CreateFilesystemsResult, error) {
   648  		createdFilesystems <- args
   649  		if len(args) != 1 {
   650  			return nil, errors.New("expected one argument")
   651  		}
   652  		return []storage.CreateFilesystemsResult{{
   653  			Filesystem: &storage.Filesystem{Tag: args[0].Tag},
   654  		}}, nil
   655  	}
   656  
   657  	destroyedFilesystems := make(chan interface{}, 1)
   658  	s.provider.destroyFilesystemsFunc = func(filesystemIds []string) ([]error, error) {
   659  		destroyedFilesystems <- filesystemIds
   660  		return make([]error, len(filesystemIds)), nil
   661  	}
   662  
   663  	args := &workerArgs{
   664  		filesystems: filesystemAccessor,
   665  		life: &mockLifecycleManager{
   666  			life: life,
   667  		},
   668  	}
   669  	worker := newStorageProvisioner(c, args)
   670  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   671  	defer worker.Kill()
   672  
   673  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   674  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
   675  	}, {
   676  		MachineTag: "machine-1", AttachmentTag: "filesystem-2",
   677  	}}
   678  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1"}
   679  	args.environ.watcher.changes <- struct{}{}
   680  	waitChannel(c, validated, "waiting for filesystem parameter validation")
   681  	assertNoEvent(c, createdFilesystems, "filesystem created")
   682  	c.Assert(validateCalls, gc.Equals, 1)
   683  
   684  	// Failure to create filesystem-1 should not block creation filesystem-2.
   685  	filesystemAccessor.filesystemsWatcher.changes <- []string{"2"}
   686  	waitChannel(c, validated, "waiting for filesystem parameter validation")
   687  	createFilesystemParams := waitChannel(c, createdFilesystems, "filesystem created").([]storage.FilesystemParams)
   688  	c.Assert(createFilesystemParams, gc.HasLen, 1)
   689  	c.Assert(createFilesystemParams[0].Tag.String(), gc.Equals, "filesystem-2")
   690  	c.Assert(validateCalls, gc.Equals, 2)
   691  
   692  	filesystemAccessor.filesystemsWatcher.changes <- []string{"3"}
   693  	waitChannel(c, validated, "waiting for filesystem parameter validation")
   694  	assertNoEvent(c, destroyedFilesystems, "filesystem destroyed")
   695  	c.Assert(validateCalls, gc.Equals, 3)
   696  
   697  	// Failure to destroy filesystem-3 should not block creation of filesystem-4.
   698  	filesystemAccessor.filesystemsWatcher.changes <- []string{"4"}
   699  	waitChannel(c, validated, "waiting for filesystem parameter validation")
   700  	destroyFilesystemParams := waitChannel(c, destroyedFilesystems, "filesystem destroyed").([]string)
   701  	c.Assert(destroyFilesystemParams, jc.DeepEquals, []string{"fs-id"})
   702  	c.Assert(validateCalls, gc.Equals, 4)
   703  
   704  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
   705  		{Tag: "filesystem-1", Status: "error", Info: "something is wrong"},
   706  		{Tag: "filesystem-2", Status: "attaching"},
   707  		{Tag: "filesystem-3", Status: "error", Info: "something is wrong"},
   708  		// destroyed filesystems are removed immediately,
   709  		// so there is no status update.
   710  	})
   711  }
   712  
   713  func (s *storageProvisionerSuite) TestFilesystemAdded(c *gc.C) {
   714  	expectedFilesystems := []params.Filesystem{{
   715  		FilesystemTag: "filesystem-1",
   716  		Info: params.FilesystemInfo{
   717  			FilesystemId: "id-1",
   718  			Size:         1024,
   719  		},
   720  	}, {
   721  		FilesystemTag: "filesystem-2",
   722  		Info: params.FilesystemInfo{
   723  			FilesystemId: "id-2",
   724  			Size:         1024,
   725  		},
   726  	}}
   727  
   728  	filesystemInfoSet := make(chan interface{})
   729  	filesystemAccessor := newMockFilesystemAccessor()
   730  	filesystemAccessor.setFilesystemInfo = func(filesystems []params.Filesystem) ([]params.ErrorResult, error) {
   731  		defer close(filesystemInfoSet)
   732  		c.Assert(filesystems, jc.SameContents, expectedFilesystems)
   733  		return nil, nil
   734  	}
   735  
   736  	args := &workerArgs{filesystems: filesystemAccessor}
   737  	worker := newStorageProvisioner(c, args)
   738  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   739  	defer worker.Kill()
   740  
   741  	// The worker should create filesystems according to ids "1" and "2".
   742  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1", "2"}
   743  	// ... but not until the environment config is available.
   744  	assertNoEvent(c, filesystemInfoSet, "filesystem info set")
   745  	args.environ.watcher.changes <- struct{}{}
   746  	waitChannel(c, filesystemInfoSet, "waiting for filesystem info to be set")
   747  }
   748  
   749  func (s *storageProvisionerSuite) TestVolumeNeedsInstance(c *gc.C) {
   750  	volumeInfoSet := make(chan interface{})
   751  	volumeAccessor := newMockVolumeAccessor()
   752  	volumeAccessor.setVolumeInfo = func([]params.Volume) ([]params.ErrorResult, error) {
   753  		defer close(volumeInfoSet)
   754  		return nil, nil
   755  	}
   756  	volumeAccessor.setVolumeAttachmentInfo = func([]params.VolumeAttachment) ([]params.ErrorResult, error) {
   757  		return nil, nil
   758  	}
   759  
   760  	args := &workerArgs{volumes: volumeAccessor}
   761  	worker := newStorageProvisioner(c, args)
   762  	defer worker.Wait()
   763  	defer worker.Kill()
   764  
   765  	volumeAccessor.volumesWatcher.changes <- []string{needsInstanceVolumeId}
   766  	args.environ.watcher.changes <- struct{}{}
   767  	assertNoEvent(c, volumeInfoSet, "volume info set")
   768  	args.machines.instanceIds[names.NewMachineTag("1")] = "inst-id"
   769  	args.machines.watcher.changes <- struct{}{}
   770  	waitChannel(c, volumeInfoSet, "waiting for volume info to be set")
   771  }
   772  
   773  func (s *storageProvisionerSuite) TestVolumeNonDynamic(c *gc.C) {
   774  	volumeInfoSet := make(chan interface{})
   775  	volumeAccessor := newMockVolumeAccessor()
   776  	volumeAccessor.setVolumeInfo = func([]params.Volume) ([]params.ErrorResult, error) {
   777  		defer close(volumeInfoSet)
   778  		return nil, nil
   779  	}
   780  
   781  	args := &workerArgs{volumes: volumeAccessor}
   782  	worker := newStorageProvisioner(c, args)
   783  	defer worker.Wait()
   784  	defer worker.Kill()
   785  
   786  	// Volumes for non-dynamic providers should not be created.
   787  	s.provider.dynamic = false
   788  	args.environ.watcher.changes <- struct{}{}
   789  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
   790  	assertNoEvent(c, volumeInfoSet, "volume info set")
   791  }
   792  
   793  func (s *storageProvisionerSuite) TestVolumeAttachmentAdded(c *gc.C) {
   794  	// We should get two volume attachments:
   795  	//   - volume-1 to machine-1, because the volume and
   796  	//     machine are provisioned, but the attachment is not.
   797  	//   - volume-1 to machine-0, because the volume,
   798  	//     machine, and attachment are provisioned, but
   799  	//     in a previous session, so a reattachment is
   800  	//     requested.
   801  	expectedVolumeAttachments := []params.VolumeAttachment{{
   802  		VolumeTag:  "volume-1",
   803  		MachineTag: "machine-1",
   804  		Info: params.VolumeAttachmentInfo{
   805  			DeviceName: "/dev/sda1",
   806  			ReadOnly:   true,
   807  		},
   808  	}, {
   809  		VolumeTag:  "volume-1",
   810  		MachineTag: "machine-0",
   811  		Info: params.VolumeAttachmentInfo{
   812  			DeviceName: "/dev/sda1",
   813  			ReadOnly:   true,
   814  		},
   815  	}}
   816  
   817  	var allVolumeAttachments []params.VolumeAttachment
   818  	volumeAttachmentInfoSet := make(chan interface{})
   819  	volumeAccessor := newMockVolumeAccessor()
   820  	volumeAccessor.setVolumeAttachmentInfo = func(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) {
   821  		allVolumeAttachments = append(allVolumeAttachments, volumeAttachments...)
   822  		volumeAttachmentInfoSet <- nil
   823  		return make([]params.ErrorResult, len(volumeAttachments)), nil
   824  	}
   825  
   826  	// volume-1, machine-0, and machine-1 are provisioned.
   827  	volumeAccessor.provisionedVolumes["volume-1"] = params.Volume{
   828  		VolumeTag: "volume-1",
   829  		Info: params.VolumeInfo{
   830  			VolumeId: "vol-123",
   831  		},
   832  	}
   833  	volumeAccessor.provisionedMachines["machine-0"] = instance.Id("already-provisioned-0")
   834  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   835  
   836  	// machine-0/volume-1 attachment is already created.
   837  	// We should see a reattachment.
   838  	alreadyAttached := params.MachineStorageId{
   839  		MachineTag:    "machine-0",
   840  		AttachmentTag: "volume-1",
   841  	}
   842  	volumeAccessor.provisionedAttachments[alreadyAttached] = params.VolumeAttachment{
   843  		MachineTag: "machine-0",
   844  		VolumeTag:  "volume-1",
   845  	}
   846  
   847  	args := &workerArgs{volumes: volumeAccessor}
   848  	worker := newStorageProvisioner(c, args)
   849  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   850  	defer worker.Kill()
   851  
   852  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   853  		MachineTag: "machine-1", AttachmentTag: "volume-1",
   854  	}, {
   855  		MachineTag: "machine-1", AttachmentTag: "volume-2",
   856  	}, {
   857  		MachineTag: "machine-2", AttachmentTag: "volume-1",
   858  	}, {
   859  		MachineTag: "machine-0", AttachmentTag: "volume-1",
   860  	}}
   861  	assertNoEvent(c, volumeAttachmentInfoSet, "volume attachment info set")
   862  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
   863  	args.environ.watcher.changes <- struct{}{}
   864  	waitChannel(c, volumeAttachmentInfoSet, "waiting for volume attachments to be set")
   865  	c.Assert(allVolumeAttachments, jc.SameContents, expectedVolumeAttachments)
   866  
   867  	// Reattachment should only happen once per session.
   868  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   869  		MachineTag:    "machine-0",
   870  		AttachmentTag: "volume-1",
   871  	}}
   872  	assertNoEvent(c, volumeAttachmentInfoSet, "volume attachment info set")
   873  }
   874  
   875  func (s *storageProvisionerSuite) TestFilesystemAttachmentAdded(c *gc.C) {
   876  	// We should only get a single filesystem attachment, because it is the
   877  	// only combination where both machine and filesystem are already
   878  	// provisioned, and the attachmenti s not.
   879  	// We should get two filesystem attachments:
   880  	//   - filesystem-1 to machine-1, because the filesystem and
   881  	//     machine are provisioned, but the attachment is not.
   882  	//   - filesystem-1 to machine-0, because the filesystem,
   883  	//     machine, and attachment are provisioned, but in a
   884  	//     previous session, so a reattachment is requested.
   885  	expectedFilesystemAttachments := []params.FilesystemAttachment{{
   886  		FilesystemTag: "filesystem-1",
   887  		MachineTag:    "machine-1",
   888  		Info: params.FilesystemAttachmentInfo{
   889  			MountPoint: "/srv/fs-123",
   890  		},
   891  	}, {
   892  		FilesystemTag: "filesystem-1",
   893  		MachineTag:    "machine-0",
   894  		Info: params.FilesystemAttachmentInfo{
   895  			MountPoint: "/srv/fs-123",
   896  		},
   897  	}}
   898  
   899  	var allFilesystemAttachments []params.FilesystemAttachment
   900  	filesystemAttachmentInfoSet := make(chan interface{})
   901  	filesystemAccessor := newMockFilesystemAccessor()
   902  	filesystemAccessor.setFilesystemAttachmentInfo = func(filesystemAttachments []params.FilesystemAttachment) ([]params.ErrorResult, error) {
   903  		allFilesystemAttachments = append(allFilesystemAttachments, filesystemAttachments...)
   904  		filesystemAttachmentInfoSet <- nil
   905  		return make([]params.ErrorResult, len(filesystemAttachments)), nil
   906  	}
   907  
   908  	// filesystem-1 and machine-1 are provisioned.
   909  	filesystemAccessor.provisionedFilesystems["filesystem-1"] = params.Filesystem{
   910  		FilesystemTag: "filesystem-1",
   911  		Info: params.FilesystemInfo{
   912  			FilesystemId: "fs-123",
   913  		},
   914  	}
   915  	filesystemAccessor.provisionedMachines["machine-0"] = instance.Id("already-provisioned-0")
   916  	filesystemAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
   917  
   918  	// machine-0/filesystem-1 attachment is already created.
   919  	// We should see a reattachment.
   920  	alreadyAttached := params.MachineStorageId{
   921  		MachineTag:    "machine-0",
   922  		AttachmentTag: "filesystem-1",
   923  	}
   924  	filesystemAccessor.provisionedAttachments[alreadyAttached] = params.FilesystemAttachment{
   925  		MachineTag:    "machine-0",
   926  		FilesystemTag: "filesystem-1",
   927  	}
   928  
   929  	args := &workerArgs{filesystems: filesystemAccessor}
   930  	worker := newStorageProvisioner(c, args)
   931  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   932  	defer worker.Kill()
   933  
   934  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   935  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
   936  	}, {
   937  		MachineTag: "machine-1", AttachmentTag: "filesystem-2",
   938  	}, {
   939  		MachineTag: "machine-2", AttachmentTag: "filesystem-1",
   940  	}, {
   941  		MachineTag: "machine-0", AttachmentTag: "filesystem-1",
   942  	}}
   943  	// ... but not until the environment config is available.
   944  	assertNoEvent(c, filesystemAttachmentInfoSet, "filesystem attachment info set")
   945  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1"}
   946  	args.environ.watcher.changes <- struct{}{}
   947  	waitChannel(c, filesystemAttachmentInfoSet, "waiting for filesystem attachments to be set")
   948  	c.Assert(allFilesystemAttachments, jc.SameContents, expectedFilesystemAttachments)
   949  
   950  	// Reattachment should only happen once per session.
   951  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
   952  		MachineTag:    "machine-0",
   953  		AttachmentTag: "filesystem-1",
   954  	}}
   955  	assertNoEvent(c, filesystemAttachmentInfoSet, "filesystem attachment info set")
   956  }
   957  
   958  func (s *storageProvisionerSuite) TestCreateVolumeBackedFilesystem(c *gc.C) {
   959  	filesystemInfoSet := make(chan interface{})
   960  	filesystemAccessor := newMockFilesystemAccessor()
   961  	filesystemAccessor.setFilesystemInfo = func(filesystems []params.Filesystem) ([]params.ErrorResult, error) {
   962  		filesystemInfoSet <- filesystems
   963  		return nil, nil
   964  	}
   965  
   966  	args := &workerArgs{
   967  		scope:       names.NewMachineTag("0"),
   968  		filesystems: filesystemAccessor,
   969  	}
   970  	worker := newStorageProvisioner(c, args)
   971  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
   972  	defer worker.Kill()
   973  
   974  	args.volumes.blockDevices[params.MachineStorageId{
   975  		MachineTag:    "machine-0",
   976  		AttachmentTag: "volume-0-0",
   977  	}] = storage.BlockDevice{
   978  		DeviceName: "xvdf1",
   979  		Size:       123,
   980  	}
   981  	filesystemAccessor.filesystemsWatcher.changes <- []string{"0/0", "0/1"}
   982  	assertNoEvent(c, filesystemInfoSet, "filesystem info set")
   983  	args.environ.watcher.changes <- struct{}{}
   984  
   985  	// Only the block device for volume 0/0 is attached at the moment,
   986  	// so only the corresponding filesystem will be created.
   987  	filesystemInfo := waitChannel(
   988  		c, filesystemInfoSet,
   989  		"waiting for filesystem info to be set",
   990  	).([]params.Filesystem)
   991  	c.Assert(filesystemInfo, jc.DeepEquals, []params.Filesystem{{
   992  		FilesystemTag: "filesystem-0-0",
   993  		Info: params.FilesystemInfo{
   994  			FilesystemId: "xvdf1",
   995  			Size:         123,
   996  		},
   997  	}})
   998  
   999  	// If we now attach the block device for volume 0/1 and trigger the
  1000  	// notification, then the storage provisioner will wake up and create
  1001  	// the filesystem.
  1002  	args.volumes.blockDevices[params.MachineStorageId{
  1003  		MachineTag:    "machine-0",
  1004  		AttachmentTag: "volume-0-1",
  1005  	}] = storage.BlockDevice{
  1006  		DeviceName: "xvdf2",
  1007  		Size:       246,
  1008  	}
  1009  	args.volumes.blockDevicesWatcher.changes <- struct{}{}
  1010  	filesystemInfo = waitChannel(
  1011  		c, filesystemInfoSet,
  1012  		"waiting for filesystem info to be set",
  1013  	).([]params.Filesystem)
  1014  	c.Assert(filesystemInfo, jc.DeepEquals, []params.Filesystem{{
  1015  		FilesystemTag: "filesystem-0-1",
  1016  		Info: params.FilesystemInfo{
  1017  			FilesystemId: "xvdf2",
  1018  			Size:         246,
  1019  		},
  1020  	}})
  1021  }
  1022  
  1023  func (s *storageProvisionerSuite) TestAttachVolumeBackedFilesystem(c *gc.C) {
  1024  	infoSet := make(chan interface{})
  1025  	filesystemAccessor := newMockFilesystemAccessor()
  1026  	filesystemAccessor.setFilesystemAttachmentInfo = func(attachments []params.FilesystemAttachment) ([]params.ErrorResult, error) {
  1027  		infoSet <- attachments
  1028  		return nil, nil
  1029  	}
  1030  
  1031  	args := &workerArgs{
  1032  		scope:       names.NewMachineTag("0"),
  1033  		filesystems: filesystemAccessor,
  1034  	}
  1035  	worker := newStorageProvisioner(c, args)
  1036  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1037  	defer worker.Kill()
  1038  
  1039  	filesystemAccessor.provisionedFilesystems["filesystem-0-0"] = params.Filesystem{
  1040  		FilesystemTag: "filesystem-0-0",
  1041  		VolumeTag:     "volume-0-0",
  1042  		Info: params.FilesystemInfo{
  1043  			FilesystemId: "whatever",
  1044  			Size:         123,
  1045  		},
  1046  	}
  1047  	filesystemAccessor.provisionedMachines["machine-0"] = instance.Id("already-provisioned-0")
  1048  
  1049  	args.volumes.blockDevices[params.MachineStorageId{
  1050  		MachineTag:    "machine-0",
  1051  		AttachmentTag: "volume-0-0",
  1052  	}] = storage.BlockDevice{
  1053  		DeviceName: "xvdf1",
  1054  		Size:       123,
  1055  	}
  1056  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1057  		MachineTag:    "machine-0",
  1058  		AttachmentTag: "filesystem-0-0",
  1059  	}}
  1060  	assertNoEvent(c, infoSet, "filesystem attachment info set")
  1061  	args.environ.watcher.changes <- struct{}{}
  1062  	filesystemAccessor.filesystemsWatcher.changes <- []string{"0/0"}
  1063  
  1064  	info := waitChannel(
  1065  		c, infoSet, "waiting for filesystem attachment info to be set",
  1066  	).([]params.FilesystemAttachment)
  1067  	c.Assert(info, jc.DeepEquals, []params.FilesystemAttachment{{
  1068  		FilesystemTag: "filesystem-0-0",
  1069  		MachineTag:    "machine-0",
  1070  		Info: params.FilesystemAttachmentInfo{
  1071  			MountPoint: "/mnt/xvdf1",
  1072  			ReadOnly:   true,
  1073  		},
  1074  	}})
  1075  }
  1076  
  1077  func (s *storageProvisionerSuite) TestUpdateModelConfig(c *gc.C) {
  1078  	volumeAccessor := newMockVolumeAccessor()
  1079  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1080  	s.provider.volumeSourceFunc = func(envConfig *config.Config, sourceConfig *storage.Config) (storage.VolumeSource, error) {
  1081  		c.Assert(envConfig, gc.NotNil)
  1082  		c.Assert(sourceConfig, gc.NotNil)
  1083  		c.Assert(envConfig.AllAttrs()["foo"], gc.Equals, "bar")
  1084  		return nil, errors.New("zinga")
  1085  	}
  1086  
  1087  	args := &workerArgs{volumes: volumeAccessor}
  1088  	worker := newStorageProvisioner(c, args)
  1089  	defer worker.Wait()
  1090  	defer worker.Kill()
  1091  
  1092  	newConfig, err := args.environ.cfg.Apply(map[string]interface{}{"foo": "bar"})
  1093  	c.Assert(err, jc.ErrorIsNil)
  1094  
  1095  	args.environ.watcher.changes <- struct{}{}
  1096  	args.environ.setConfig(newConfig)
  1097  	args.environ.watcher.changes <- struct{}{}
  1098  	args.volumes.volumesWatcher.changes <- []string{"1", "2"}
  1099  
  1100  	err = worker.Wait()
  1101  	c.Assert(err, gc.ErrorMatches, `creating volumes: getting volume source: getting storage source "dummy": zinga`)
  1102  }
  1103  
  1104  func (s *storageProvisionerSuite) TestResourceTags(c *gc.C) {
  1105  	volumeInfoSet := make(chan interface{})
  1106  	volumeAccessor := newMockVolumeAccessor()
  1107  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1108  	volumeAccessor.setVolumeInfo = func(volumes []params.Volume) ([]params.ErrorResult, error) {
  1109  		defer close(volumeInfoSet)
  1110  		return nil, nil
  1111  	}
  1112  
  1113  	filesystemInfoSet := make(chan interface{})
  1114  	filesystemAccessor := newMockFilesystemAccessor()
  1115  	filesystemAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1116  	filesystemAccessor.setFilesystemInfo = func(filesystems []params.Filesystem) ([]params.ErrorResult, error) {
  1117  		defer close(filesystemInfoSet)
  1118  		return nil, nil
  1119  	}
  1120  
  1121  	var volumeSource dummyVolumeSource
  1122  	s.provider.volumeSourceFunc = func(envConfig *config.Config, sourceConfig *storage.Config) (storage.VolumeSource, error) {
  1123  		return &volumeSource, nil
  1124  	}
  1125  
  1126  	var filesystemSource dummyFilesystemSource
  1127  	s.provider.filesystemSourceFunc = func(envConfig *config.Config, sourceConfig *storage.Config) (storage.FilesystemSource, error) {
  1128  		return &filesystemSource, nil
  1129  	}
  1130  
  1131  	args := &workerArgs{
  1132  		volumes:     volumeAccessor,
  1133  		filesystems: filesystemAccessor,
  1134  	}
  1135  	worker := newStorageProvisioner(c, args)
  1136  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1137  	defer worker.Kill()
  1138  
  1139  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
  1140  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1"}
  1141  	args.environ.watcher.changes <- struct{}{}
  1142  	waitChannel(c, volumeInfoSet, "waiting for volume info to be set")
  1143  	waitChannel(c, filesystemInfoSet, "waiting for filesystem info to be set")
  1144  	c.Assert(volumeSource.createVolumesArgs, jc.DeepEquals, [][]storage.VolumeParams{{{
  1145  		Tag:          names.NewVolumeTag("1"),
  1146  		Size:         1024,
  1147  		Provider:     "dummy",
  1148  		Attributes:   map[string]interface{}{"persistent": true},
  1149  		ResourceTags: map[string]string{"very": "fancy"},
  1150  		Attachment: &storage.VolumeAttachmentParams{
  1151  			Volume: names.NewVolumeTag("1"),
  1152  			AttachmentParams: storage.AttachmentParams{
  1153  				Machine:    names.NewMachineTag("1"),
  1154  				Provider:   "dummy",
  1155  				InstanceId: "already-provisioned-1",
  1156  				ReadOnly:   true,
  1157  			},
  1158  		},
  1159  	}}})
  1160  	c.Assert(filesystemSource.createFilesystemsArgs, jc.DeepEquals, [][]storage.FilesystemParams{{{
  1161  		Tag:          names.NewFilesystemTag("1"),
  1162  		Size:         1024,
  1163  		Provider:     "dummy",
  1164  		ResourceTags: map[string]string{"very": "fancy"},
  1165  	}}})
  1166  }
  1167  
  1168  func (s *storageProvisionerSuite) TestSetVolumeInfoErrorStopsWorker(c *gc.C) {
  1169  	volumeAccessor := newMockVolumeAccessor()
  1170  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1171  	volumeAccessor.setVolumeInfo = func(volumes []params.Volume) ([]params.ErrorResult, error) {
  1172  		return nil, errors.New("belly up")
  1173  	}
  1174  
  1175  	args := &workerArgs{volumes: volumeAccessor}
  1176  	worker := newStorageProvisioner(c, args)
  1177  	defer worker.Wait()
  1178  	defer worker.Kill()
  1179  
  1180  	done := make(chan interface{})
  1181  	go func() {
  1182  		defer close(done)
  1183  		err := worker.Wait()
  1184  		c.Assert(err, gc.ErrorMatches, "creating volumes: publishing volumes to state: belly up")
  1185  	}()
  1186  
  1187  	args.volumes.volumesWatcher.changes <- []string{"1"}
  1188  	args.environ.watcher.changes <- struct{}{}
  1189  	waitChannel(c, done, "waiting for worker to exit")
  1190  }
  1191  
  1192  func (s *storageProvisionerSuite) TestSetVolumeInfoErrorResultDoesNotStopWorker(c *gc.C) {
  1193  	volumeAccessor := newMockVolumeAccessor()
  1194  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1195  	volumeAccessor.setVolumeInfo = func(volumes []params.Volume) ([]params.ErrorResult, error) {
  1196  		return []params.ErrorResult{{Error: &params.Error{Message: "message", Code: "code"}}}, nil
  1197  	}
  1198  
  1199  	args := &workerArgs{volumes: volumeAccessor}
  1200  	worker := newStorageProvisioner(c, args)
  1201  	defer func() {
  1202  		err := worker.Wait()
  1203  		c.Assert(err, jc.ErrorIsNil)
  1204  	}()
  1205  	defer worker.Kill()
  1206  
  1207  	done := make(chan interface{})
  1208  	go func() {
  1209  		defer close(done)
  1210  		worker.Wait()
  1211  	}()
  1212  
  1213  	args.volumes.volumesWatcher.changes <- []string{"1"}
  1214  	args.environ.watcher.changes <- struct{}{}
  1215  	assertNoEvent(c, done, "worker exited")
  1216  }
  1217  
  1218  func (s *storageProvisionerSuite) TestDetachVolumesUnattached(c *gc.C) {
  1219  	removed := make(chan interface{})
  1220  	removeAttachments := func(ids []params.MachineStorageId) ([]params.ErrorResult, error) {
  1221  		defer close(removed)
  1222  		c.Assert(ids, gc.DeepEquals, []params.MachineStorageId{{
  1223  			MachineTag:    "machine-0",
  1224  			AttachmentTag: "volume-0",
  1225  		}})
  1226  		return make([]params.ErrorResult, len(ids)), nil
  1227  	}
  1228  
  1229  	args := &workerArgs{
  1230  		life: &mockLifecycleManager{removeAttachments: removeAttachments},
  1231  	}
  1232  	worker := newStorageProvisioner(c, args)
  1233  	defer worker.Wait()
  1234  	defer worker.Kill()
  1235  
  1236  	args.volumes.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1237  		MachineTag: "machine-0", AttachmentTag: "volume-0",
  1238  	}}
  1239  	args.environ.watcher.changes <- struct{}{}
  1240  	waitChannel(c, removed, "waiting for attachment to be removed")
  1241  }
  1242  
  1243  func (s *storageProvisionerSuite) TestDetachVolumes(c *gc.C) {
  1244  	var attached bool
  1245  	volumeAttachmentInfoSet := make(chan interface{})
  1246  	volumeAccessor := newMockVolumeAccessor()
  1247  	volumeAccessor.setVolumeAttachmentInfo = func(volumeAttachments []params.VolumeAttachment) ([]params.ErrorResult, error) {
  1248  		close(volumeAttachmentInfoSet)
  1249  		attached = true
  1250  		for _, a := range volumeAttachments {
  1251  			id := params.MachineStorageId{
  1252  				MachineTag:    a.MachineTag,
  1253  				AttachmentTag: a.VolumeTag,
  1254  			}
  1255  			volumeAccessor.provisionedAttachments[id] = a
  1256  		}
  1257  		return make([]params.ErrorResult, len(volumeAttachments)), nil
  1258  	}
  1259  
  1260  	expectedAttachmentIds := []params.MachineStorageId{{
  1261  		MachineTag: "machine-1", AttachmentTag: "volume-1",
  1262  	}}
  1263  
  1264  	attachmentLife := func(ids []params.MachineStorageId) ([]params.LifeResult, error) {
  1265  		c.Assert(ids, gc.DeepEquals, expectedAttachmentIds)
  1266  		life := params.Alive
  1267  		if attached {
  1268  			life = params.Dying
  1269  		}
  1270  		return []params.LifeResult{{Life: life}}, nil
  1271  	}
  1272  
  1273  	detached := make(chan interface{})
  1274  	s.provider.detachVolumesFunc = func(args []storage.VolumeAttachmentParams) ([]error, error) {
  1275  		c.Assert(args, gc.HasLen, 1)
  1276  		c.Assert(args[0].Machine.String(), gc.Equals, expectedAttachmentIds[0].MachineTag)
  1277  		c.Assert(args[0].Volume.String(), gc.Equals, expectedAttachmentIds[0].AttachmentTag)
  1278  		defer close(detached)
  1279  		return make([]error, len(args)), nil
  1280  	}
  1281  
  1282  	removed := make(chan interface{})
  1283  	removeAttachments := func(ids []params.MachineStorageId) ([]params.ErrorResult, error) {
  1284  		c.Assert(ids, gc.DeepEquals, expectedAttachmentIds)
  1285  		close(removed)
  1286  		return make([]params.ErrorResult, len(ids)), nil
  1287  	}
  1288  
  1289  	// volume-1 and machine-1 are provisioned.
  1290  	volumeAccessor.provisionedVolumes["volume-1"] = params.Volume{
  1291  		VolumeTag: "volume-1",
  1292  		Info: params.VolumeInfo{
  1293  			VolumeId: "vol-123",
  1294  		},
  1295  	}
  1296  	volumeAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1297  
  1298  	args := &workerArgs{
  1299  		volumes: volumeAccessor,
  1300  		life: &mockLifecycleManager{
  1301  			attachmentLife:    attachmentLife,
  1302  			removeAttachments: removeAttachments,
  1303  		},
  1304  	}
  1305  	worker := newStorageProvisioner(c, args)
  1306  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1307  	defer worker.Kill()
  1308  
  1309  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1310  		MachineTag: "machine-1", AttachmentTag: "volume-1",
  1311  	}}
  1312  	volumeAccessor.volumesWatcher.changes <- []string{"1"}
  1313  	args.environ.watcher.changes <- struct{}{}
  1314  	waitChannel(c, volumeAttachmentInfoSet, "waiting for volume attachments to be set")
  1315  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1316  		MachineTag: "machine-1", AttachmentTag: "volume-1",
  1317  	}}
  1318  	waitChannel(c, detached, "waiting for volume to be detached")
  1319  	waitChannel(c, removed, "waiting for attachment to be removed")
  1320  }
  1321  
  1322  func (s *storageProvisionerSuite) TestDetachVolumesRetry(c *gc.C) {
  1323  	machine := names.NewMachineTag("1")
  1324  	volume := names.NewVolumeTag("1")
  1325  	attachmentId := params.MachineStorageId{
  1326  		MachineTag:    machine.String(),
  1327  		AttachmentTag: volume.String(),
  1328  	}
  1329  	volumeAccessor := newMockVolumeAccessor()
  1330  	volumeAccessor.provisionedAttachments[attachmentId] = params.VolumeAttachment{
  1331  		MachineTag: machine.String(),
  1332  		VolumeTag:  volume.String(),
  1333  	}
  1334  	volumeAccessor.provisionedVolumes[volume.String()] = params.Volume{
  1335  		VolumeTag: volume.String(),
  1336  		Info: params.VolumeInfo{
  1337  			VolumeId: "vol-123",
  1338  		},
  1339  	}
  1340  	volumeAccessor.provisionedMachines[machine.String()] = instance.Id("already-provisioned-1")
  1341  
  1342  	attachmentLife := func(ids []params.MachineStorageId) ([]params.LifeResult, error) {
  1343  		return []params.LifeResult{{Life: params.Dying}}, nil
  1344  	}
  1345  
  1346  	// mockFunc's After will progress the current time by the specified
  1347  	// duration and signal the channel immediately.
  1348  	clock := &mockClock{}
  1349  	var detachVolumeTimes []time.Time
  1350  
  1351  	s.provider.detachVolumesFunc = func(args []storage.VolumeAttachmentParams) ([]error, error) {
  1352  		detachVolumeTimes = append(detachVolumeTimes, clock.Now())
  1353  		if len(detachVolumeTimes) < 10 {
  1354  			return []error{errors.New("badness")}, nil
  1355  		}
  1356  		return []error{nil}, nil
  1357  	}
  1358  
  1359  	removed := make(chan interface{})
  1360  	removeAttachments := func(ids []params.MachineStorageId) ([]params.ErrorResult, error) {
  1361  		close(removed)
  1362  		return make([]params.ErrorResult, len(ids)), nil
  1363  	}
  1364  
  1365  	args := &workerArgs{
  1366  		volumes: volumeAccessor,
  1367  		clock:   clock,
  1368  		life: &mockLifecycleManager{
  1369  			attachmentLife:    attachmentLife,
  1370  			removeAttachments: removeAttachments,
  1371  		},
  1372  	}
  1373  	worker := newStorageProvisioner(c, args)
  1374  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1375  	defer worker.Kill()
  1376  
  1377  	volumeAccessor.volumesWatcher.changes <- []string{volume.Id()}
  1378  	args.environ.watcher.changes <- struct{}{}
  1379  	volumeAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1380  		MachineTag:    machine.String(),
  1381  		AttachmentTag: volume.String(),
  1382  	}}
  1383  	waitChannel(c, removed, "waiting for attachment to be removed")
  1384  	c.Assert(detachVolumeTimes, gc.HasLen, 10)
  1385  
  1386  	// The first attempt should have been immediate: T0.
  1387  	c.Assert(detachVolumeTimes[0], gc.Equals, time.Time{})
  1388  
  1389  	delays := make([]time.Duration, len(detachVolumeTimes)-1)
  1390  	for i := range detachVolumeTimes[1:] {
  1391  		delays[i] = detachVolumeTimes[i+1].Sub(detachVolumeTimes[i])
  1392  	}
  1393  	c.Assert(delays, jc.DeepEquals, []time.Duration{
  1394  		30 * time.Second,
  1395  		1 * time.Minute,
  1396  		2 * time.Minute,
  1397  		4 * time.Minute,
  1398  		8 * time.Minute,
  1399  		16 * time.Minute,
  1400  		30 * time.Minute, // ceiling reached
  1401  		30 * time.Minute,
  1402  		30 * time.Minute,
  1403  	})
  1404  
  1405  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
  1406  		{Tag: "volume-1", Status: "detaching", Info: "badness"}, // DetachVolumes
  1407  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1408  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1409  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1410  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1411  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1412  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1413  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1414  		{Tag: "volume-1", Status: "detaching", Info: "badness"},
  1415  		{Tag: "volume-1", Status: "detached", Info: ""},
  1416  	})
  1417  }
  1418  
  1419  func (s *storageProvisionerSuite) TestDetachFilesystemsUnattached(c *gc.C) {
  1420  	removed := make(chan interface{})
  1421  	removeAttachments := func(ids []params.MachineStorageId) ([]params.ErrorResult, error) {
  1422  		defer close(removed)
  1423  		c.Assert(ids, gc.DeepEquals, []params.MachineStorageId{{
  1424  			MachineTag:    "machine-0",
  1425  			AttachmentTag: "filesystem-0",
  1426  		}})
  1427  		return make([]params.ErrorResult, len(ids)), nil
  1428  	}
  1429  
  1430  	args := &workerArgs{
  1431  		life: &mockLifecycleManager{removeAttachments: removeAttachments},
  1432  	}
  1433  	worker := newStorageProvisioner(c, args)
  1434  	defer worker.Wait()
  1435  	defer worker.Kill()
  1436  
  1437  	args.filesystems.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1438  		MachineTag: "machine-0", AttachmentTag: "filesystem-0",
  1439  	}}
  1440  	args.environ.watcher.changes <- struct{}{}
  1441  	waitChannel(c, removed, "waiting for attachment to be removed")
  1442  }
  1443  
  1444  func (s *storageProvisionerSuite) TestDetachFilesystems(c *gc.C) {
  1445  	var attached bool
  1446  	filesystemAttachmentInfoSet := make(chan interface{})
  1447  	filesystemAccessor := newMockFilesystemAccessor()
  1448  	filesystemAccessor.setFilesystemAttachmentInfo = func(filesystemAttachments []params.FilesystemAttachment) ([]params.ErrorResult, error) {
  1449  		close(filesystemAttachmentInfoSet)
  1450  		attached = true
  1451  		for _, a := range filesystemAttachments {
  1452  			id := params.MachineStorageId{
  1453  				MachineTag:    a.MachineTag,
  1454  				AttachmentTag: a.FilesystemTag,
  1455  			}
  1456  			filesystemAccessor.provisionedAttachments[id] = a
  1457  		}
  1458  		return make([]params.ErrorResult, len(filesystemAttachments)), nil
  1459  	}
  1460  
  1461  	expectedAttachmentIds := []params.MachineStorageId{{
  1462  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
  1463  	}}
  1464  
  1465  	attachmentLife := func(ids []params.MachineStorageId) ([]params.LifeResult, error) {
  1466  		c.Assert(ids, gc.DeepEquals, expectedAttachmentIds)
  1467  		life := params.Alive
  1468  		if attached {
  1469  			life = params.Dying
  1470  		}
  1471  		return []params.LifeResult{{Life: life}}, nil
  1472  	}
  1473  
  1474  	detached := make(chan interface{})
  1475  	s.provider.detachFilesystemsFunc = func(args []storage.FilesystemAttachmentParams) ([]error, error) {
  1476  		c.Assert(args, gc.HasLen, 1)
  1477  		c.Assert(args[0].Machine.String(), gc.Equals, expectedAttachmentIds[0].MachineTag)
  1478  		c.Assert(args[0].Filesystem.String(), gc.Equals, expectedAttachmentIds[0].AttachmentTag)
  1479  		defer close(detached)
  1480  		return make([]error, len(args)), nil
  1481  	}
  1482  
  1483  	removed := make(chan interface{})
  1484  	removeAttachments := func(ids []params.MachineStorageId) ([]params.ErrorResult, error) {
  1485  		c.Assert(ids, gc.DeepEquals, expectedAttachmentIds)
  1486  		close(removed)
  1487  		return make([]params.ErrorResult, len(ids)), nil
  1488  	}
  1489  
  1490  	// filesystem-1 and machine-1 are provisioned.
  1491  	filesystemAccessor.provisionedFilesystems["filesystem-1"] = params.Filesystem{
  1492  		FilesystemTag: "filesystem-1",
  1493  		Info: params.FilesystemInfo{
  1494  			FilesystemId: "fs-id",
  1495  		},
  1496  	}
  1497  	filesystemAccessor.provisionedMachines["machine-1"] = instance.Id("already-provisioned-1")
  1498  
  1499  	args := &workerArgs{
  1500  		filesystems: filesystemAccessor,
  1501  		life: &mockLifecycleManager{
  1502  			attachmentLife:    attachmentLife,
  1503  			removeAttachments: removeAttachments,
  1504  		},
  1505  	}
  1506  	worker := newStorageProvisioner(c, args)
  1507  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1508  	defer worker.Kill()
  1509  
  1510  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1511  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
  1512  	}}
  1513  	filesystemAccessor.filesystemsWatcher.changes <- []string{"1"}
  1514  	args.environ.watcher.changes <- struct{}{}
  1515  	waitChannel(c, filesystemAttachmentInfoSet, "waiting for filesystem attachments to be set")
  1516  	filesystemAccessor.attachmentsWatcher.changes <- []watcher.MachineStorageId{{
  1517  		MachineTag: "machine-1", AttachmentTag: "filesystem-1",
  1518  	}}
  1519  	waitChannel(c, detached, "waiting for filesystem to be detached")
  1520  	waitChannel(c, removed, "waiting for attachment to be removed")
  1521  }
  1522  
  1523  func (s *storageProvisionerSuite) TestDestroyVolumes(c *gc.C) {
  1524  	provisionedVolume := names.NewVolumeTag("1")
  1525  	unprovisionedVolume := names.NewVolumeTag("2")
  1526  
  1527  	volumeAccessor := newMockVolumeAccessor()
  1528  	volumeAccessor.provisionVolume(provisionedVolume)
  1529  
  1530  	life := func(tags []names.Tag) ([]params.LifeResult, error) {
  1531  		results := make([]params.LifeResult, len(tags))
  1532  		for i := range results {
  1533  			results[i].Life = params.Dead
  1534  		}
  1535  		return results, nil
  1536  	}
  1537  
  1538  	destroyedChan := make(chan interface{}, 1)
  1539  	s.provider.destroyVolumesFunc = func(volumeIds []string) ([]error, error) {
  1540  		destroyedChan <- volumeIds
  1541  		return make([]error, len(volumeIds)), nil
  1542  	}
  1543  
  1544  	removedChan := make(chan interface{}, 1)
  1545  	remove := func(tags []names.Tag) ([]params.ErrorResult, error) {
  1546  		removedChan <- tags
  1547  		return make([]params.ErrorResult, len(tags)), nil
  1548  	}
  1549  
  1550  	args := &workerArgs{
  1551  		volumes: volumeAccessor,
  1552  		life: &mockLifecycleManager{
  1553  			life:   life,
  1554  			remove: remove,
  1555  		},
  1556  	}
  1557  	worker := newStorageProvisioner(c, args)
  1558  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1559  	defer worker.Kill()
  1560  
  1561  	volumeAccessor.volumesWatcher.changes <- []string{
  1562  		provisionedVolume.Id(),
  1563  		unprovisionedVolume.Id(),
  1564  	}
  1565  	args.environ.watcher.changes <- struct{}{}
  1566  
  1567  	// Both volumes should be removed; the provisioned one
  1568  	// should be deprovisioned first.
  1569  
  1570  	destroyed := waitChannel(c, destroyedChan, "waiting for volume to be deprovisioned")
  1571  	assertNoEvent(c, destroyedChan, "volumes deprovisioned")
  1572  	c.Assert(destroyed, jc.DeepEquals, []string{"vol-1"})
  1573  
  1574  	var removed []names.Tag
  1575  	for len(removed) < 2 {
  1576  		tags := waitChannel(c, removedChan, "waiting for volumes to be removed").([]names.Tag)
  1577  		removed = append(removed, tags...)
  1578  	}
  1579  	c.Assert(removed, jc.SameContents, []names.Tag{provisionedVolume, unprovisionedVolume})
  1580  	assertNoEvent(c, removedChan, "volumes removed")
  1581  }
  1582  
  1583  func (s *storageProvisionerSuite) TestDestroyVolumesRetry(c *gc.C) {
  1584  	volume := names.NewVolumeTag("1")
  1585  	volumeAccessor := newMockVolumeAccessor()
  1586  	volumeAccessor.provisionVolume(volume)
  1587  
  1588  	life := func(tags []names.Tag) ([]params.LifeResult, error) {
  1589  		return []params.LifeResult{{Life: params.Dead}}, nil
  1590  	}
  1591  
  1592  	// mockFunc's After will progress the current time by the specified
  1593  	// duration and signal the channel immediately.
  1594  	clock := &mockClock{}
  1595  	var destroyVolumeTimes []time.Time
  1596  
  1597  	s.provider.destroyVolumesFunc = func(volumeIds []string) ([]error, error) {
  1598  		destroyVolumeTimes = append(destroyVolumeTimes, clock.Now())
  1599  		if len(destroyVolumeTimes) < 10 {
  1600  			return []error{errors.New("badness")}, nil
  1601  		}
  1602  		return []error{nil}, nil
  1603  	}
  1604  
  1605  	removedChan := make(chan interface{}, 1)
  1606  	remove := func(tags []names.Tag) ([]params.ErrorResult, error) {
  1607  		removedChan <- tags
  1608  		return make([]params.ErrorResult, len(tags)), nil
  1609  	}
  1610  
  1611  	args := &workerArgs{
  1612  		volumes: volumeAccessor,
  1613  		clock:   clock,
  1614  		life: &mockLifecycleManager{
  1615  			life:   life,
  1616  			remove: remove,
  1617  		},
  1618  	}
  1619  	worker := newStorageProvisioner(c, args)
  1620  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1621  	defer worker.Kill()
  1622  
  1623  	volumeAccessor.volumesWatcher.changes <- []string{volume.Id()}
  1624  	args.environ.watcher.changes <- struct{}{}
  1625  	waitChannel(c, removedChan, "waiting for volume to be removed")
  1626  	c.Assert(destroyVolumeTimes, gc.HasLen, 10)
  1627  
  1628  	// The first attempt should have been immediate: T0.
  1629  	c.Assert(destroyVolumeTimes[0], gc.Equals, time.Time{})
  1630  
  1631  	delays := make([]time.Duration, len(destroyVolumeTimes)-1)
  1632  	for i := range destroyVolumeTimes[1:] {
  1633  		delays[i] = destroyVolumeTimes[i+1].Sub(destroyVolumeTimes[i])
  1634  	}
  1635  	c.Assert(delays, jc.DeepEquals, []time.Duration{
  1636  		30 * time.Second,
  1637  		1 * time.Minute,
  1638  		2 * time.Minute,
  1639  		4 * time.Minute,
  1640  		8 * time.Minute,
  1641  		16 * time.Minute,
  1642  		30 * time.Minute, // ceiling reached
  1643  		30 * time.Minute,
  1644  		30 * time.Minute,
  1645  	})
  1646  
  1647  	c.Assert(args.statusSetter.args, jc.DeepEquals, []params.EntityStatusArgs{
  1648  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1649  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1650  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1651  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1652  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1653  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1654  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1655  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1656  		{Tag: "volume-1", Status: "destroying", Info: "badness"},
  1657  	})
  1658  }
  1659  
  1660  func (s *storageProvisionerSuite) TestDestroyFilesystems(c *gc.C) {
  1661  	provisionedFilesystem := names.NewFilesystemTag("1")
  1662  	unprovisionedFilesystem := names.NewFilesystemTag("2")
  1663  
  1664  	filesystemAccessor := newMockFilesystemAccessor()
  1665  	filesystemAccessor.provisionFilesystem(provisionedFilesystem)
  1666  
  1667  	life := func(tags []names.Tag) ([]params.LifeResult, error) {
  1668  		results := make([]params.LifeResult, len(tags))
  1669  		for i := range results {
  1670  			results[i].Life = params.Dead
  1671  		}
  1672  		return results, nil
  1673  	}
  1674  
  1675  	removedChan := make(chan interface{}, 1)
  1676  	remove := func(tags []names.Tag) ([]params.ErrorResult, error) {
  1677  		removedChan <- tags
  1678  		return make([]params.ErrorResult, len(tags)), nil
  1679  	}
  1680  
  1681  	args := &workerArgs{
  1682  		filesystems: filesystemAccessor,
  1683  		life: &mockLifecycleManager{
  1684  			life:   life,
  1685  			remove: remove,
  1686  		},
  1687  	}
  1688  	worker := newStorageProvisioner(c, args)
  1689  	defer func() { c.Assert(worker.Wait(), gc.IsNil) }()
  1690  	defer worker.Kill()
  1691  
  1692  	filesystemAccessor.filesystemsWatcher.changes <- []string{
  1693  		provisionedFilesystem.Id(),
  1694  		unprovisionedFilesystem.Id(),
  1695  	}
  1696  	args.environ.watcher.changes <- struct{}{}
  1697  
  1698  	// Both filesystems should be removed; the provisioned one
  1699  	// *should* be deprovisioned first, but we don't currently
  1700  	// have the ability to do so via the storage provider API.
  1701  
  1702  	var removed []names.Tag
  1703  	for len(removed) < 2 {
  1704  		tags := waitChannel(c, removedChan, "waiting for filesystems to be removed").([]names.Tag)
  1705  		removed = append(removed, tags...)
  1706  	}
  1707  	c.Assert(removed, jc.SameContents, []names.Tag{provisionedFilesystem, unprovisionedFilesystem})
  1708  	assertNoEvent(c, removedChan, "filesystems removed")
  1709  }
  1710  
  1711  func newStorageProvisioner(c *gc.C, args *workerArgs) worker.Worker {
  1712  	if args == nil {
  1713  		args = &workerArgs{}
  1714  	}
  1715  	var storageDir string
  1716  	switch args.scope.(type) {
  1717  	case names.MachineTag:
  1718  		storageDir = "storage-dir"
  1719  	case names.ModelTag:
  1720  	case nil:
  1721  		args.scope = coretesting.ModelTag
  1722  	}
  1723  	if args.volumes == nil {
  1724  		args.volumes = newMockVolumeAccessor()
  1725  	}
  1726  	if args.filesystems == nil {
  1727  		args.filesystems = newMockFilesystemAccessor()
  1728  	}
  1729  	if args.life == nil {
  1730  		args.life = &mockLifecycleManager{}
  1731  	}
  1732  	if args.environ == nil {
  1733  		args.environ = newMockModelAccessor(c)
  1734  	}
  1735  	if args.machines == nil {
  1736  		args.machines = newMockMachineAccessor(c)
  1737  	}
  1738  	if args.clock == nil {
  1739  		args.clock = &mockClock{}
  1740  	}
  1741  	if args.statusSetter == nil {
  1742  		args.statusSetter = &mockStatusSetter{}
  1743  	}
  1744  	worker, err := storageprovisioner.NewStorageProvisioner(storageprovisioner.Config{
  1745  		Scope:       args.scope,
  1746  		StorageDir:  storageDir,
  1747  		Volumes:     args.volumes,
  1748  		Filesystems: args.filesystems,
  1749  		Life:        args.life,
  1750  		Environ:     args.environ,
  1751  		Machines:    args.machines,
  1752  		Status:      args.statusSetter,
  1753  		Clock:       args.clock,
  1754  	})
  1755  	c.Assert(err, jc.ErrorIsNil)
  1756  	return worker
  1757  }
  1758  
  1759  type workerArgs struct {
  1760  	scope        names.Tag
  1761  	volumes      *mockVolumeAccessor
  1762  	filesystems  *mockFilesystemAccessor
  1763  	life         *mockLifecycleManager
  1764  	environ      *mockModelAccessor
  1765  	machines     *mockMachineAccessor
  1766  	clock        clock.Clock
  1767  	statusSetter *mockStatusSetter
  1768  }
  1769  
  1770  func waitChannel(c *gc.C, ch <-chan interface{}, activity string) interface{} {
  1771  	select {
  1772  	case v := <-ch:
  1773  		return v
  1774  	case <-time.After(coretesting.LongWait):
  1775  		c.Fatalf("timed out " + activity)
  1776  		panic("unreachable")
  1777  	}
  1778  }
  1779  
  1780  func assertNoEvent(c *gc.C, ch <-chan interface{}, event string) {
  1781  	select {
  1782  	case <-ch:
  1783  		c.Fatalf("unexpected " + event)
  1784  	case <-time.After(coretesting.ShortWait):
  1785  	}
  1786  }