1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
10
"github.com/juju/utils/clock"
11
"gopkg.in/juju/charm.v6-unstable/hooks"
14
"github.com/juju/juju/worker"
15
"github.com/juju/juju/worker/uniter/runner/context"
19
// defaultAmberGracePeriod is the time that the unit is allowed to
20
// function without a working API connection before its meter
21
// status is switched to AMBER.
22
defaultAmberGracePeriod = time.Minute * 5
24
// defaultRedGracePeriod is the time that a unit is allowed to function
25
// without a working API connection before its meter status is
27
defaultRedGracePeriod = time.Minute * 15
30
// workerState defines all the possible states the isolatedStatusWorker can be in.
34
Uninitialized WorkerState = iota
35
WaitingAmber // Waiting for a signal to switch to AMBER status.
36
WaitingRed // Waiting for a signal to switch to RED status.
37
Done // No more transitions to perform.
40
// IsolatedConfig stores all the dependencies required to create an isolated meter status worker.
41
type IsolatedConfig struct {
45
AmberGracePeriod time.Duration
46
RedGracePeriod time.Duration
47
TriggerFactory TriggerCreator
50
// Validate validates the config structure and returns an error on failure.
51
func (c IsolatedConfig) Validate() error {
53
return errors.New("hook runner not provided")
55
if c.StateFile == nil {
56
return errors.New("state file not provided")
59
return errors.New("clock not provided")
61
if c.AmberGracePeriod <= 0 {
62
return errors.New("invalid amber grace period")
64
if c.RedGracePeriod <= 0 {
65
return errors.New("invalid red grace period")
67
if c.AmberGracePeriod >= c.RedGracePeriod {
68
return errors.New("amber grace period must be shorter than the red grace period")
73
// isolatedStatusWorker is a worker that is instantiated by the
74
// meter status manifold when the API connection is unavailable.
75
// Its main function is to escalate the meter status of the unit
76
// to amber and later to red.
77
type isolatedStatusWorker struct {
83
// NewIsolatedStatusWorker creates a new status worker that runs without an API connection.
84
func NewIsolatedStatusWorker(cfg IsolatedConfig) (worker.Worker, error) {
85
if err := cfg.Validate(); err != nil {
86
return nil, errors.Trace(err)
88
w := &isolatedStatusWorker{
98
func (w *isolatedStatusWorker) loop() error {
99
code, info, disconnected, err := w.config.StateFile.Read()
101
return errors.Trace(err)
104
// Disconnected time has not been recorded yet.
105
if disconnected == nil {
106
disconnected = &Disconnected{w.config.Clock.Now().Unix(), WaitingAmber}
109
amberSignal, redSignal := w.config.TriggerFactory(disconnected.State, code, disconnected.When(), w.config.Clock, w.config.AmberGracePeriod, w.config.RedGracePeriod)
112
case <-w.tomb.Dying():
115
logger.Debugf("triggering meter status transition to RED due to loss of connection")
117
currentInfo := "unit agent has been disconnected"
119
w.applyStatus(currentCode, currentInfo)
120
code, info = currentCode, currentInfo
121
disconnected.State = Done
123
logger.Debugf("triggering meter status transition to AMBER due to loss of connection")
124
currentCode := "AMBER"
125
currentInfo := "unit agent has been disconnected"
127
w.applyStatus(currentCode, currentInfo)
128
code, info = currentCode, currentInfo
129
disconnected.State = WaitingRed
131
err := w.config.StateFile.Write(code, info, disconnected)
133
return errors.Annotate(err, "failed to record meter status worker state")
138
func (w *isolatedStatusWorker) applyStatus(code, info string) {
139
logger.Tracef("applying meter status change: %q (%q)", code, info)
140
err := w.config.Runner.RunHook(code, info, w.tomb.Dying())
141
cause := errors.Cause(err)
143
case context.IsMissingHookError(cause):
144
logger.Infof("skipped %q hook (missing)", string(hooks.MeterStatusChanged))
146
logger.Errorf("meter status worker encountered hook error: %v", err)
150
// Kill is part of the worker.Worker interface.
151
func (w *isolatedStatusWorker) Kill() {
155
// Wait is part of the worker.Worker interface.
156
func (w *isolatedStatusWorker) Wait() error {