1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
13
"github.com/juju/errors"
14
jc "github.com/juju/testing/checkers"
15
"github.com/juju/utils"
16
"github.com/juju/utils/arch"
17
"github.com/juju/utils/series"
18
"github.com/juju/utils/symlink"
19
"github.com/juju/version"
20
gc "gopkg.in/check.v1"
21
"gopkg.in/juju/names.v2"
23
"github.com/juju/juju/agent"
24
agenttools "github.com/juju/juju/agent/tools"
25
"github.com/juju/juju/api"
26
envtesting "github.com/juju/juju/environs/testing"
27
envtools "github.com/juju/juju/environs/tools"
28
jujutesting "github.com/juju/juju/juju/testing"
29
"github.com/juju/juju/state"
30
statetesting "github.com/juju/juju/state/testing"
31
coretesting "github.com/juju/juju/testing"
32
coretools "github.com/juju/juju/tools"
33
jujuversion "github.com/juju/juju/version"
34
"github.com/juju/juju/worker/gate"
35
"github.com/juju/juju/worker/upgrader"
38
func TestPackage(t *stdtesting.T) {
39
coretesting.MgoTestPackage(t)
42
type UpgraderSuite struct {
43
jujutesting.JujuConnSuite
45
machine *state.Machine
47
oldRetryAfter func() <-chan time.Time
48
confVersion version.Number
49
upgradeStepsComplete gate.Lock
50
initialCheckComplete gate.Lock
53
type AllowedTargetVersionSuite struct{}
55
var _ = gc.Suite(&UpgraderSuite{})
56
var _ = gc.Suite(&AllowedTargetVersionSuite{})
58
func (s *UpgraderSuite) SetUpTest(c *gc.C) {
59
s.JujuConnSuite.SetUpTest(c)
60
// s.machine needs to have IsManager() so that it can get the actual
61
// current revision to upgrade to.
62
s.state, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageModel)
63
// Capture the value of RetryAfter, and use that captured
64
// value in the cleanup lambda.
65
oldRetryAfter := *upgrader.RetryAfter
66
s.AddCleanup(func(*gc.C) {
67
*upgrader.RetryAfter = oldRetryAfter
69
s.upgradeStepsComplete = gate.NewLock()
70
s.initialCheckComplete = gate.NewLock()
73
func (s *UpgraderSuite) patchVersion(v version.Binary) {
74
s.PatchValue(&arch.HostArch, func() string { return v.Arch })
75
s.PatchValue(&series.HostSeries, func() string { return v.Series })
76
s.PatchValue(&jujuversion.Current, v.Number)
79
type mockConfig struct {
83
version version.Number
86
func (mock *mockConfig) Tag() names.Tag {
90
func (mock *mockConfig) DataDir() string {
94
func agentConfig(tag names.Tag, datadir string) agent.Config {
101
func (s *UpgraderSuite) makeUpgrader(c *gc.C) *upgrader.Upgrader {
102
w, err := upgrader.NewAgentUpgrader(
104
agentConfig(s.machine.Tag(), s.DataDir()),
106
s.upgradeStepsComplete,
107
s.initialCheckComplete,
109
c.Assert(err, jc.ErrorIsNil)
113
func (s *UpgraderSuite) TestUpgraderSetsTools(c *gc.C) {
114
vers := version.MustParseBinary("5.4.3-precise-amd64")
115
err := statetesting.SetAgentVersion(s.State, vers.Number)
116
c.Assert(err, jc.ErrorIsNil)
117
stor := s.DefaultToolsStorage
118
agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), vers)
119
s.patchVersion(agentTools.Version)
120
err = envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{agentTools}, envtools.DoNotWriteMirrors)
121
_, err = s.machine.AgentTools()
122
c.Assert(err, jc.Satisfies, errors.IsNotFound)
124
u := s.makeUpgrader(c)
125
statetesting.AssertStop(c, u)
126
s.expectInitialUpgradeCheckDone(c)
128
gotTools, err := s.machine.AgentTools()
129
c.Assert(err, jc.ErrorIsNil)
130
envtesting.CheckTools(c, gotTools, agentTools)
133
func (s *UpgraderSuite) TestUpgraderSetVersion(c *gc.C) {
134
vers := version.MustParseBinary("5.4.3-precise-amd64")
135
agentTools := envtesting.PrimeTools(c, s.DefaultToolsStorage, s.DataDir(), s.Environ.Config().AgentStream(), vers)
136
s.patchVersion(agentTools.Version)
137
err := os.RemoveAll(filepath.Join(s.DataDir(), "tools"))
138
c.Assert(err, jc.ErrorIsNil)
140
_, err = s.machine.AgentTools()
141
c.Assert(err, jc.Satisfies, errors.IsNotFound)
142
err = statetesting.SetAgentVersion(s.State, vers.Number)
143
c.Assert(err, jc.ErrorIsNil)
145
u := s.makeUpgrader(c)
146
statetesting.AssertStop(c, u)
147
s.expectInitialUpgradeCheckDone(c)
149
gotTools, err := s.machine.AgentTools()
150
c.Assert(err, jc.ErrorIsNil)
151
c.Assert(gotTools, gc.DeepEquals, &coretools.Tools{Version: vers})
154
func (s *UpgraderSuite) expectInitialUpgradeCheckDone(c *gc.C) {
155
c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsTrue)
158
func (s *UpgraderSuite) expectInitialUpgradeCheckNotDone(c *gc.C) {
159
c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsFalse)
162
func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) {
163
stor := s.DefaultToolsStorage
164
oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
165
s.patchVersion(oldTools.Version)
166
newTools := envtesting.AssertUploadFakeToolsVersions(
167
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0]
168
err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
169
c.Assert(err, jc.ErrorIsNil)
171
u := s.makeUpgrader(c)
173
s.expectInitialUpgradeCheckNotDone(c)
174
envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
175
AgentName: s.machine.Tag().String(),
176
OldTools: oldTools.Version,
177
NewTools: newTools.Version,
178
DataDir: s.DataDir(),
180
foundTools, err := agenttools.ReadTools(s.DataDir(), newTools.Version)
181
c.Assert(err, jc.ErrorIsNil)
182
newTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.5-precise-amd64",
183
s.APIState.Addr(), coretesting.ModelTag.Id())
184
envtesting.CheckTools(c, foundTools, newTools)
187
func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) {
188
stor := s.DefaultToolsStorage
189
oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
190
s.patchVersion(oldTools.Version)
191
newTools := envtesting.AssertUploadFakeToolsVersions(
192
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0]
193
err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
194
c.Assert(err, jc.ErrorIsNil)
196
retryc := make(chan time.Time)
197
*upgrader.RetryAfter = func() <-chan time.Time {
198
c.Logf("replacement retry after")
201
err = stor.Remove(envtools.StorageName(newTools.Version, "released"))
202
c.Assert(err, jc.ErrorIsNil)
203
u := s.makeUpgrader(c)
205
s.expectInitialUpgradeCheckNotDone(c)
207
for i := 0; i < 3; i++ {
209
case retryc <- time.Now():
210
case <-time.After(coretesting.LongWait):
211
c.Fatalf("upgrader did not retry (attempt %d)", i)
215
// Make it upgrade to some newer tools that can be
216
// downloaded ok; it should stop retrying, download
217
// the newer tools and exit.
218
newerTools := envtesting.AssertUploadFakeToolsVersions(
219
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.6-precise-amd64"))[0]
221
err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number)
222
c.Assert(err, jc.ErrorIsNil)
224
s.BackingState.StartSync()
225
done := make(chan error)
231
envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
232
AgentName: s.machine.Tag().String(),
233
OldTools: oldTools.Version,
234
NewTools: newerTools.Version,
235
DataDir: s.DataDir(),
237
case <-time.After(coretesting.LongWait):
238
c.Fatalf("upgrader did not quit after upgrading")
242
func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) {
243
oldTools := &coretools.Tools{
244
Version: version.MustParseBinary("1.2.3-quantal-amd64"),
246
stor := s.DefaultToolsStorage
247
newToolsBinary := "5.4.3-precise-amd64"
248
newTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary(newToolsBinary))
249
s.patchVersion(newTools.Version)
250
err := envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{newTools}, envtools.DoNotWriteMirrors)
251
c.Assert(err, jc.ErrorIsNil)
252
ugErr := &upgrader.UpgradeReadyError{
253
AgentName: "anAgent",
254
OldTools: oldTools.Version,
255
NewTools: newTools.Version,
256
DataDir: s.DataDir(),
258
err = ugErr.ChangeAgentTools()
259
c.Assert(err, jc.ErrorIsNil)
260
target := agenttools.ToolsDir(s.DataDir(), newToolsBinary)
261
link, err := symlink.Read(agenttools.ToolsDir(s.DataDir(), "anAgent"))
262
c.Assert(err, jc.ErrorIsNil)
263
c.Assert(link, jc.SamePath, target)
266
func (s *UpgraderSuite) TestUsesAlreadyDownloadedToolsIfAvailable(c *gc.C) {
267
oldVersion := version.MustParseBinary("1.2.3-quantal-amd64")
268
s.patchVersion(oldVersion)
270
newVersion := version.MustParseBinary("5.4.3-quantal-amd64")
271
err := statetesting.SetAgentVersion(s.State, newVersion.Number)
272
c.Assert(err, jc.ErrorIsNil)
274
// Install tools matching the new version in the data directory
275
// but *not* in environment storage. The upgrader should find the
276
// downloaded tools without looking in environment storage.
277
envtesting.InstallFakeDownloadedTools(c, s.DataDir(), newVersion)
279
u := s.makeUpgrader(c)
281
s.expectInitialUpgradeCheckNotDone(c)
283
envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
284
AgentName: s.machine.Tag().String(),
285
OldTools: oldVersion,
286
NewTools: newVersion,
287
DataDir: s.DataDir(),
291
func (s *UpgraderSuite) TestUpgraderRefusesToDowngradeMinorVersions(c *gc.C) {
292
stor := s.DefaultToolsStorage
293
origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
294
s.patchVersion(origTools.Version)
295
downgradeTools := envtesting.AssertUploadFakeToolsVersions(
296
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.3.3-precise-amd64"))[0]
297
err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
298
c.Assert(err, jc.ErrorIsNil)
300
u := s.makeUpgrader(c)
302
s.expectInitialUpgradeCheckDone(c)
303
// If the upgrade would have triggered, we would have gotten an
304
// UpgradeReadyError, since it was skipped, we get no error
305
c.Check(err, jc.ErrorIsNil)
306
_, err = agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
307
// TODO: ReadTools *should* be returning some form of errors.NotFound,
308
// however, it just passes back a fmt.Errorf so we live with it
309
// c.Assert(err, jc.Satisfies, errors.IsNotFound)
310
c.Check(err, gc.ErrorMatches, "cannot read tools metadata in tools directory.*"+utils.NoSuchFileErrRegexp)
313
func (s *UpgraderSuite) TestUpgraderAllowsDowngradingPatchVersions(c *gc.C) {
314
stor := s.DefaultToolsStorage
315
origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
316
s.patchVersion(origTools.Version)
317
downgradeTools := envtesting.AssertUploadFakeToolsVersions(
318
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.2-precise-amd64"))[0]
319
err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number)
320
c.Assert(err, jc.ErrorIsNil)
322
u := s.makeUpgrader(c)
324
s.expectInitialUpgradeCheckNotDone(c)
325
envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
326
AgentName: s.machine.Tag().String(),
327
OldTools: origTools.Version,
328
NewTools: downgradeTools.Version,
329
DataDir: s.DataDir(),
331
foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
332
c.Assert(err, jc.ErrorIsNil)
333
downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.2-precise-amd64",
334
s.APIState.Addr(), coretesting.ModelTag.Id())
335
envtesting.CheckTools(c, foundTools, downgradeTools)
338
func (s *UpgraderSuite) TestUpgraderAllowsDowngradeToOrigVersionIfUpgradeInProgress(c *gc.C) {
339
// note: otherwise illegal version jump
340
downgradeVersion := version.MustParseBinary("5.3.0-precise-amd64")
341
s.confVersion = downgradeVersion.Number
343
stor := s.DefaultToolsStorage
344
origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
345
s.patchVersion(origTools.Version)
346
downgradeTools := envtesting.AssertUploadFakeToolsVersions(
347
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)[0]
348
err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number)
349
c.Assert(err, jc.ErrorIsNil)
351
u := s.makeUpgrader(c)
353
s.expectInitialUpgradeCheckNotDone(c)
354
envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{
355
AgentName: s.machine.Tag().String(),
356
OldTools: origTools.Version,
357
NewTools: downgradeVersion,
358
DataDir: s.DataDir(),
360
foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version)
361
c.Assert(err, jc.ErrorIsNil)
362
downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.3.0-precise-amd64",
363
s.APIState.Addr(), coretesting.ModelTag.Id())
364
envtesting.CheckTools(c, foundTools, downgradeTools)
367
func (s *UpgraderSuite) TestUpgraderRefusesDowngradeToOrigVersionIfUpgradeNotInProgress(c *gc.C) {
368
downgradeVersion := version.MustParseBinary("5.3.0-precise-amd64")
369
s.confVersion = downgradeVersion.Number
370
s.upgradeStepsComplete.Unlock()
372
stor := s.DefaultToolsStorage
373
origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64"))
374
s.patchVersion(origTools.Version)
375
envtesting.AssertUploadFakeToolsVersions(
376
c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)
377
err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number)
378
c.Assert(err, jc.ErrorIsNil)
380
u := s.makeUpgrader(c)
382
s.expectInitialUpgradeCheckDone(c)
384
// If the upgrade would have triggered, we would have gotten an
385
// UpgradeReadyError, since it was skipped, we get no error
386
c.Check(err, jc.ErrorIsNil)
389
type allowedTest struct {
397
func (s *AllowedTargetVersionSuite) TestAllowedTargetVersionSuite(c *gc.C) {
398
cases := []allowedTest{
399
{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.3.3", allowed: true},
400
{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.2.3", allowed: true},
401
{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "2.2.3", allowed: true},
402
{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.1.3", allowed: false},
403
{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.2.2", allowed: true}, // downgrade between builds
404
{original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "0.2.3", allowed: false},
405
{original: "0.2.3", current: "1.2.3", upgradeRunning: false, target: "0.2.3", allowed: false},
406
{original: "0.2.3", current: "1.2.3", upgradeRunning: true, target: "0.2.3", allowed: true}, // downgrade during upgrade
408
for i, test := range cases {
409
c.Logf("test case %d, %#v", i, test)
410
original := version.MustParse(test.original)
411
current := version.MustParse(test.current)
412
target := version.MustParse(test.target)
413
result := upgrader.AllowedTargetVersion(original, current, test.upgradeRunning, target)
414
c.Check(result, gc.Equals, test.allowed)