1
// Copyright 2012-2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/juju/state"
10
"github.com/juju/juju/state/watcher"
11
"github.com/juju/juju/worker"
14
// ensureErr is defined as a variable to allow the test suite
16
var ensureErr = watcher.EnsureErr
18
// notifyWorker is the internal implementation of the Worker
19
// interface, using a NotifyWatcher for handling changes.
20
type notifyWorker struct {
22
handler NotifyWatchHandler
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)
34
// TearDown should cleanup any resources that are left around.
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
42
Handle(done <-chan struct{}) error
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 {
55
nw.tomb.Kill(nw.loop())
60
// Kill is part of the worker.Worker interface.
61
func (nw *notifyWorker) Kill() {
65
// Wait is part of the worker.Worker interface.
66
func (nw *notifyWorker) Wait() error {
70
type tearDowner interface {
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 {
82
func (nw *notifyWorker) loop() error {
83
w, err := nw.handler.SetUp()
86
// We don't bother to propagate an error, because we
87
// already have an error
92
defer propagateTearDown(nw.handler, &nw.tomb)
93
defer watcher.Stop(w, &nw.tomb)
96
case <-nw.tomb.Dying():
98
case _, ok := <-w.Changes():
102
if err := nw.handler.Handle(nw.tomb.Dying()); err != nil {