-
Notifications
You must be signed in to change notification settings - Fork 494
/
upgradeseries.go
133 lines (119 loc) · 3.85 KB
/
upgradeseries.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package model
import "github.com/juju/errors"
// UpgradeSeriesStatus is the current status of a series upgrade for units
type UpgradeSeriesStatus string
func (s UpgradeSeriesStatus) String() string {
return string(s)
}
const (
UpgradeSeriesNotStarted UpgradeSeriesStatus = "not started"
UpgradeSeriesValidate UpgradeSeriesStatus = "validate"
UpgradeSeriesPrepareStarted UpgradeSeriesStatus = "prepare started"
UpgradeSeriesPrepareRunning UpgradeSeriesStatus = "prepare running"
UpgradeSeriesPrepareCompleted UpgradeSeriesStatus = "prepare completed"
UpgradeSeriesCompleteStarted UpgradeSeriesStatus = "complete started"
UpgradeSeriesCompleteRunning UpgradeSeriesStatus = "complete running"
UpgradeSeriesCompleted UpgradeSeriesStatus = "completed"
UpgradeSeriesError UpgradeSeriesStatus = "error"
)
// Graph is a type for representing a Directed acyclic graph (DAG).
type Graph map[UpgradeSeriesStatus][]UpgradeSeriesStatus
// Validate attempts to ensure that all edges from a vertex have a vertex to
// the root graph.
func (g Graph) Validate() error {
for vertex, vertices := range g {
for _, child := range vertices {
if !g.ValidState(child) {
return errors.NotValidf("vertex %q edge to vertex %q is", vertex, child)
}
}
}
return nil
}
// ValidState checks that a state is a valid vertex, as graphs have to ensure
// that all edges to other vertices are also valid then this should be fine to
// do.
func (g Graph) ValidState(state UpgradeSeriesStatus) bool {
_, ok := g[state]
return ok
}
// UpgradeSeriesGraph defines a graph for moving between vertices of an upgrade
// series.
func UpgradeSeriesGraph() Graph {
return map[UpgradeSeriesStatus][]UpgradeSeriesStatus{
// Some clients are older and don't know about the validation phase, so
// in that case we allow them to jump to prepare-started.
UpgradeSeriesNotStarted: {
UpgradeSeriesPrepareStarted,
UpgradeSeriesValidate,
UpgradeSeriesError,
},
UpgradeSeriesValidate: {
UpgradeSeriesPrepareStarted,
UpgradeSeriesError,
},
UpgradeSeriesPrepareStarted: {
UpgradeSeriesPrepareRunning,
UpgradeSeriesError,
},
UpgradeSeriesPrepareRunning: {
UpgradeSeriesPrepareCompleted,
UpgradeSeriesError,
},
UpgradeSeriesPrepareCompleted: {
UpgradeSeriesCompleteStarted,
UpgradeSeriesError,
},
UpgradeSeriesCompleteStarted: {
UpgradeSeriesCompleteRunning,
UpgradeSeriesError,
},
UpgradeSeriesCompleteRunning: {
UpgradeSeriesCompleted,
UpgradeSeriesError,
},
UpgradeSeriesCompleted: {
UpgradeSeriesError,
},
UpgradeSeriesError: {},
}
}
// UpgradeSeriesFSM defines a finite state machine from a given graph of
// possible vertices to transition. The FSM can start in any position using the
// initial state and can move along the edges to the correct vertex.
type UpgradeSeriesFSM struct {
state UpgradeSeriesStatus
vertices Graph
}
// NewUpgradeSeriesFSM creates a UpgradeSeriesFSM from a graph and an initial
// state.
func NewUpgradeSeriesFSM(graph Graph, initial UpgradeSeriesStatus) (*UpgradeSeriesFSM, error) {
if err := graph.Validate(); err != nil {
return nil, errors.Trace(err)
}
return &UpgradeSeriesFSM{
state: initial,
vertices: graph,
}, nil
}
// TransitionTo attempts to transition from the current state to the new given
// state. If the state is currently at the requested state, then that's
// classified as a no-op and no transition is required.
func (u *UpgradeSeriesFSM) TransitionTo(state UpgradeSeriesStatus) bool {
if u.state == state {
return false
}
for _, vertex := range u.vertices[u.state] {
if vertex == state {
u.state = state
return true
}
}
return false
}
// State returns the current state of the fsm.
func (u *UpgradeSeriesFSM) State() UpgradeSeriesStatus {
return u.state
}