github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/upgrades/upgrade.go (about) 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 "github.com/juju/loggo" 10 11 "github.com/juju/juju/agent" 12 "github.com/juju/juju/state" 13 "github.com/juju/juju/state/api" 14 "github.com/juju/juju/version" 15 ) 16 17 var logger = loggo.GetLogger("juju.upgrade") 18 19 // Step defines an idempotent operation that is run to perform 20 // a specific upgrade step. 21 type Step interface { 22 // Description is a human readable description of what the upgrade step does. 23 Description() string 24 25 // Targets returns the target machine types for which the upgrade step is applicable. 26 Targets() []Target 27 28 // Run executes the upgrade business logic. 29 Run(context Context) error 30 } 31 32 // Operation defines what steps to perform to upgrade to a target version. 33 type Operation interface { 34 // The Juju version for which this operation is applicable. 35 // Upgrade operations designed for versions of Juju earlier 36 // than we are upgrading from are not run since such steps would 37 // already have been used to get to the version we are running now. 38 TargetVersion() version.Number 39 40 // Steps to perform during an upgrade. 41 Steps() []Step 42 } 43 44 // Target defines the type of machine for which a particular upgrade 45 // step can be run. 46 type Target string 47 48 const ( 49 // AllMachines applies to any machine. 50 AllMachines = Target("allMachines") 51 52 // HostMachine is a machine on which units are deployed. 53 HostMachine = Target("hostMachine") 54 55 // StateServer is a machine participating in a Juju state server cluster. 56 StateServer = Target("stateServer") 57 ) 58 59 // upgradeToVersion encapsulates the steps which need to be run to 60 // upgrade any prior version of Juju to targetVersion. 61 type upgradeToVersion struct { 62 targetVersion version.Number 63 steps []Step 64 } 65 66 // Steps is defined on the Operation interface. 67 func (u upgradeToVersion) Steps() []Step { 68 return u.steps 69 } 70 71 // TargetVersion is defined on the Operation interface. 72 func (u upgradeToVersion) TargetVersion() version.Number { 73 return u.targetVersion 74 } 75 76 // Context is used give the upgrade steps attributes needed 77 // to do their job. 78 type Context interface { 79 // APIState returns an API connection to state. 80 APIState() *api.State 81 82 // State returns a connection to state. This will be non-nil 83 // only in the context of a state server. 84 State() *state.State 85 86 // AgentConfig returns the agent config for the machine that is being 87 // upgraded. 88 AgentConfig() agent.ConfigSetter 89 } 90 91 // upgradeContext is a default Context implementation. 92 type upgradeContext struct { 93 // Work in progress........ 94 // Exactly what a context needs is to be determined as the 95 // implementation evolves. 96 api *api.State 97 st *state.State 98 agentConfig agent.ConfigSetter 99 } 100 101 // APIState is defined on the Context interface. 102 func (c *upgradeContext) APIState() *api.State { 103 return c.api 104 } 105 106 // State is defined on the Context interface. 107 func (c *upgradeContext) State() *state.State { 108 return c.st 109 } 110 111 // AgentConfig is defined on the Context interface. 112 func (c *upgradeContext) AgentConfig() agent.ConfigSetter { 113 return c.agentConfig 114 } 115 116 // NewContext returns a new upgrade context. 117 func NewContext(agentConfig agent.ConfigSetter, api *api.State, st *state.State) Context { 118 return &upgradeContext{ 119 api: api, 120 st: st, 121 agentConfig: agentConfig, 122 } 123 } 124 125 // upgradeError records a description of the step being performed and the error. 126 type upgradeError struct { 127 description string 128 err error 129 } 130 131 func (e *upgradeError) Error() string { 132 return fmt.Sprintf("%s: %v", e.description, e.err) 133 } 134 135 // PerformUpgrade runs the business logic needed to upgrade the current "from" version to this 136 // version of Juju on the "target" type of machine. 137 func PerformUpgrade(from version.Number, target Target, context Context) error { 138 // If from is not known, it is 1.16. 139 if from == version.Zero { 140 from = version.MustParse("1.16.0") 141 } 142 for _, upgradeOps := range upgradeOperations() { 143 targetVersion := upgradeOps.TargetVersion() 144 // Do not run steps for versions of Juju earlier or same as we are upgrading from. 145 if targetVersion.Compare(from) <= 0 { 146 continue 147 } 148 // Do not run steps for versions of Juju later than we are upgrading to. 149 if targetVersion.Compare(version.Current.Number) > 0 { 150 continue 151 } 152 if err := runUpgradeSteps(context, target, upgradeOps); err != nil { 153 return err 154 } 155 } 156 return nil 157 } 158 159 // validTarget returns true if target is in step.Targets(). 160 func validTarget(target Target, step Step) bool { 161 for _, opTarget := range step.Targets() { 162 if opTarget == AllMachines || target == opTarget { 163 return true 164 } 165 } 166 return len(step.Targets()) == 0 167 } 168 169 // runUpgradeSteps runs all the upgrade steps relevant to target. 170 // As soon as any error is encountered, the operation is aborted since 171 // subsequent steps may required successful completion of earlier ones. 172 // The steps must be idempotent so that the entire upgrade operation can 173 // be retried. 174 func runUpgradeSteps(context Context, target Target, upgradeOp Operation) *upgradeError { 175 for _, step := range upgradeOp.Steps() { 176 if !validTarget(target, step) { 177 continue 178 } 179 logger.Infof("running upgrade step on target %q: %v", target, step.Description()) 180 if err := step.Run(context); err != nil { 181 logger.Errorf("upgrade step %q failed: %v", step.Description(), err) 182 return &upgradeError{ 183 description: step.Description(), 184 err: err, 185 } 186 } 187 } 188 logger.Infof("All upgrade steps completed successfully") 189 return nil 190 } 191 192 type upgradeStep struct { 193 description string 194 targets []Target 195 run func(Context) error 196 } 197 198 // Description is defined on the Step interface. 199 func (step *upgradeStep) Description() string { 200 return step.description 201 } 202 203 // Targets is defined on the Step interface. 204 func (step *upgradeStep) Targets() []Target { 205 return step.targets 206 } 207 208 // Run is defined on the Step interface. 209 func (step *upgradeStep) Run(context Context) error { 210 return step.run(context) 211 }