Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(118)

Side by Side Diff: go/src/infra/tools/cipd/deployer_test.go

Issue 1129043003: cipd: Refactor client to make it more readable. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/src/infra/tools/cipd/deployer.go ('k') | go/src/infra/tools/cipd/doc.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package cipd
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "io/ioutil"
12 "os"
13 "path/filepath"
14 "sort"
15 "testing"
16
17 . "github.com/smartystreets/goconvey/convey"
18 )
19
20 func TestUtilities(t *testing.T) {
21 Convey("Given a temp directory", t, func() {
22 tempDir, err := ioutil.TempDir("", "cipd_test")
23 So(err, ShouldBeNil)
24 Reset(func() { os.RemoveAll(tempDir) })
25
26 // Wrappers that accept paths relative to tempDir.
27 touch := func(rel string) {
28 abs := filepath.Join(tempDir, filepath.FromSlash(rel))
29 err := os.MkdirAll(filepath.Dir(abs), 0777)
30 So(err, ShouldBeNil)
31 f, err := os.Create(abs)
32 So(err, ShouldBeNil)
33 f.Close()
34 }
35 ensureLink := func(symlinkRel string, target string) error {
36 return ensureSymlink(filepath.Join(tempDir, symlinkRel), target)
37 }
38 readLink := func(symlinkRel string) string {
39 val, err := os.Readlink(filepath.Join(tempDir, symlinkRe l))
40 So(err, ShouldBeNil)
41 return val
42 }
43
44 Convey("ensureSymlink creates new symlink", func() {
45 So(ensureLink("symlink", "target"), ShouldBeNil)
46 So(readLink("symlink"), ShouldEqual, "target")
47 })
48
49 Convey("ensureSymlink builds full path", func() {
50 So(ensureLink(filepath.Join("a", "b", "c"), "target"), S houldBeNil)
51 So(readLink(filepath.Join("a", "b", "c")), ShouldEqual, "target")
52 })
53
54 Convey("ensureSymlink replaces existing one", func() {
55 So(ensureLink("symlink", "target"), ShouldBeNil)
56 So(ensureLink("symlink", "another"), ShouldBeNil)
57 So(readLink("symlink"), ShouldEqual, "another")
58 })
59
60 Convey("scanPackageDir works with empty dir", func() {
61 err := os.Mkdir(filepath.Join(tempDir, "dir"), 0777)
62 So(err, ShouldBeNil)
63 files := makeStringSet()
64 err = scanPackageDir(filepath.Join(tempDir, "dir"), file s)
65 So(err, ShouldBeNil)
66 So(len(files), ShouldEqual, 0)
67 })
68
69 Convey("scanPackageDir works", func() {
70 touch("unrelated/1")
71 touch("dir/a/1")
72 touch("dir/a/2")
73 touch("dir/b/1")
74 touch("dir/.cipdpkg/abc")
75 touch("dir/.cipd/abc")
76 ensureLink("dir/a/sym_link", "target")
77 files := makeStringSet()
78 err := scanPackageDir(filepath.Join(tempDir, "dir"), fil es)
79 So(err, ShouldBeNil)
80 names := sort.StringSlice{}
81 for n := range files {
82 names = append(names, filepath.ToSlash(n))
83 }
84 names.Sort()
85 So(names, ShouldResemble, sort.StringSlice{
86 "a/1",
87 "a/2",
88 "a/sym_link",
89 "b/1",
90 })
91 })
92
93 Convey("ensureDirectoryGone works with missing dir", func() {
94 So(ensureDirectoryGone(filepath.Join(tempDir, "missing") ), ShouldBeNil)
95 })
96
97 Convey("ensureDirectoryGone works", func() {
98 touch("dir/a/1")
99 touch("dir/a/2")
100 touch("dir/b/1")
101 So(ensureDirectoryGone(filepath.Join(tempDir, "dir")), S houldBeNil)
102 _, err := os.Stat(filepath.Join(tempDir, "dir"))
103 So(os.IsNotExist(err), ShouldBeTrue)
104 })
105
106 Convey("ensureFileGone works", func() {
107 touch("abc")
108 So(ensureFileGone(filepath.Join(tempDir, "abc")), Should BeNil)
109 _, err := os.Stat(filepath.Join(tempDir, "abc"))
110 So(os.IsNotExist(err), ShouldBeTrue)
111 })
112
113 Convey("ensureFileGone works with missing file", func() {
114 So(ensureFileGone(filepath.Join(tempDir, "abc")), Should BeNil)
115 })
116
117 Convey("ensureFileGone works with symlink", func() {
118 ensureLink("abc", "target")
119 So(ensureFileGone(filepath.Join(tempDir, "abc")), Should BeNil)
120 _, err := os.Stat(filepath.Join(tempDir, "abc"))
121 So(os.IsNotExist(err), ShouldBeTrue)
122 })
123 })
124 }
125
126 func TestDeployInstance(t *testing.T) {
127 Convey("Given a temp directory", t, func() {
128 tempDir, err := ioutil.TempDir("", "cipd_test")
129 So(err, ShouldBeNil)
130 Reset(func() { os.RemoveAll(tempDir) })
131
132 Convey("DeployInstance new empty package instance", func() {
133 inst := makeTestInstance("test/package", nil)
134 info, err := DeployInstance(tempDir, inst)
135 So(err, ShouldBeNil)
136 So(info, ShouldResemble, PackageState{
137 PackageName: "test/package",
138 InstanceID: inst.InstanceID(),
139 })
140 So(scanDir(tempDir), ShouldResemble, []string{
141 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/.cipdpkg/manifest.json",
142 ".cipd/pkgs/test_package_B6R4ErK5ko/_current:012 3456789abcdef00000123456789abcdef0000",
143 })
144 })
145
146 Convey("DeployInstance new non-empty package instance", func() {
147 inst := makeTestInstance("test/package", []File{
148 makeTestFile("some/file/path", "data a", false),
149 makeTestFile("some/executable", "data b", true),
150 makeTestSymlink("some/symlink", "executable"),
151 })
152 _, err := DeployInstance(tempDir, inst)
153 So(err, ShouldBeNil)
154 So(scanDir(tempDir), ShouldResemble, []string{
155 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/.cipdpkg/manifest.json",
156 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/some/executable*",
157 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/some/file/path",
158 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/some/symlink:executable",
159 ".cipd/pkgs/test_package_B6R4ErK5ko/_current:012 3456789abcdef00000123456789abcdef0000",
160 "some/executable:../.cipd/pkgs/test_package_B6R4 ErK5ko/_current/some/executable",
161 "some/file/path:../../.cipd/pkgs/test_package_B6 R4ErK5ko/_current/some/file/path",
162 "some/symlink:../.cipd/pkgs/test_package_B6R4ErK 5ko/_current/some/symlink",
163 })
164 // Ensure symlinks are actually traversable.
165 body, err := ioutil.ReadFile(filepath.Join(tempDir, "som e", "file", "path"))
166 So(err, ShouldBeNil)
167 So(string(body), ShouldEqual, "data a")
168 // Symlink to symlink is traversable too.
169 body, err = ioutil.ReadFile(filepath.Join(tempDir, "some ", "symlink"))
170 So(err, ShouldBeNil)
171 So(string(body), ShouldEqual, "data b")
172 })
173
174 Convey("Redeploy same package instance", func() {
175 inst := makeTestInstance("test/package", []File{
176 makeTestFile("some/file/path", "data a", false),
177 makeTestFile("some/executable", "data b", true),
178 makeTestSymlink("some/symlink", "executable"),
179 })
180 _, err := DeployInstance(tempDir, inst)
181 So(err, ShouldBeNil)
182 _, err = DeployInstance(tempDir, inst)
183 So(err, ShouldBeNil)
184 So(scanDir(tempDir), ShouldResemble, []string{
185 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/.cipdpkg/manifest.json",
186 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/some/executable*",
187 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/some/file/path",
188 ".cipd/pkgs/test_package_B6R4ErK5ko/0123456789ab cdef00000123456789abcdef0000/some/symlink:executable",
189 ".cipd/pkgs/test_package_B6R4ErK5ko/_current:012 3456789abcdef00000123456789abcdef0000",
190 "some/executable:../.cipd/pkgs/test_package_B6R4 ErK5ko/_current/some/executable",
191 "some/file/path:../../.cipd/pkgs/test_package_B6 R4ErK5ko/_current/some/file/path",
192 "some/symlink:../.cipd/pkgs/test_package_B6R4ErK 5ko/_current/some/symlink",
193 })
194 })
195
196 Convey("DeployInstance package update", func() {
197 oldPkg := makeTestInstance("test/package", []File{
198 makeTestFile("some/file/path", "data a old", fal se),
199 makeTestFile("some/executable", "data b old", tr ue),
200 makeTestFile("old only", "data c old", true),
201 makeTestFile("mode change 1", "data d", true),
202 makeTestFile("mode change 2", "data e", false),
203 makeTestSymlink("symlink unchanged", "target"),
204 makeTestSymlink("symlink changed", "old target") ,
205 makeTestSymlink("symlink removed", "target"),
206 })
207 oldPkg.instanceID = "00000000000000000000000000000000000 00000"
208
209 newPkg := makeTestInstance("test/package", []File{
210 makeTestFile("some/file/path", "data a new", fal se),
211 makeTestFile("some/executable", "data b new", tr ue),
212 makeTestFile("mode change 1", "data d", false),
213 makeTestFile("mode change 2", "data d", true),
214 makeTestSymlink("symlink unchanged", "target"),
215 makeTestSymlink("symlink changed", "new target") ,
216 })
217 newPkg.instanceID = "11111111111111111111111111111111111 11111"
218
219 _, err := DeployInstance(tempDir, oldPkg)
220 So(err, ShouldBeNil)
221 _, err = DeployInstance(tempDir, newPkg)
222 So(err, ShouldBeNil)
223
224 So(scanDir(tempDir), ShouldResemble, []string{
225 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/.cipdpkg/manifest.json",
226 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/mode change 1",
227 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/mode change 2*",
228 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/some/executable*",
229 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/some/file/path",
230 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/symlink changed:new target",
231 ".cipd/pkgs/test_package_B6R4ErK5ko/111111111111 1111111111111111111111111111/symlink unchanged:target",
232 ".cipd/pkgs/test_package_B6R4ErK5ko/_current:111 1111111111111111111111111111111111111",
233 "mode change 1:.cipd/pkgs/test_package_B6R4ErK5k o/_current/mode change 1",
234 "mode change 2:.cipd/pkgs/test_package_B6R4ErK5k o/_current/mode change 2",
235 "some/executable:../.cipd/pkgs/test_package_B6R4 ErK5ko/_current/some/executable",
236 "some/file/path:../../.cipd/pkgs/test_package_B6 R4ErK5ko/_current/some/file/path",
237 "symlink changed:.cipd/pkgs/test_package_B6R4ErK 5ko/_current/symlink changed",
238 "symlink unchanged:.cipd/pkgs/test_package_B6R4E rK5ko/_current/symlink unchanged",
239 })
240 })
241
242 Convey("DeployInstance two different packages", func() {
243 pkg1 := makeTestInstance("test/package", []File{
244 makeTestFile("some/file/path", "data a old", fal se),
245 makeTestFile("some/executable", "data b old", tr ue),
246 makeTestFile("pkg1 file", "data c", false),
247 })
248 pkg1.instanceID = "0000000000000000000000000000000000000 000"
249
250 // Nesting in package names is allowed.
251 pkg2 := makeTestInstance("test/package/another", []File{
252 makeTestFile("some/file/path", "data a new", fal se),
253 makeTestFile("some/executable", "data b new", tr ue),
254 makeTestFile("pkg2 file", "data d", false),
255 })
256 pkg2.instanceID = "1111111111111111111111111111111111111 111"
257
258 _, err := DeployInstance(tempDir, pkg1)
259 So(err, ShouldBeNil)
260 _, err = DeployInstance(tempDir, pkg2)
261 So(err, ShouldBeNil)
262
263 // TODO: Conflicting symlinks point to last installed pa ckage, it is not
264 // very deterministic.
265 So(scanDir(tempDir), ShouldResemble, []string{
266 ".cipd/pkgs/package_another_4HL4H61fGm/111111111 1111111111111111111111111111111/.cipdpkg/manifest.json",
267 ".cipd/pkgs/package_another_4HL4H61fGm/111111111 1111111111111111111111111111111/pkg2 file",
268 ".cipd/pkgs/package_another_4HL4H61fGm/111111111 1111111111111111111111111111111/some/executable*",
269 ".cipd/pkgs/package_another_4HL4H61fGm/111111111 1111111111111111111111111111111/some/file/path",
270 ".cipd/pkgs/package_another_4HL4H61fGm/_current: 1111111111111111111111111111111111111111",
271 ".cipd/pkgs/test_package_B6R4ErK5ko/000000000000 0000000000000000000000000000/.cipdpkg/manifest.json",
272 ".cipd/pkgs/test_package_B6R4ErK5ko/000000000000 0000000000000000000000000000/pkg1 file",
273 ".cipd/pkgs/test_package_B6R4ErK5ko/000000000000 0000000000000000000000000000/some/executable*",
274 ".cipd/pkgs/test_package_B6R4ErK5ko/000000000000 0000000000000000000000000000/some/file/path",
275 ".cipd/pkgs/test_package_B6R4ErK5ko/_current:000 0000000000000000000000000000000000000",
276 "pkg1 file:.cipd/pkgs/test_package_B6R4ErK5ko/_c urrent/pkg1 file",
277 "pkg2 file:.cipd/pkgs/package_another_4HL4H61fGm /_current/pkg2 file",
278 "some/executable:../.cipd/pkgs/package_another_4 HL4H61fGm/_current/some/executable",
279 "some/file/path:../../.cipd/pkgs/package_another _4HL4H61fGm/_current/some/file/path",
280 })
281 })
282
283 Convey("Try to deploy package instance with bad package name", f unc() {
284 _, err := DeployInstance(tempDir, makeTestInstance("../t est/package", nil))
285 So(err, ShouldNotBeNil)
286 })
287
288 Convey("Try to deploy package instance with bad instance ID", fu nc() {
289 inst := makeTestInstance("test/package", nil)
290 inst.instanceID = "../000000000"
291 _, err := DeployInstance(tempDir, inst)
292 So(err, ShouldNotBeNil)
293 })
294 })
295 }
296
297 func TestFindDeployed(t *testing.T) {
298 Convey("Given a temp directory", t, func() {
299 tempDir, err := ioutil.TempDir("", "cipd_test")
300 So(err, ShouldBeNil)
301 Reset(func() { os.RemoveAll(tempDir) })
302
303 Convey("FindDeployed works with empty dir", func() {
304 out, err := FindDeployed(tempDir)
305 So(err, ShouldBeNil)
306 So(out, ShouldBeNil)
307 })
308
309 Convey("FindDeployed works", func() {
310 // Deploy a bunch of stuff.
311 _, err := DeployInstance(tempDir, makeTestInstance("test /pkg/123", nil))
312 So(err, ShouldBeNil)
313 _, err = DeployInstance(tempDir, makeTestInstance("test/ pkg/456", nil))
314 So(err, ShouldBeNil)
315 _, err = DeployInstance(tempDir, makeTestInstance("test/ pkg", nil))
316 So(err, ShouldBeNil)
317 _, err = DeployInstance(tempDir, makeTestInstance("test" , nil))
318 So(err, ShouldBeNil)
319
320 // Verify it is discoverable.
321 out, err := FindDeployed(tempDir)
322 So(err, ShouldBeNil)
323 So(out, ShouldResemble, []PackageState{
324 PackageState{
325 PackageName: "test",
326 InstanceID: "0123456789abcdef0000012345 6789abcdef0000",
327 },
328 PackageState{
329 PackageName: "test/pkg",
330 InstanceID: "0123456789abcdef0000012345 6789abcdef0000",
331 },
332 PackageState{
333 PackageName: "test/pkg/123",
334 InstanceID: "0123456789abcdef0000012345 6789abcdef0000",
335 },
336 PackageState{
337 PackageName: "test/pkg/456",
338 InstanceID: "0123456789abcdef0000012345 6789abcdef0000",
339 },
340 })
341 })
342 })
343 }
344
345 func TestRemoveDeployed(t *testing.T) {
346 Convey("Given a temp directory", t, func() {
347 tempDir, err := ioutil.TempDir("", "cipd_test")
348 So(err, ShouldBeNil)
349 Reset(func() { os.RemoveAll(tempDir) })
350
351 Convey("RemoveDeployed works with missing package", func() {
352 err := RemoveDeployed(tempDir, "package/path")
353 So(err, ShouldBeNil)
354 })
355
356 Convey("RemoveDeployed works", func() {
357 // Deploy some instance (to keep it).
358 inst := makeTestInstance("test/package/123", []File{
359 makeTestFile("some/file/path1", "data a", false) ,
360 makeTestFile("some/executable1", "data b", true) ,
361 })
362 _, err := DeployInstance(tempDir, inst)
363 So(err, ShouldBeNil)
364
365 // Deploy another instance (to remove it).
366 inst = makeTestInstance("test/package", []File{
367 makeTestFile("some/file/path2", "data a", false) ,
368 makeTestFile("some/executable2", "data b", true) ,
369 makeTestSymlink("some/symlink", "executable"),
370 })
371 _, err = DeployInstance(tempDir, inst)
372 So(err, ShouldBeNil)
373
374 // Now remove the second package.
375 err = RemoveDeployed(tempDir, "test/package")
376 So(err, ShouldBeNil)
377
378 // Verify the final state (only first package should sur vive).
379 So(scanDir(tempDir), ShouldResemble, []string{
380 ".cipd/pkgs/package_123_Wnok5l4iFr/0123456789abc def00000123456789abcdef0000/.cipdpkg/manifest.json",
381 ".cipd/pkgs/package_123_Wnok5l4iFr/0123456789abc def00000123456789abcdef0000/some/executable1*",
382 ".cipd/pkgs/package_123_Wnok5l4iFr/0123456789abc def00000123456789abcdef0000/some/file/path1",
383 ".cipd/pkgs/package_123_Wnok5l4iFr/_current:0123 456789abcdef00000123456789abcdef0000",
384 "some/executable1:../.cipd/pkgs/package_123_Wnok 5l4iFr/_current/some/executable1",
385 "some/file/path1:../../.cipd/pkgs/package_123_Wn ok5l4iFr/_current/some/file/path1",
386 })
387 })
388 })
389 }
390
391 ////////////////////////////////////////////////////////////////////////////////
392
393 type testPackageInstance struct {
394 packageName string
395 instanceID string
396 files []File
397 }
398
399 // makeTestInstance returns PackageInstance implementation with mocked guts.
400 func makeTestInstance(name string, files []File) *testPackageInstance {
401 // Generate and append manifest file.
402 out := bytes.Buffer{}
403 err := writeManifest(&Manifest{
404 FormatVersion: manifestFormatVersion,
405 PackageName: name,
406 }, &out)
407 if err != nil {
408 panic("Failed to write a manifest")
409 }
410 files = append(files, makeTestFile(manifestName, string(out.Bytes()), fa lse))
411 return &testPackageInstance{
412 packageName: name,
413 instanceID: "0123456789abcdef00000123456789abcdef0000",
414 files: files,
415 }
416 }
417
418 func (f *testPackageInstance) Close() error { return nil }
419 func (f *testPackageInstance) PackageName() string { return f.packageName }
420 func (f *testPackageInstance) InstanceID() string { return f.instanceID }
421 func (f *testPackageInstance) Files() []File { return f.files }
422 func (f *testPackageInstance) DataReader() io.ReadSeeker { panic("Not implemente d") }
423
424 ////////////////////////////////////////////////////////////////////////////////
425
426 // scanDir returns list of files (regular and symlinks) it finds in a directory.
427 // Symlinks are returned as "path:target". Regular executable files are suffixed
428 // with '*'. All paths are relative to the scanned directory and slash
429 // separated. Symlink targets are slash separated too, but otherwise not
430 // modified. Does not look inside symlinked directories.
431 func scanDir(root string) (out []string) {
432 err := filepath.Walk(root, func(path string, info os.FileInfo, err error ) error {
433 if err != nil {
434 return err
435 }
436 rel, err := filepath.Rel(root, path)
437 if err != nil {
438 return err
439 }
440 if info.Mode().IsDir() {
441 return nil
442 }
443
444 rel = filepath.ToSlash(rel)
445 target, err := os.Readlink(path)
446 var item string
447 if err == nil {
448 item = fmt.Sprintf("%s:%s", rel, filepath.ToSlash(target ))
449 } else {
450 if info.Mode().IsRegular() {
451 item = rel
452 } else {
453 item = fmt.Sprintf("%s:??????", rel)
454 }
455 }
456
457 suffix := ""
458 if info.Mode().IsRegular() && (info.Mode().Perm()&0100) != 0 {
459 suffix = "*"
460 }
461
462 out = append(out, item+suffix)
463 return nil
464 })
465 if err != nil {
466 panic("Failed to walk a directory")
467 }
468 return
469 }
OLDNEW
« no previous file with comments | « go/src/infra/tools/cipd/deployer.go ('k') | go/src/infra/tools/cipd/doc.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698