github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "launchpad.net/tomb" 8 9 "github.com/juju/juju/state" 10 "github.com/juju/juju/state/watcher" 11 "github.com/juju/juju/worker" 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 go func() { 54 defer nw.tomb.Done() 55 nw.tomb.Kill(nw.loop()) 56 }() 57 return nw 58 } 59 60 // Kill is part of the worker.Worker interface. 61 func (nw *notifyWorker) Kill() { 62 nw.tomb.Kill(nil) 63 } 64 65 // Wait is part of the worker.Worker interface. 66 func (nw *notifyWorker) Wait() error { 67 return nw.tomb.Wait() 68 } 69 70 type tearDowner interface { 71 TearDown() error 72 } 73 74 // propagateTearDown tears down the handler, but ensures any error is 75 // propagated through the tomb's Kill method. 76 func propagateTearDown(handler tearDowner, t *tomb.Tomb) { 77 if err := handler.TearDown(); err != nil { 78 t.Kill(err) 79 } 80 } 81 82 func (nw *notifyWorker) loop() error { 83 w, err := nw.handler.SetUp() 84 if err != nil { 85 if w != nil { 86 // We don't bother to propagate an error, because we 87 // already have an error 88 w.Stop() 89 } 90 return err 91 } 92 defer propagateTearDown(nw.handler, &nw.tomb) 93 defer watcher.Stop(w, &nw.tomb) 94 for { 95 select { 96 case <-nw.tomb.Dying(): 97 return tomb.ErrDying 98 case _, ok := <-w.Changes(): 99 if !ok { 100 return ensureErr(w) 101 } 102 if err := nw.handler.Handle(nw.tomb.Dying()); err != nil { 103 return err 104 } 105 } 106 } 107 }