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 }