1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"launchpad.net/juju-core/state/api"
10
"launchpad.net/juju-core/version"
13
// UpgradeStep defines an idempotent operation that is run to perform
14
// a specific upgrade step.
15
type UpgradeStep interface {
16
// A human readable description of what the upgrade step does.
18
// The target machine types for which the upgrade step is applicable.
19
Targets() []UpgradeTarget
20
// The upgrade business logic.
24
// UpgradeOperation defines a slice of upgrade steps.
25
type UpgradeOperation interface {
26
// The Juju version for which this operation is applicable.
27
// Upgrade operations designed for versions of Juju earlier
28
// than we are upgrading from are not run since such steps would
29
// already have been used to get to the version we are running now.
30
TargetVersion() version.Number
31
// Steps to perform during an upgrade.
35
// UpgradeTarget defines the type of machine for which a particular upgrade
37
type UpgradeTarget string
40
HostMachine = UpgradeTarget("hostMachine")
41
HostContainer = UpgradeTarget("hostContainer")
42
StateServer = UpgradeTarget("stateServer")
45
// upgradeToVersion encapsulates the steps which need to be run to
46
// upgrade any prior version of Juju to targetVersion.
47
type upgradeToVersion struct {
48
targetVersion version.Number
52
// Steps is defined on the UpgradeOperation interface.
53
func (u upgradeToVersion) Steps() []UpgradeStep {
57
// TargetVersion is defined on the UpgradeOperation interface.
58
func (u upgradeToVersion) TargetVersion() version.Number {
59
return u.targetVersion
62
// Context is used give the upgrade steps attributes needed
64
type Context interface {
65
// An API connection to state.
69
// UpgradeContext is a default Context implementation.
70
type UpgradeContext struct {
74
// APIState is defined on the Context interface.
75
func (c *UpgradeContext) APIState() *api.State {
79
// upgradeOperation provides base attributes for any upgrade step.
80
type upgradeOperation struct {
82
targets []UpgradeTarget
86
// Description is defined on the UpgradeStep interface.
87
func (u *upgradeOperation) Description() string {
91
// Targets is defined on the UpgradeStep interface.
92
func (u *upgradeOperation) Targets() []UpgradeTarget {
96
// upgradeOperations returns an ordered slice of sets of operations needed
97
// to upgrade Juju to particular version. The slice is ordered by target
98
// version, so that the sets of operations are executed in order from oldest
99
// version to most recent.
100
var upgradeOperations = func(context Context) []UpgradeOperation {
101
steps := []UpgradeOperation{
103
version.MustParse("1.18.0"),
104
stepsFor118(context),
110
// upgradeError records a description of the step being performed and the error.
111
type upgradeError struct {
116
func (e *upgradeError) Error() string {
117
return fmt.Sprintf("%s: %v", e.description, e.err)
120
// PerformUpgrade runs the business logic needed to upgrade the current "from" version to this
121
// version of Juju on the "target" type of machine.
122
func PerformUpgrade(from version.Number, target UpgradeTarget, context Context) *upgradeError {
123
// If from is not known, it is 1.16.
124
if from == version.Zero {
125
from = version.MustParse("1.16.0")
127
for _, upgradeOps := range upgradeOperations(context) {
128
// Do not run steps for versions of Juju earlier than we are upgrading from.
129
if upgradeOps.TargetVersion().Less(from) {
132
if err := runUpgradeSteps(target, upgradeOps); err != nil {
139
// validTarget returns true if target is in step.Targets().
140
func validTarget(target UpgradeTarget, step UpgradeStep) bool {
141
for _, opTarget := range step.Targets() {
142
if target == opTarget {
146
return len(step.Targets()) == 0
149
// runUpgradeSteps runs all the upgrade steps relevant to target.
150
// As soon as any error is encountered, the operation is aborted since
151
// subsequent steps may required successful completion of earlier ones.
152
// The steps must be idempotent so that the entire upgrade operation can
154
func runUpgradeSteps(target UpgradeTarget, upgradeOp UpgradeOperation) *upgradeError {
155
for _, step := range upgradeOp.Steps() {
156
if !validTarget(target, step) {
159
if err := step.Run(); err != nil {
160
return &upgradeError{
161
description: step.Description(),