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