~axwalk/juju-core/lp1303195-manual-ubuntuuser-bash

« back to all changes in this revision

Viewing changes to upgrades/upgrade.go

  • Committer: Ian Booth
  • Date: 2014-02-10 05:34:06 UTC
  • mto: This revision was merged to the branch mainline in revision 2311.
  • Revision ID: ian.booth@canonical.com-20140210053406-jbcy03yplu05pug8
Initial upgrade framework

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2014 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package upgrades
 
5
 
 
6
import (
 
7
        "fmt"
 
8
 
 
9
        "launchpad.net/juju-core/state/api"
 
10
        "launchpad.net/juju-core/version"
 
11
)
 
12
 
 
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.
 
17
        Description() string
 
18
        // The target machine types for which the upgrade step is applicable.
 
19
        Targets() []UpgradeTarget
 
20
        // The upgrade business logic.
 
21
        Run() error
 
22
}
 
23
 
 
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.
 
32
        Steps() []UpgradeStep
 
33
}
 
34
 
 
35
// UpgradeTarget defines the type of machine for which a particular upgrade
 
36
// step can be run.
 
37
type UpgradeTarget string
 
38
 
 
39
const (
 
40
        HostMachine   = UpgradeTarget("hostMachine")
 
41
        HostContainer = UpgradeTarget("hostContainer")
 
42
        StateServer   = UpgradeTarget("stateServer")
 
43
)
 
44
 
 
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
 
49
        steps         []UpgradeStep
 
50
}
 
51
 
 
52
// Steps is defined on the UpgradeOperation interface.
 
53
func (u upgradeToVersion) Steps() []UpgradeStep {
 
54
        return u.steps
 
55
}
 
56
 
 
57
// TargetVersion is defined on the UpgradeOperation interface.
 
58
func (u upgradeToVersion) TargetVersion() version.Number {
 
59
        return u.targetVersion
 
60
}
 
61
 
 
62
// Context is used give the upgrade steps attributes needed
 
63
// to do their job.
 
64
type Context interface {
 
65
        // An API connection to state.
 
66
        APIState() *api.State
 
67
}
 
68
 
 
69
// UpgradeContext is a default Context implementation.
 
70
type UpgradeContext struct {
 
71
        st *api.State
 
72
}
 
73
 
 
74
// APIState is defined on the Context interface.
 
75
func (c *UpgradeContext) APIState() *api.State {
 
76
        return c.st
 
77
}
 
78
 
 
79
// upgradeOperation provides base attributes for any upgrade step.
 
80
type upgradeOperation struct {
 
81
        description string
 
82
        targets     []UpgradeTarget
 
83
        st          *api.State
 
84
}
 
85
 
 
86
// Description is defined on the UpgradeStep interface.
 
87
func (u *upgradeOperation) Description() string {
 
88
        return u.description
 
89
}
 
90
 
 
91
// Targets is defined on the UpgradeStep interface.
 
92
func (u *upgradeOperation) Targets() []UpgradeTarget {
 
93
        return u.targets
 
94
}
 
95
 
 
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{
 
102
                upgradeToVersion{
 
103
                        version.MustParse("1.18.0"),
 
104
                        stepsFor118(context),
 
105
                },
 
106
        }
 
107
        return steps
 
108
}
 
109
 
 
110
// upgradeError records a description of the step being performed and the error.
 
111
type upgradeError struct {
 
112
        description string
 
113
        err         error
 
114
}
 
115
 
 
116
func (e *upgradeError) Error() string {
 
117
        return fmt.Sprintf("%s: %v", e.description, e.err)
 
118
}
 
119
 
 
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")
 
126
        }
 
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) {
 
130
                        continue
 
131
                }
 
132
                if err := runUpgradeSteps(target, upgradeOps); err != nil {
 
133
                        return err
 
134
                }
 
135
        }
 
136
        return nil
 
137
}
 
138
 
 
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 {
 
143
                        return true
 
144
                }
 
145
        }
 
146
        return len(step.Targets()) == 0
 
147
}
 
148
 
 
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
 
153
// be retried.
 
154
func runUpgradeSteps(target UpgradeTarget, upgradeOp UpgradeOperation) *upgradeError {
 
155
        for _, step := range upgradeOp.Steps() {
 
156
                if !validTarget(target, step) {
 
157
                        continue
 
158
                }
 
159
                if err := step.Run(); err != nil {
 
160
                        return &upgradeError{
 
161
                                description: step.Description(),
 
162
                                err:         err,
 
163
                        }
 
164
                }
 
165
        }
 
166
        return nil
 
167
}