github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/uniter/remotestate/storagewatcher.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package remotestate
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names"
     9  	"launchpad.net/tomb"
    10  
    11  	apiwatcher "github.com/juju/juju/api/watcher"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/state/watcher"
    14  )
    15  
    16  func newStorageAttachmentWatcher(
    17  	st StorageAccessor,
    18  	in apiwatcher.NotifyWatcher,
    19  	unitTag names.UnitTag,
    20  	storageTag names.StorageTag,
    21  	changes chan<- storageAttachmentChange,
    22  ) *storageAttachmentWatcher {
    23  	s := &storageAttachmentWatcher{
    24  		st:         st,
    25  		watcher:    in,
    26  		changes:    changes,
    27  		storageTag: storageTag,
    28  		unitTag:    unitTag,
    29  	}
    30  	go func() {
    31  		defer s.tomb.Done()
    32  		defer watcher.Stop(in, &s.tomb)
    33  		s.tomb.Kill(s.loop())
    34  	}()
    35  	return s
    36  }
    37  
    38  type StorageAccessor interface {
    39  	// StorageAttachment returns the storage attachment with the specified
    40  	// unit and storage tags.
    41  	StorageAttachment(names.StorageTag, names.UnitTag) (params.StorageAttachment, error)
    42  }
    43  
    44  // storageAttachmentWatcher watches for changes to the attachment status of
    45  // the storage with the specified tag and sends the tag to the specified channel
    46  // when a change occurs.
    47  type storageAttachmentWatcher struct {
    48  	tomb tomb.Tomb
    49  
    50  	st         StorageAccessor
    51  	watcher    apiwatcher.NotifyWatcher
    52  	storageTag names.StorageTag
    53  	unitTag    names.UnitTag
    54  	changes    chan<- storageAttachmentChange
    55  }
    56  
    57  type storageAttachmentChange struct {
    58  	Tag      names.StorageTag
    59  	Snapshot StorageSnapshot
    60  }
    61  
    62  func getStorageSnapshot(
    63  	st StorageAccessor,
    64  	storageTag names.StorageTag,
    65  	unitTag names.UnitTag,
    66  ) (StorageSnapshot, error) {
    67  	attachment, err := st.StorageAttachment(storageTag, unitTag)
    68  	if err != nil {
    69  		return StorageSnapshot{}, errors.Annotate(err, "refreshing storage details")
    70  	}
    71  	snapshot := StorageSnapshot{
    72  		Life:     attachment.Life,
    73  		Kind:     attachment.Kind,
    74  		Attached: true,
    75  		Location: attachment.Location,
    76  	}
    77  	return snapshot, nil
    78  }
    79  
    80  func (s *storageAttachmentWatcher) loop() error {
    81  	for {
    82  		select {
    83  		case <-s.tomb.Dying():
    84  			return tomb.ErrDying
    85  		case _, ok := <-s.watcher.Changes():
    86  			if !ok {
    87  				return watcher.EnsureErr(s.watcher)
    88  			}
    89  			snapshot, err := getStorageSnapshot(
    90  				s.st, s.storageTag, s.unitTag,
    91  			)
    92  			if params.IsCodeNotFound(err) {
    93  				// The storage attachment was removed
    94  				// from state, so we can stop watching.
    95  				return nil
    96  			} else if params.IsCodeNotProvisioned(err) {
    97  				// We do not care about unattached
    98  				// storage here.
    99  				continue
   100  			} else if err != nil {
   101  				return err
   102  			}
   103  			change := storageAttachmentChange{
   104  				s.storageTag,
   105  				snapshot,
   106  			}
   107  			select {
   108  			case <-s.tomb.Dying():
   109  				return tomb.ErrDying
   110  			case s.changes <- change:
   111  			}
   112  		}
   113  	}
   114  }
   115  
   116  func (s *storageAttachmentWatcher) Stop() error {
   117  	s.tomb.Kill(nil)
   118  	return s.tomb.Wait()
   119  }