1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package stateconfigwatcher
7
"github.com/juju/errors"
8
"github.com/juju/loggo"
9
"github.com/juju/utils/voyeur"
10
"gopkg.in/juju/names.v2"
13
"github.com/juju/juju/agent"
14
"github.com/juju/juju/worker"
15
"github.com/juju/juju/worker/dependency"
18
var logger = loggo.GetLogger("juju.worker.stateconfigwatcher")
20
type ManifoldConfig struct {
22
AgentConfigChanged *voyeur.Value
25
// Manifold returns a dependency.Manifold which wraps the machine
26
// agent's voyeur.Value which gets set whenever it the machine agent's
27
// config is changed. Whenever the config is updated the presence of
28
// state serving info is checked and if state serving info was added
29
// or removed the manifold worker will bounce itself.
31
// The manifold offes a single boolean output which will be true if
32
// state serving info is available (i.e. the machine agent should be a
33
// state server) and false otherwise.
35
// This manifold is intended to be used as a dependency for the state
37
func Manifold(config ManifoldConfig) dependency.Manifold {
38
return dependency.Manifold{
39
Inputs: []string{config.AgentName},
40
Start: func(context dependency.Context) (worker.Worker, error) {
42
if err := context.Get(config.AgentName, &a); err != nil {
46
if config.AgentConfigChanged == nil {
47
return nil, errors.NotValidf("nil AgentConfigChanged")
50
if _, ok := a.CurrentConfig().Tag().(names.MachineTag); !ok {
51
return nil, errors.New("manifold can only be used with a machine agent")
54
w := &stateConfigWatcher{
56
agentConfigChanged: config.AgentConfigChanged,
68
// outputFunc extracts a bool from a *stateConfigWatcher. If true, the
69
// agent is a state server.
70
func outputFunc(in worker.Worker, out interface{}) error {
71
inWorker, _ := in.(*stateConfigWatcher)
73
return errors.Errorf("in should be a %T; got %T", inWorker, in)
75
switch outPointer := out.(type) {
77
*outPointer = inWorker.isStateServer()
79
return errors.Errorf("out should be *bool; got %T", out)
84
type stateConfigWatcher struct {
87
agentConfigChanged *voyeur.Value
90
func (w *stateConfigWatcher) isStateServer() bool {
91
config := w.agent.CurrentConfig()
92
_, ok := config.StateServingInfo()
96
func (w *stateConfigWatcher) loop() error {
97
watch := w.agentConfigChanged.Watch()
100
lastValue := w.isStateServer()
102
watchCh := make(chan bool)
107
case <-w.tomb.Dying():
109
case watchCh <- true:
112
// watcher or voyeur.Value closed.
121
case <-w.tomb.Dying():
122
logger.Infof("tomb dying")
124
case _, ok := <-watchCh:
126
return errors.New("config changed value closed")
128
if w.isStateServer() != lastValue {
129
// State serving info has been set or unset so restart
130
// so that dependents get notified. ErrBounce ensures
131
// that the manifold is restarted quickly.
132
logger.Debugf("state serving info change in agent config")
133
return dependency.ErrBounce
139
// Kill implements worker.Worker.
140
func (w *stateConfigWatcher) Kill() {
144
// Wait implements worker.Worker.
145
func (w *stateConfigWatcher) Wait() error {