github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"gopkg.in/juju/names.v2"
     9  	"gopkg.in/juju/worker.v1"
    10  	"gopkg.in/juju/worker.v1/catacomb"
    11  
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/core/watcher"
    14  )
    15  
    16  type StorageAccessor interface {
    17  	// StorageAttachment returns the storage attachment with the specified
    18  	// unit and storage tags.
    19  	StorageAttachment(names.StorageTag, names.UnitTag) (params.StorageAttachment, error)
    20  }
    21  
    22  // newStorageAttachmentsWatcher creates a new worker that wakes on input from
    23  // the supplied watcher's Changes chan, finds out more about them, and delivers
    24  // them on the supplied out chan.
    25  //
    26  // The caller releases responsibility for stopping the supplied watcher and
    27  // waiting for errors, *whether or not this method succeeds*.
    28  func newStorageAttachmentWatcher(
    29  	st StorageAccessor,
    30  	watcher watcher.NotifyWatcher,
    31  	unitTag names.UnitTag,
    32  	storageTag names.StorageTag,
    33  	out chan<- storageAttachmentChange,
    34  ) (*storageAttachmentWatcher, error) {
    35  	s := &storageAttachmentWatcher{
    36  		st:         st,
    37  		changes:    watcher.Changes(),
    38  		out:        out,
    39  		storageTag: storageTag,
    40  		unitTag:    unitTag,
    41  	}
    42  	err := catacomb.Invoke(catacomb.Plan{
    43  		Site: &s.catacomb,
    44  		Work: s.loop,
    45  		Init: []worker.Worker{watcher},
    46  	})
    47  	if err != nil {
    48  		return nil, errors.Trace(err)
    49  	}
    50  	return s, nil
    51  }
    52  
    53  // storageAttachmentWatcher watches for changes to the attachment status of
    54  // the storage with the specified tag and sends the tag to the specified channel
    55  // when a change occurs.
    56  type storageAttachmentWatcher struct {
    57  	catacomb catacomb.Catacomb
    58  
    59  	st         StorageAccessor
    60  	changes    watcher.NotifyChannel
    61  	storageTag names.StorageTag
    62  	unitTag    names.UnitTag
    63  	out        chan<- storageAttachmentChange
    64  }
    65  
    66  type storageAttachmentChange struct {
    67  	Tag      names.StorageTag
    68  	Snapshot StorageSnapshot
    69  }
    70  
    71  func getStorageSnapshot(
    72  	st StorageAccessor,
    73  	storageTag names.StorageTag,
    74  	unitTag names.UnitTag,
    75  ) (StorageSnapshot, error) {
    76  	attachment, err := st.StorageAttachment(storageTag, unitTag)
    77  	if err != nil {
    78  		return StorageSnapshot{}, errors.Annotate(err, "refreshing storage details")
    79  	}
    80  	snapshot := StorageSnapshot{
    81  		Life:     attachment.Life,
    82  		Kind:     attachment.Kind,
    83  		Attached: true,
    84  		Location: attachment.Location,
    85  	}
    86  	return snapshot, nil
    87  }
    88  
    89  func (s *storageAttachmentWatcher) loop() error {
    90  	for {
    91  		select {
    92  		case <-s.catacomb.Dying():
    93  			return s.catacomb.ErrDying()
    94  		case _, ok := <-s.changes:
    95  			if !ok {
    96  				return errors.New("storage attachment watcher closed")
    97  			}
    98  			snapshot, err := getStorageSnapshot(
    99  				s.st, s.storageTag, s.unitTag,
   100  			)
   101  			if params.IsCodeNotFound(err) {
   102  				// The storage attachment was removed
   103  				// from state, so we can stop watching.
   104  				return nil
   105  			} else if params.IsCodeNotProvisioned(err) {
   106  				// We do not care about unattached
   107  				// storage here.
   108  				continue
   109  			} else if err != nil {
   110  				return err
   111  			}
   112  			change := storageAttachmentChange{
   113  				s.storageTag,
   114  				snapshot,
   115  			}
   116  			select {
   117  			case <-s.catacomb.Dying():
   118  				return s.catacomb.ErrDying()
   119  			case s.out <- change:
   120  			}
   121  		}
   122  	}
   123  }
   124  
   125  // Kill is part of the worker.Worker interface.
   126  func (s *storageAttachmentWatcher) Kill() {
   127  	s.catacomb.Kill(nil)
   128  }
   129  
   130  // Wait is part of the worker.Worker interface.
   131  func (s *storageAttachmentWatcher) Wait() error {
   132  	return s.catacomb.Wait()
   133  }