github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/core/watcher/strings.go (about)

     1  // Copyright 2013-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/worker.v1/catacomb"
     9  )
    10  
    11  // StringsChannel is a change channel as described in the CoreWatcher docs.
    12  //
    13  // It sends a single value indicating a baseline set of values, and subsequent
    14  // values representing additions, changes, and/or removals of those values. The
    15  // precise semantics may depend upon the individual watcher.
    16  type StringsChannel <-chan []string
    17  
    18  // StringsWatcher conveniently ties a StringsChannel to the worker.Worker that
    19  // represents its validity.
    20  type StringsWatcher interface {
    21  	CoreWatcher
    22  	Changes() StringsChannel
    23  }
    24  
    25  // StringsHandler defines the operation of a StringsWorker.
    26  type StringsHandler interface {
    27  
    28  	// SetUp is called once when creating a StringsWorker. It must return a
    29  	// StringsWatcher or an error. The StringsHandler takes responsibility for
    30  	// stopping any returned watcher and handling any errors.
    31  	SetUp() (StringsWatcher, error)
    32  
    33  	// Handle is called with every value received from the StringsWatcher
    34  	// returned by SetUp. If it returns an error, the StringsWorker will be
    35  	// stopped.
    36  	//
    37  	// If Handle runs any blocking operations it must pass through, or select
    38  	// on, the supplied abort channel; this channel will be closed when the
    39  	// StringsWorker is killed. An aborted Handle should not return an error.
    40  	Handle(abort <-chan struct{}, changes []string) error
    41  
    42  	// TearDown is called once when stopping a StringsWorker, whether or not
    43  	// SetUp succeeded. It need not concern itself with the StringsWatcher, but
    44  	// must clean up any other resources created in SetUp or Handle.
    45  	TearDown() error
    46  }
    47  
    48  // StringsConfig holds the direct dependencies of a StringsWorker.
    49  type StringsConfig struct {
    50  	Handler StringsHandler
    51  }
    52  
    53  // Validate returns ann error if the config cannot start a StringsWorker.
    54  func (config StringsConfig) Validate() error {
    55  	if config.Handler == nil {
    56  		return errors.NotValidf("nil Handler")
    57  	}
    58  	return nil
    59  }
    60  
    61  // NewStringsWorker starts a new worker that runs a StringsHandler.
    62  func NewStringsWorker(config StringsConfig) (*StringsWorker, error) {
    63  	if err := config.Validate(); err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	sw := &StringsWorker{
    67  		config: config,
    68  	}
    69  	err := catacomb.Invoke(catacomb.Plan{
    70  		Site: &sw.catacomb,
    71  		Work: sw.loop,
    72  	})
    73  	if err != nil {
    74  		return nil, errors.Trace(err)
    75  	}
    76  	return sw, nil
    77  }
    78  
    79  // StringsWorker is a worker that wraps a StringsWatcher.
    80  type StringsWorker struct {
    81  	config   StringsConfig
    82  	catacomb catacomb.Catacomb
    83  }
    84  
    85  func (sw *StringsWorker) loop() (err error) {
    86  	changes := sw.setUp()
    87  	defer sw.tearDown(err)
    88  	abort := sw.catacomb.Dying()
    89  	for {
    90  		select {
    91  		case <-abort:
    92  			return sw.catacomb.ErrDying()
    93  		case strings, ok := <-changes:
    94  			if !ok {
    95  				return errors.New("change channel closed")
    96  			}
    97  			err = sw.config.Handler.Handle(abort, strings)
    98  			if err != nil {
    99  				return err
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // setUp calls the handler's SetUp method; registers any returned watcher with
   106  // the worker's catacomb; and returns the watcher's changes channel. Any errors
   107  // encountered kill the worker and cause a nil channel to be returned.
   108  func (sw *StringsWorker) setUp() StringsChannel {
   109  	watcher, err := sw.config.Handler.SetUp()
   110  	if err != nil {
   111  		sw.catacomb.Kill(err)
   112  	}
   113  	if watcher == nil {
   114  		sw.catacomb.Kill(errors.New("handler returned nil watcher"))
   115  	} else {
   116  		if err := sw.catacomb.Add(watcher); err != nil {
   117  			sw.catacomb.Kill(err)
   118  		} else {
   119  			return watcher.Changes()
   120  		}
   121  	}
   122  	return nil
   123  }
   124  
   125  // tearDown kills the worker with the supplied error; and then kills it with
   126  // any error returned by the handler's TearDown method.
   127  func (sw *StringsWorker) tearDown(err error) {
   128  	sw.catacomb.Kill(err)
   129  	err = sw.config.Handler.TearDown()
   130  	sw.catacomb.Kill(err)
   131  }
   132  
   133  // Kill is part of the worker.Worker interface.
   134  func (sw *StringsWorker) Kill() {
   135  	sw.catacomb.Kill(nil)
   136  }
   137  
   138  // Wait is part of the worker.Worker interface.
   139  func (sw *StringsWorker) Wait() error {
   140  	return sw.catacomb.Wait()
   141  }