Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(502)

Delta Between Two Patch Sets: cmd/juju/upgradejuju.go

Issue 8663045: upgrade-juju: improvements
Left Patch Set: Created 11 years, 12 months ago
Right Patch Set: upgrade-juju: improvements Created 11 years, 11 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « cmd/juju/synctools.go ('k') | cmd/juju/upgradejuju_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 package main 1 package main
2 2
3 import ( 3 import (
4 "errors" 4 "errors"
5 "fmt" 5 "fmt"
6 "launchpad.net/gnuflag" 6 "launchpad.net/gnuflag"
7 "launchpad.net/juju-core/cmd" 7 "launchpad.net/juju-core/cmd"
8 "launchpad.net/juju-core/environs" 8 "launchpad.net/juju-core/environs"
9 "launchpad.net/juju-core/environs/config" 9 "launchpad.net/juju-core/environs/config"
10 "launchpad.net/juju-core/environs/tools" 10 "launchpad.net/juju-core/environs/tools"
11 "launchpad.net/juju-core/juju" 11 "launchpad.net/juju-core/juju"
12 "launchpad.net/juju-core/log" 12 "launchpad.net/juju-core/log"
13 "launchpad.net/juju-core/version" 13 "launchpad.net/juju-core/version"
14 ) 14 )
15 15
16 // UpgradeJujuCommand upgrades the agents in a juju installation. 16 // UpgradeJujuCommand upgrades the agents in a juju installation.
17 type UpgradeJujuCommand struct { 17 type UpgradeJujuCommand struct {
18 EnvCommandBase 18 EnvCommandBase
19 vers string 19 vers string
20 Version version.Number 20 Version version.Number
21 Development bool 21 Development bool
22 UploadTools bool 22 UploadTools bool
23 Series []string 23 Series []string
24 } 24 }
25 25
26 var uploadTools = tools.Upload 26 var uploadTools = tools.Upload
27 27
28 var upgradeJujuDoc = `
29 The upgrade-juju command upgrades a running environment by setting a version
30 number for all juju agents to run. By default, it chooses the most recent non-
31 development version compatible with the command-line tools.
32
33 A development version is defined to be any version with an odd minor version
34 or a nonzero build component (for example version 2.1.1, 3.3.0 and 2.0.0.1 are
35 development versions; 2.0.3 and 3.4.1 are not). A development version may be
36 chosen if any of the following conditions hold:
37
38 * the current juju tool has a development version.
39 * the juju environment has a development version
40 * the environment "development" setting is true
41 * the --dev flag is specified
42
43 For development use, the --upload-tools flag specifies that the juju tools will
44 be compiled locally and uploaded before the version is set. Currently the tools
45 will be uploaded as if they had the version of the current juju tool, unless
46 specified otherwise by the --version flag.
47 `[1:]
48
28 func (c *UpgradeJujuCommand) Info() *cmd.Info { 49 func (c *UpgradeJujuCommand) Info() *cmd.Info {
29 return &cmd.Info{ 50 return &cmd.Info{
30 Name: "upgrade-juju", 51 Name: "upgrade-juju",
31 Purpose: "upgrade the tools in a juju environment", 52 Purpose: "upgrade the tools in a juju environment",
53 Doc: upgradeJujuDoc,
32 } 54 }
33 } 55 }
34 56
35 func (c *UpgradeJujuCommand) SetFlags(f *gnuflag.FlagSet) { 57 func (c *UpgradeJujuCommand) SetFlags(f *gnuflag.FlagSet) {
36 c.EnvCommandBase.SetFlags(f) 58 c.EnvCommandBase.SetFlags(f)
37 » f.StringVar(&c.vers, "version", "", "version to upgrade to (defaults to highest available version with the current major version number)") 59 » f.StringVar(&c.vers, "version", "", "upgrade to specific version")
38 f.BoolVar(&c.Development, "dev", false, "allow development versions to b e chosen") 60 f.BoolVar(&c.Development, "dev", false, "allow development versions to b e chosen")
39 f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version o f tools") 61 f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version o f tools")
40 » f.Var(seriesVar{&c.Series}, "series", "upload tools for supplied comma-s eparated series") 62 » f.Var(seriesVar{&c.Series}, "series", "upload tools for supplied comma-s eparated series list")
41 } 63 }
42 64
43 func (c *UpgradeJujuCommand) Init(args []string) error { 65 func (c *UpgradeJujuCommand) Init(args []string) error {
44 if c.vers != "" { 66 if c.vers != "" {
45 vers, err := version.Parse(c.vers) 67 vers, err := version.Parse(c.vers)
46 if err != nil { 68 if err != nil {
47 return err 69 return err
48 } 70 }
49 if vers.Major != version.Current.Major { 71 if vers.Major != version.Current.Major {
50 return fmt.Errorf("cannot upgrade to version incompatibl e with CLI") 72 return fmt.Errorf("cannot upgrade to version incompatibl e with CLI")
51 } 73 }
52 if c.UploadTools && vers.Build != 0 { 74 if c.UploadTools && vers.Build != 0 {
75 // TODO(fwereade): when we start taking versions from ac tual built
76 // code, we should disable --version when used with --up load-tools.
77 // For now, it's the only way to experiment with version upgrade
78 // behaviour live, so the only restriction is that Build cannot
79 // be used (because its value needs to be chosen interna lly so as
80 // not to collide with existing tools).
53 return fmt.Errorf("cannot specify build number when uplo ading tools") 81 return fmt.Errorf("cannot specify build number when uplo ading tools")
54 } 82 }
55 c.Version = vers 83 c.Version = vers
56 } 84 }
57 if len(c.Series) > 0 && !c.UploadTools { 85 if len(c.Series) > 0 && !c.UploadTools {
58 return fmt.Errorf("--series requires --upload-tools") 86 return fmt.Errorf("--series requires --upload-tools")
59 } 87 }
60 return cmd.CheckEmpty(args) 88 return cmd.CheckEmpty(args)
61 } 89 }
62 90
(...skipping 22 matching lines...) Expand all
85 v, err := c.initVersions(cfg, env) 113 v, err := c.initVersions(cfg, env)
86 if err != nil { 114 if err != nil {
87 return err 115 return err
88 } 116 }
89 if c.UploadTools { 117 if c.UploadTools {
90 series := getUploadSeries(cfg, c.Series) 118 series := getUploadSeries(cfg, c.Series)
91 if err := v.uploadTools(env.Storage(), series); err != nil { 119 if err := v.uploadTools(env.Storage(), series); err != nil {
92 return err 120 return err
93 } 121 }
94 } 122 }
95 » if err := v.validate(c.Development); err != nil { 123 » if err := v.validate(); err != nil {
96 return err 124 return err
97 } 125 }
98 log.Infof("upgrade version chosen: %s", v.chosen) 126 log.Infof("upgrade version chosen: %s", v.chosen)
99 // TODO(fwereade): this list may be incomplete, pending tools.Upload cha nge. 127 // TODO(fwereade): this list may be incomplete, pending tools.Upload cha nge.
100 log.Infof("available tools: %s", v.tools) 128 log.Infof("available tools: %s", v.tools)
101 129
102 // Write updated config back to state if necessary. Note that this is 130 // Write updated config back to state if necessary. Note that this is
103 // crackful and racy, because we have no idea what incompatible agent- 131 // crackful and racy, because we have no idea what incompatible agent-
104 // version might be set by another administrator in the meantime. If 132 // version might be set by another administrator in the meantime. If
105 // this happens, tough: I'm not going to pretend to do it right when 133 // this happens, tough: I'm not going to pretend to do it right when
106 // I'm not. 134 // I'm not.
107 // TODO(fwereade): Do this right. Warning: scope unclear. 135 // TODO(fwereade): Do this right. Warning: scope unclear.
108 » // TODO(fwereade): I don't think Config.Development does anything very 136 » cfg, err = cfg.Apply(map[string]interface{}{
109 » // useful. Preserved behaviour just in case.
110 » if cfg, err = cfg.Apply(map[string]interface{}{
111 "agent-version": v.chosen.String(), 137 "agent-version": v.chosen.String(),
112 » » "development": c.Development, 138 » })
113 » }); err != nil { 139 » if err != nil {
114 return err 140 return err
115 } 141 }
116 if err := conn.State.SetEnvironConfig(cfg); err != nil { 142 if err := conn.State.SetEnvironConfig(cfg); err != nil {
117 return err 143 return err
118 } 144 }
119 log.Noticef("started upgrade to %s", v.chosen) 145 log.Noticef("started upgrade to %s", v.chosen)
120 return nil 146 return nil
121 } 147 }
122 148
123 // initVersions collects state relevant to an upgrade decision. The returned 149 // initVersions collects state relevant to an upgrade decision. The returned
124 // agent and client versions, and the list of currently available tools, will 150 // agent and client versions, and the list of currently available tools, will
125 // always be accurate; the chosen vesion may remain blank until uploadTools 151 // always be accurate; the chosen version, and the flag indicating development
126 // or validate is called. 152 // mode, may remain blank until uploadTools or validate is called.
127 func (c *UpgradeJujuCommand) initVersions(cfg *config.Config, env environs.Envir on) (*upgradeVersions, error) { 153 func (c *UpgradeJujuCommand) initVersions(cfg *config.Config, env environs.Envir on) (*upgradeVersions, error) {
128 agent, ok := cfg.AgentVersion() 154 agent, ok := cfg.AgentVersion()
129 if !ok { 155 if !ok {
130 // Can't happen. In theory. 156 // Can't happen. In theory.
131 return nil, fmt.Errorf("incomplete environment configuration") 157 return nil, fmt.Errorf("incomplete environment configuration")
132 } 158 }
133 if c.Version == agent { 159 if c.Version == agent {
134 return nil, errUpToDate 160 return nil, errUpToDate
135 } 161 }
136 client := version.Current.Number 162 client := version.Current.Number
137 available, err := environs.FindAvailableTools(env, client.Major) 163 available, err := environs.FindAvailableTools(env, client.Major)
138 » switch err { 164 » if err != nil {
139 » case nil, tools.ErrNoMatches, tools.ErrNoTools: 165 » » if _, missing := err.(*environs.NotFoundError); !missing {
140 » » if err != nil && !c.UploadTools { 166 » » » return nil, err
167 » » }
168 » » if !c.UploadTools {
141 if c.Version == version.Zero { 169 if c.Version == version.Zero {
142 return nil, errUpToDate 170 return nil, errUpToDate
143 } 171 }
144 return nil, err 172 return nil, err
145 } 173 }
146 » default: 174 » }
147 » » return nil, err 175 » dev := c.Development || cfg.Development() || agent.IsDev() || client.IsD ev()
148 » }
149 return &upgradeVersions{ 176 return &upgradeVersions{
177 dev: dev,
150 agent: agent, 178 agent: agent,
151 client: client, 179 client: client,
152 chosen: c.Version, 180 chosen: c.Version,
153 tools: available, 181 tools: available,
154 }, nil 182 }, nil
155 } 183 }
156 184
157 // upgradeVersions holds the version information for making upgrade decisions. 185 // upgradeVersions holds the version information for making upgrade decisions.
158 type upgradeVersions struct { 186 type upgradeVersions struct {
187 dev bool
159 agent version.Number 188 agent version.Number
160 client version.Number 189 client version.Number
161 chosen version.Number 190 chosen version.Number
162 tools tools.List 191 tools tools.List
163 } 192 }
164 193
165 // uploadTools compiles jujud from $GOPATH and uploads it into the supplied 194 // uploadTools compiles jujud from $GOPATH and uploads it into the supplied
166 // storage. If no version has been explicitly chosen, the version number 195 // storage. If no version has been explicitly chosen, the version number
167 // reported by the built tools will be based on the client version number. 196 // reported by the built tools will be based on the client version number.
168 // In any case, the version number reported will have a build component higher 197 // In any case, the version number reported will have a build component higher
169 // than that of any otherwise-matching available tools. 198 // than that of any otherwise-matching available tools.
170 // uploadTools resets the chosen version and replaces the available tools 199 // uploadTools resets the chosen version and replaces the available tools
171 // with the ones just uploaded. 200 // with the ones just uploaded.
172 func (v *upgradeVersions) uploadTools(storage environs.Storage, series []string) error { 201 func (v *upgradeVersions) uploadTools(storage environs.Storage, series []string) error {
173 // TODO(fwereade): this is kinda crack: we should not assume that 202 // TODO(fwereade): this is kinda crack: we should not assume that
174 // version.Current matches whatever source happens to be built. The 203 // version.Current matches whatever source happens to be built. The
175 // ideal would be: 204 // ideal would be:
176 // 1) compile jujud from $GOPATH into some build dir 205 // 1) compile jujud from $GOPATH into some build dir
177 // 2) get actual version with `jujud version` 206 // 2) get actual version with `jujud version`
178 // 3) check actual version for compatibility with CLI tools 207 // 3) check actual version for compatibility with CLI tools
179 // 4) generate unique build version with reference to available tools 208 // 4) generate unique build version with reference to available tools
180 // 5) force-version that unique version into the dir directly 209 // 5) force-version that unique version into the dir directly
181 // 6) archive and upload the build dir 210 // 6) archive and upload the build dir
182 // ...but there's no way we have time for that now. In the meantime, 211 // ...but there's no way we have time for that now. In the meantime,
183 // considering the use cases, this should work well enough; but it 212 // considering the use cases, this should work well enough; but it
184 // won't detect an incompatible major-version change, which is a shame. 213 // won't detect an incompatible major-version change, which is a shame.
185 if v.chosen == version.Zero { 214 if v.chosen == version.Zero {
186 v.chosen = v.client 215 v.chosen = v.client
187 } 216 }
188 » v.chosen = uniqueVersion(v.chosen, v.tools) 217 » v.chosen = uploadVersion(v.chosen, v.tools)
189 218
190 // TODO(fwereade): tools.Upload should return a tools.List, and should 219 // TODO(fwereade): tools.Upload should return a tools.List, and should
191 // include all the extra series we build, so we can set *that* onto 220 // include all the extra series we build, so we can set *that* onto
192 // v.available and maybe one day be able to check that a given upgrade 221 // v.available and maybe one day be able to check that a given upgrade
193 // won't leave out-of-date machines lying around, starved of tools. 222 // won't leave out-of-date machines lying around, starved of tools.
194 uploaded, err := uploadTools(storage, &v.chosen, series...) 223 uploaded, err := uploadTools(storage, &v.chosen, series...)
195 if err != nil { 224 if err != nil {
196 return err 225 return err
197 } 226 }
198 v.tools = tools.List{uploaded} 227 v.tools = tools.List{uploaded}
199 return nil 228 return nil
200 } 229 }
201 230
202 // validate chooses an upgrade version, of one has not already been chosen, 231 // validate chooses an upgrade version, if one has not already been chosen,
203 // and ensures the tools list contains no entries that do not have that version. 232 // and ensures the tools list contains no entries that do not have that version.
204 // If validate returns no error, the environment agent-version can be set to 233 // If validate returns no error, the environment agent-version can be set to
205 // the value of the chosen field. 234 // the value of the chosen field.
206 func (v *upgradeVersions) validate(dev bool) (err error) { 235 func (v *upgradeVersions) validate() (err error) {
207 // If not completely specified already, pick a single tools version. 236 // If not completely specified already, pick a single tools version.
208 » dev = dev || v.agent.IsDev() || v.client.IsDev() || v.chosen.IsDev() 237 » v.dev = v.dev || v.chosen.IsDev()
209 » filter := tools.Filter{Number: v.chosen, Released: !dev} 238 » filter := tools.Filter{Number: v.chosen, Released: !v.dev}
210 if v.tools, err = v.tools.Match(filter); err != nil { 239 if v.tools, err = v.tools.Match(filter); err != nil {
211 return err 240 return err
212 } 241 }
213 » v.tools, v.chosen = v.tools.Newest() 242 » v.chosen, v.tools = v.tools.Newest()
214 if v.chosen == v.agent { 243 if v.chosen == v.agent {
215 return errUpToDate 244 return errUpToDate
216 } 245 }
217 246
218 // Major version upgrade 247 // Major version upgrade
219 if v.chosen.Major < v.agent.Major { 248 if v.chosen.Major < v.agent.Major {
220 // TODO(fwereade): I'm a bit concerned about old agent/CLI tools even 249 // TODO(fwereade): I'm a bit concerned about old agent/CLI tools even
221 // *connecting* to environments with higher agent-versions; but ofc they 250 // *connecting* to environments with higher agent-versions; but ofc they
222 // have to connect in order to discover they shouldn't. However, once 251 // have to connect in order to discover they shouldn't. However, once
223 // any of our tools detect an incompatible version, they should act to 252 // any of our tools detect an incompatible version, they should act to
224 // minimize damage: the CLI should abort politely, and the agent s should 253 // minimize damage: the CLI should abort politely, and the agent s should
225 » » // run an upgrader but no other tasks. 254 » » // run an Upgrader but no other tasks.
226 return fmt.Errorf("cannot change major version from %d to %d", v .agent.Major, v.chosen.Major) 255 return fmt.Errorf("cannot change major version from %d to %d", v .agent.Major, v.chosen.Major)
227 } else if v.chosen.Major > v.agent.Major { 256 } else if v.chosen.Major > v.agent.Major {
228 return fmt.Errorf("major version upgrades are not supported yet" ) 257 return fmt.Errorf("major version upgrades are not supported yet" )
229 } 258 }
230 259
231 return nil 260 return nil
232 } 261 }
233 262
234 // uniqueVersion returns a copy of the supplied version with a build number 263 // uploadVersion returns a copy of the supplied version with a build number
235 // higher than any tools in existing that share its major, minor and patch. 264 // higher than any of the supplied tools that share its major, minor and patch.
236 func uniqueVersion(vers version.Number, existing tools.List) version.Number { 265 func uploadVersion(vers version.Number, existing tools.List) version.Number {
237 for _, t := range existing { 266 for _, t := range existing {
238 if t.Major != vers.Major || t.Minor != vers.Minor || t.Patch != vers.Patch { 267 if t.Major != vers.Major || t.Minor != vers.Minor || t.Patch != vers.Patch {
239 continue 268 continue
240 } 269 }
241 if t.Build >= vers.Build { 270 if t.Build >= vers.Build {
242 vers.Build = t.Build + 1 271 vers.Build = t.Build + 1
243 } 272 }
244 } 273 }
245 return vers 274 return vers
246 } 275 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b