~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/uniter/charm/manifest_deployer_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012-2014 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package charm_test
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "path/filepath"
 
9
        "runtime"
 
10
 
 
11
        jc "github.com/juju/testing/checkers"
 
12
        ft "github.com/juju/testing/filetesting"
 
13
        "github.com/juju/utils/set"
 
14
        gc "gopkg.in/check.v1"
 
15
 
 
16
        "github.com/juju/juju/testing"
 
17
        "github.com/juju/juju/worker/uniter/charm"
 
18
)
 
19
 
 
20
type ManifestDeployerSuite struct {
 
21
        testing.BaseSuite
 
22
        bundles    *bundleReader
 
23
        targetPath string
 
24
        deployer   charm.Deployer
 
25
}
 
26
 
 
27
var _ = gc.Suite(&ManifestDeployerSuite{})
 
28
 
 
29
// because we generally use real charm bundles for testing, and charm bundling
 
30
// sets every file mode to 0755 or 0644, all our input data uses those modes as
 
31
// well.
 
32
 
 
33
func (s *ManifestDeployerSuite) SetUpTest(c *gc.C) {
 
34
        s.BaseSuite.SetUpTest(c)
 
35
        s.bundles = &bundleReader{}
 
36
        s.targetPath = filepath.Join(c.MkDir(), "target")
 
37
        deployerPath := filepath.Join(c.MkDir(), "deployer")
 
38
        s.deployer = charm.NewManifestDeployer(s.targetPath, deployerPath, s.bundles)
 
39
}
 
40
 
 
41
func (s *ManifestDeployerSuite) addMockCharm(c *gc.C, revision int, bundle charm.Bundle) charm.BundleInfo {
 
42
        return s.bundles.AddBundle(c, charmURL(revision), bundle)
 
43
}
 
44
 
 
45
func (s *ManifestDeployerSuite) addCharm(c *gc.C, revision int, content ...ft.Entry) charm.BundleInfo {
 
46
        return s.bundles.AddCustomBundle(c, charmURL(revision), func(path string) {
 
47
                ft.Entries(content).Create(c, path)
 
48
        })
 
49
}
 
50
 
 
51
func (s *ManifestDeployerSuite) deployCharm(c *gc.C, revision int, content ...ft.Entry) charm.BundleInfo {
 
52
        info := s.addCharm(c, revision, content...)
 
53
        err := s.deployer.Stage(info, nil)
 
54
        c.Assert(err, jc.ErrorIsNil)
 
55
        err = s.deployer.Deploy()
 
56
        c.Assert(err, jc.ErrorIsNil)
 
57
        s.assertCharm(c, revision, content...)
 
58
        return info
 
59
}
 
60
 
 
61
func (s *ManifestDeployerSuite) assertCharm(c *gc.C, revision int, content ...ft.Entry) {
 
62
        url, err := charm.ReadCharmURL(filepath.Join(s.targetPath, ".juju-charm"))
 
63
        c.Assert(err, jc.ErrorIsNil)
 
64
        c.Assert(url, gc.DeepEquals, charmURL(revision))
 
65
        ft.Entries(content).Check(c, s.targetPath)
 
66
}
 
67
 
 
68
func (s *ManifestDeployerSuite) TestAbortStageWhenClosed(c *gc.C) {
 
69
        info := s.addMockCharm(c, 1, mockBundle{})
 
70
        abort := make(chan struct{})
 
71
        errors := make(chan error)
 
72
        s.bundles.EnableWaitForAbort()
 
73
        go func() {
 
74
                errors <- s.deployer.Stage(info, abort)
 
75
        }()
 
76
        close(abort)
 
77
        err := <-errors
 
78
        c.Assert(err, gc.ErrorMatches, "charm read aborted")
 
79
}
 
80
 
 
81
func (s *ManifestDeployerSuite) TestDontAbortStageWhenNotClosed(c *gc.C) {
 
82
        info := s.addMockCharm(c, 1, mockBundle{})
 
83
        abort := make(chan struct{})
 
84
        errors := make(chan error)
 
85
        stopWaiting := s.bundles.EnableWaitForAbort()
 
86
        go func() {
 
87
                errors <- s.deployer.Stage(info, abort)
 
88
        }()
 
89
        close(stopWaiting)
 
90
        err := <-errors
 
91
        c.Assert(err, jc.ErrorIsNil)
 
92
}
 
93
 
 
94
func (s *ManifestDeployerSuite) TestDeployWithoutStage(c *gc.C) {
 
95
        err := s.deployer.Deploy()
 
96
        c.Assert(err, gc.ErrorMatches, "charm deployment failed: no charm set")
 
97
}
 
98
 
 
99
func (s *ManifestDeployerSuite) TestInstall(c *gc.C) {
 
100
        //TODO(bogdanteleaga): Fix this on windows
 
101
        if runtime.GOOS == "windows" {
 
102
                c.Skip("bug 1403084: cannot symlink to relative paths on windows")
 
103
        }
 
104
        s.deployCharm(c, 1,
 
105
                ft.File{"some-file", "hello", 0644},
 
106
                ft.Dir{"some-dir", 0755},
 
107
                ft.Symlink{"some-dir/some-link", "../some-file"},
 
108
        )
 
109
}
 
110
 
 
111
func (s *ManifestDeployerSuite) TestUpgradeOverwrite(c *gc.C) {
 
112
        //TODO(bogdanteleaga): Fix this on windows
 
113
        if runtime.GOOS == "windows" {
 
114
                c.Skip("bug 1403084: cannot symlink to relative paths on windows")
 
115
        }
 
116
        s.deployCharm(c, 1,
 
117
                ft.File{"some-file", "hello", 0644},
 
118
                ft.Dir{"some-dir", 0755},
 
119
                ft.File{"some-dir/another-file", "to be removed", 0755},
 
120
                ft.Dir{"another-dir", 0755},
 
121
                ft.Symlink{"another-dir/some-link", "../some-file"},
 
122
        )
 
123
        // Replace each of file, dir, and symlink with a different entry; in
 
124
        // the case of dir, checking that contained files are also removed.
 
125
        s.deployCharm(c, 2,
 
126
                ft.Symlink{"some-file", "no-longer-a-file"},
 
127
                ft.File{"some-dir", "no-longer-a-dir", 0644},
 
128
                ft.Dir{"another-dir", 0755},
 
129
                ft.Dir{"another-dir/some-link", 0755},
 
130
        )
 
131
}
 
132
 
 
133
func (s *ManifestDeployerSuite) TestUpgradePreserveUserFiles(c *gc.C) {
 
134
        //TODO(bogdanteleaga): Fix this on windows
 
135
        if runtime.GOOS == "windows" {
 
136
                c.Skip("bug 1403084: cannot symlink to relative paths on windows")
 
137
        }
 
138
        originalCharmContent := ft.Entries{
 
139
                ft.File{"charm-file", "to-be-removed", 0644},
 
140
                ft.Dir{"charm-dir", 0755},
 
141
        }
 
142
        s.deployCharm(c, 1, originalCharmContent...)
 
143
 
 
144
        // Add user files we expect to keep to the target dir.
 
145
        preserveUserContent := ft.Entries{
 
146
                ft.File{"user-file", "to-be-preserved", 0644},
 
147
                ft.Dir{"user-dir", 0755},
 
148
                ft.File{"user-dir/user-file", "also-preserved", 0644},
 
149
        }.Create(c, s.targetPath)
 
150
 
 
151
        // Add some user files we expect to be removed.
 
152
        removeUserContent := ft.Entries{
 
153
                ft.File{"charm-dir/user-file", "whoops-removed", 0755},
 
154
        }.Create(c, s.targetPath)
 
155
 
 
156
        // Add some user files we expect to be replaced.
 
157
        ft.Entries{
 
158
                ft.File{"replace-file", "original", 0644},
 
159
                ft.Dir{"replace-dir", 0755},
 
160
                ft.Symlink{"replace-symlink", "replace-file"},
 
161
        }.Create(c, s.targetPath)
 
162
 
 
163
        // Deploy an upgrade; all new content overwrites the old...
 
164
        s.deployCharm(c, 2,
 
165
                ft.File{"replace-file", "updated", 0644},
 
166
                ft.Dir{"replace-dir", 0755},
 
167
                ft.Symlink{"replace-symlink", "replace-dir"},
 
168
        )
 
169
 
 
170
        // ...and other files are preserved or removed according to
 
171
        // source and location.
 
172
        preserveUserContent.Check(c, s.targetPath)
 
173
        removeUserContent.AsRemoveds().Check(c, s.targetPath)
 
174
        originalCharmContent.AsRemoveds().Check(c, s.targetPath)
 
175
}
 
176
 
 
177
func (s *ManifestDeployerSuite) TestUpgradeConflictResolveRetrySameCharm(c *gc.C) {
 
178
        // Create base install.
 
179
        s.deployCharm(c, 1,
 
180
                ft.File{"shared-file", "old", 0755},
 
181
                ft.File{"old-file", "old", 0644},
 
182
        )
 
183
 
 
184
        // Create mock upgrade charm that can (claim to) fail to expand...
 
185
        failDeploy := true
 
186
        upgradeContent := ft.Entries{
 
187
                ft.File{"shared-file", "new", 0755},
 
188
                ft.File{"new-file", "new", 0644},
 
189
        }
 
190
        mockCharm := mockBundle{
 
191
                paths: set.NewStrings(upgradeContent.Paths()...),
 
192
                expand: func(targetPath string) error {
 
193
                        upgradeContent.Create(c, targetPath)
 
194
                        if failDeploy {
 
195
                                return fmt.Errorf("oh noes")
 
196
                        }
 
197
                        return nil
 
198
                },
 
199
        }
 
200
        info := s.addMockCharm(c, 2, mockCharm)
 
201
        err := s.deployer.Stage(info, nil)
 
202
        c.Assert(err, jc.ErrorIsNil)
 
203
 
 
204
        // ...and see it fail to expand. We're not too bothered about the actual
 
205
        // content of the target dir at this stage, but we do want to check it's
 
206
        // still marked as based on the original charm...
 
207
        err = s.deployer.Deploy()
 
208
        c.Assert(err, gc.Equals, charm.ErrConflict)
 
209
        s.assertCharm(c, 1)
 
210
 
 
211
        // ...and we want to verify that if we "fix the errors" and redeploy the
 
212
        // same charm...
 
213
        failDeploy = false
 
214
        err = s.deployer.NotifyResolved()
 
215
        c.Assert(err, jc.ErrorIsNil)
 
216
        err = s.deployer.Deploy()
 
217
        c.Assert(err, jc.ErrorIsNil)
 
218
 
 
219
        // ...we end up with the right stuff in play.
 
220
        s.assertCharm(c, 2, upgradeContent...)
 
221
        ft.Removed{"old-file"}.Check(c, s.targetPath)
 
222
}
 
223
 
 
224
func (s *ManifestDeployerSuite) TestUpgradeConflictRevertRetryDifferentCharm(c *gc.C) {
 
225
        // Create base install and add a user file.
 
226
        s.deployCharm(c, 1,
 
227
                ft.File{"shared-file", "old", 0755},
 
228
                ft.File{"old-file", "old", 0644},
 
229
        )
 
230
        userFile := ft.File{"user-file", "user", 0644}.Create(c, s.targetPath)
 
231
 
 
232
        // Create a charm upgrade that never works (but still writes a bunch of files),
 
233
        // and deploy it.
 
234
        badUpgradeContent := ft.Entries{
 
235
                ft.File{"shared-file", "bad", 0644},
 
236
                ft.File{"bad-file", "bad", 0644},
 
237
        }
 
238
        badCharm := mockBundle{
 
239
                paths: set.NewStrings(badUpgradeContent.Paths()...),
 
240
                expand: func(targetPath string) error {
 
241
                        badUpgradeContent.Create(c, targetPath)
 
242
                        return fmt.Errorf("oh noes")
 
243
                },
 
244
        }
 
245
        badInfo := s.addMockCharm(c, 2, badCharm)
 
246
        err := s.deployer.Stage(badInfo, nil)
 
247
        c.Assert(err, jc.ErrorIsNil)
 
248
        err = s.deployer.Deploy()
 
249
        c.Assert(err, gc.Equals, charm.ErrConflict)
 
250
 
 
251
        // Notify the Deployer that it'll be expected to revert the changes from
 
252
        // the last attempt.
 
253
        err = s.deployer.NotifyRevert()
 
254
        c.Assert(err, jc.ErrorIsNil)
 
255
 
 
256
        // Create a charm upgrade that creates a bunch of different files, without
 
257
        // error, and deploy it; check user files are preserved, and nothing from
 
258
        // charm 1 or 2 is.
 
259
        s.deployCharm(c, 3,
 
260
                ft.File{"shared-file", "new", 0755},
 
261
                ft.File{"new-file", "new", 0644},
 
262
        )
 
263
        userFile.Check(c, s.targetPath)
 
264
        ft.Removed{"old-file"}.Check(c, s.targetPath)
 
265
        ft.Removed{"bad-file"}.Check(c, s.targetPath)
 
266
}