-
Notifications
You must be signed in to change notification settings - Fork 0
/
upgrade.go
185 lines (154 loc) · 5.28 KB
/
upgrade.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package upgrades
import (
"fmt"
"github.com/juju/errors"
"github.com/juju/loggo"
"github.com/juju/version"
)
var logger = loggo.GetLogger("juju.upgrade")
// Step defines an idempotent operation that is run to perform
// a specific upgrade step.
type Step interface {
// Description is a human readable description of what the upgrade step does.
Description() string
// Targets returns the target machine types for which the upgrade step is applicable.
Targets() []Target
// Run executes the upgrade business logic.
Run(Context) error
}
// Operation defines what steps to perform to upgrade to a target version.
type Operation interface {
// The Juju version for which this operation is applicable.
// Upgrade operations designed for versions of Juju earlier
// than we are upgrading from are not run since such steps would
// already have been used to get to the version we are running now.
TargetVersion() version.Number
// Steps to perform during an upgrade.
Steps() []Step
}
// OperationSource provides a means of obtaining upgrade operations.
type OperationSource interface {
// UpgradeOperations returns Operations to run during upgrade.
UpgradeOperations() []Operation
}
// Target defines the type of machine for which a particular upgrade
// step can be run.
type Target string
const (
// AllMachines applies to any machine.
AllMachines = Target("allMachines")
// HostMachine is a machine on which units are deployed.
HostMachine = Target("hostMachine")
// Controller is a machine participating in a Juju controller cluster.
Controller = Target("controller")
// DatabaseMaster is a Controller that has the master database, and as such
// is the only target that should run database schema upgrade steps.
DatabaseMaster = Target("databaseMaster")
)
// upgradeToVersion encapsulates the steps which need to be run to
// upgrade any prior version of Juju to targetVersion.
type upgradeToVersion struct {
targetVersion version.Number
steps []Step
}
// Steps is defined on the Operation interface.
func (u upgradeToVersion) Steps() []Step {
return u.steps
}
// TargetVersion is defined on the Operation interface.
func (u upgradeToVersion) TargetVersion() version.Number {
return u.targetVersion
}
// upgradeError records a description of the step being performed and the error.
type upgradeError struct {
description string
err error
}
func (e *upgradeError) Error() string {
return fmt.Sprintf("%s: %v", e.description, e.err)
}
// PerformUpgrade runs the business logic needed to upgrade the current "from"
// version to this version of Juju on the "target" type of machine.
func PerformUpgrade(from version.Number, targets []Target, context Context) error {
if hasStateTarget(targets) {
if err := PerformStateUpgrade(from, targets, context); err != nil {
return errors.Trace(err)
}
}
ops := newUpgradeOpsIterator(from)
if err := runUpgradeSteps(ops, targets, context.APIContext()); err != nil {
return errors.Trace(err)
}
logger.Infof("All upgrade steps completed successfully")
return nil
}
// PerformStateUpgrade runs the upgrades steps
// that target Controller or DatabaseMaster.
func PerformStateUpgrade(from version.Number, targets []Target, context Context) error {
return errors.Trace(runUpgradeSteps(newStateUpgradeOpsIterator(from), targets, context.StateContext()))
}
func hasStateTarget(targets []Target) bool {
for _, target := range targets {
if target == Controller || target == DatabaseMaster {
return true
}
}
return false
}
// runUpgradeSteps finds all the upgrade operations relevant to
// the targets given and runs the associated upgrade steps.
//
// As soon as any error is encountered, the operation is aborted since
// subsequent steps may required successful completion of earlier
// ones. The steps must be idempotent so that the entire upgrade
// operation can be retried.
func runUpgradeSteps(ops *opsIterator, targets []Target, context Context) error {
for ops.Next() {
for _, step := range ops.Get().Steps() {
if targetsMatch(targets, step.Targets()) {
logger.Infof("running upgrade step: %v", step.Description())
if err := step.Run(context); err != nil {
logger.Errorf("upgrade step %q failed: %v", step.Description(), err)
return &upgradeError{
description: step.Description(),
err: err,
}
}
}
}
}
return nil
}
// targetsMatch returns true if any machineTargets match any of
// stepTargets.
func targetsMatch(machineTargets []Target, stepTargets []Target) bool {
for _, machineTarget := range machineTargets {
for _, stepTarget := range stepTargets {
if machineTarget == stepTarget || stepTarget == AllMachines {
return true
}
}
}
return false
}
// upgradeStep is a default Step implementation.
type upgradeStep struct {
description string
targets []Target
run func(Context) error
}
var _ Step = (*upgradeStep)(nil)
// Description is defined on the Step interface.
func (step *upgradeStep) Description() string {
return step.description
}
// Targets is defined on the Step interface.
func (step *upgradeStep) Targets() []Target {
return step.targets
}
// Run is defined on the Step interface.
func (step *upgradeStep) Run(context Context) error {
return step.run(context)
}