github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/watcher/legacy/notifyworker.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package legacy 5 6 import ( 7 "gopkg.in/juju/worker.v1" 8 "gopkg.in/tomb.v2" 9 10 "github.com/juju/juju/state" 11 "github.com/juju/juju/state/watcher" 12 ) 13 14 // ensureErr is defined as a variable to allow the test suite 15 // to override it. 16 var ensureErr = watcher.EnsureErr 17 18 // notifyWorker is the internal implementation of the Worker 19 // interface, using a NotifyWatcher for handling changes. 20 type notifyWorker struct { 21 tomb tomb.Tomb 22 handler NotifyWatchHandler 23 } 24 25 // NotifyWatchHandler implements the business logic that is triggered 26 // as part of watching a NotifyWatcher. 27 type NotifyWatchHandler interface { 28 // SetUp will be called once, and should return the watcher that will 29 // be used to trigger subsequent Handle()s. SetUp can return a watcher 30 // even if there is an error, and the notify worker will make sure 31 // to stop the watcher. 32 SetUp() (state.NotifyWatcher, error) 33 34 // TearDown should cleanup any resources that are left around. 35 TearDown() error 36 37 // Handle is called whenever the watcher returned from SetUp sends a value 38 // on its Changes() channel. The done channel will be closed if and when 39 // the worker is being interrupted to finish. Any worker should avoid any 40 // bare channel reads or writes, but instead use a select with the done 41 // channel. 42 Handle(done <-chan struct{}) error 43 } 44 45 // NewNotifyWorker starts a new worker running the business logic from 46 // the handler. The worker loop is started in another goroutine as a 47 // side effect of calling this. 48 func NewNotifyWorker(handler NotifyWatchHandler) worker.Worker { 49 nw := ¬ifyWorker{ 50 handler: handler, 51 } 52 53 nw.tomb.Go(nw.loop) 54 return nw 55 } 56 57 // Kill is part of the worker.Worker interface. 58 func (nw *notifyWorker) Kill() { 59 nw.tomb.Kill(nil) 60 } 61 62 // Wait is part of the worker.Worker interface. 63 func (nw *notifyWorker) Wait() error { 64 return nw.tomb.Wait() 65 } 66 67 type tearDowner interface { 68 TearDown() error 69 } 70 71 // propagateTearDown tears down the handler, but ensures any error is 72 // propagated through the tomb's Kill method. 73 func propagateTearDown(handler tearDowner, t *tomb.Tomb) { 74 if err := handler.TearDown(); err != nil { 75 t.Kill(err) 76 } 77 } 78 79 func (nw *notifyWorker) loop() error { 80 w, err := nw.handler.SetUp() 81 if err != nil { 82 if w != nil { 83 // We don't bother to propagate an error, because we 84 // already have an error 85 w.Stop() 86 } 87 return err 88 } 89 defer propagateTearDown(nw.handler, &nw.tomb) 90 defer watcher.Stop(w, &nw.tomb) 91 for { 92 select { 93 case <-nw.tomb.Dying(): 94 return tomb.ErrDying 95 case _, ok := <-w.Changes(): 96 if !ok { 97 return ensureErr(w) 98 } 99 if err := nw.handler.Handle(nw.tomb.Dying()); err != nil { 100 return err 101 } 102 } 103 } 104 }