Merge lp:~elopio/snappy/test-verify into lp:~snappy-dev/snappy/snappy-moved-to-github

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/snappy/test-verify
Merge into: lp:~snappy-dev/snappy/snappy-moved-to-github
Prerequisite: lp:~elopio/snappy/snappy_from_trunk
Diff against target: 485 lines (+319/-45)
10 files modified
_integration-tests/tests/latest/install_test.go (+9/-0)
cmd/snappy/cmd_verify.go (+43/-0)
helpers/helpers.go (+4/-0)
snappy/build.go (+3/-37)
snappy/build_test.go (+1/-3)
snappy/hashes.go (+85/-0)
snappy/hashes_test.go (+65/-0)
snappy/snapp.go (+26/-5)
snappy/verify.go (+48/-0)
snappy/verify_test.go (+35/-0)
To merge this branch: bzr merge lp:~elopio/snappy/test-verify
Reviewer Review Type Date Requested Status
Michael Vogt Pending
Review via email: mp+263311@code.launchpad.net
To post a comment you must log in.
lp:~elopio/snappy/test-verify updated
520. By Leo Arias

Added a todo comment.

Unmerged revisions

520. By Leo Arias

Added a todo comment.

519. By Leo Arias

Merged with the other verify branch.

518. By Leo Arias

Update the common calls.

517. By Leo Arias

Fixed the suite name.

516. By Leo Arias

Merged with prerequisite.

515. By Leo Arias

Added a verify test.

514. By Leo Arias

Merged with prerequisites.

513. By Michael Vogt

helpers/helpers.go: fix typo in comment (thanks Leo)

512. By Michael Vogt

cmd/snappy/cmd_verify.go: improve help message (thanks Leo!)

511. By Michael Vogt

snappy/snapp.go: remove RemoteSnapPart.Verify, SystemImagePart.Verify

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '_integration-tests/tests/latest/install_test.go'
2--- _integration-tests/tests/latest/install_test.go 2015-06-30 03:40:28 +0000
3+++ _integration-tests/tests/latest/install_test.go 2015-06-30 03:40:28 +0000
4@@ -92,3 +92,12 @@
5 expected := "(?ms).*^apps: hello-world\n"
6 c.Assert(infoOutput, Matches, expected)
7 }
8+
9+func (s *installSuite) TestVerifyWithInstalledPackage(c *C) {
10+ installSnap(c, "hello-world")
11+
12+ verifyOutput := ExecCommand(c, "snappy", "verify")
13+
14+ // TODO fill the right expected message.
15+ c.Assert(verifyOutput, Equals, "Verified successfully")
16+}
17
18=== added file 'cmd/snappy/cmd_verify.go'
19--- cmd/snappy/cmd_verify.go 1970-01-01 00:00:00 +0000
20+++ cmd/snappy/cmd_verify.go 2015-06-30 03:40:28 +0000
21@@ -0,0 +1,43 @@
22+// -*- Mode: Go; indent-tabs-mode: t -*-
23+
24+/*
25+ * Copyright (C) 2014-2015 Canonical Ltd
26+ *
27+ * This program is free software: you can redistribute it and/or modify
28+ * it under the terms of the GNU General Public License version 3 as
29+ * published by the Free Software Foundation.
30+ *
31+ * This program is distributed in the hope that it will be useful,
32+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
33+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34+ * GNU General Public License for more details.
35+ *
36+ * You should have received a copy of the GNU General Public License
37+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
38+ *
39+ */
40+
41+package main
42+
43+import (
44+ "launchpad.net/snappy/logger"
45+ "launchpad.net/snappy/progress"
46+ "launchpad.net/snappy/snappy"
47+)
48+
49+type cmdVerify struct {
50+}
51+
52+func init() {
53+ _, err := parser.AddCommand("verify",
54+ "Verify the integrity of all installed snap packages",
55+ "Verify the integrity of all installed snap packages by comparing the permissions, sizes and file hashes of each file",
56+ &cmdVerify{})
57+ if err != nil {
58+ logger.Panicf("Unable to verify: %v", err)
59+ }
60+}
61+
62+func (x *cmdVerify) Execute(args []string) (err error) {
63+ return snappy.VerifyInstalled(progress.MakeProgressBar())
64+}
65
66=== modified file 'helpers/helpers.go'
67--- helpers/helpers.go 2015-06-09 19:11:54 +0000
68+++ helpers/helpers.go 2015-06-30 03:40:28 +0000
69@@ -115,6 +115,10 @@
70
71 // UnpackTar unpacks the given tar file into the target directory
72 func UnpackTar(r io.Reader, targetDir string, fn UnpackTarTransformFunc) error {
73+ // ensure we extract with the original permissions
74+ oldUmask := syscall.Umask(0)
75+ defer syscall.Umask(oldUmask)
76+
77 return TarIterate(r, func(tr *tar.Reader, hdr *tar.Header) (err error) {
78 // run tar transform func
79 name := hdr.Name
80
81=== modified file 'snappy/build.go'
82--- snappy/build.go 2015-06-11 19:30:45 +0000
83+++ snappy/build.go 2015-06-30 03:40:28 +0000
84@@ -186,42 +186,6 @@
85 return strings.Fields(string(output))[0], nil
86 }
87
88-func hashForFile(buildDir, path string, info os.FileInfo) (h *fileHash, err error) {
89- sha512sum := ""
90- // pointer so that omitempty works (we don't want size for
91- // directories or symlinks)
92- var size *int64
93- if info.Mode().IsRegular() {
94- sha512sum, err = helpers.Sha512sum(path)
95- if err != nil {
96- return nil, err
97- }
98- fsize := info.Size()
99- size = &fsize
100- }
101-
102- // major/minor handling
103- device := ""
104- major, minor, err := helpers.MajorMinor(info)
105- if err == nil {
106- device = fmt.Sprintf("%v,%v", major, minor)
107- }
108-
109- if buildDir != "" {
110- path = path[len(buildDir)+1:]
111- }
112-
113- return &fileHash{
114- Name: path,
115- Size: size,
116- Sha512: sha512sum,
117- Device: device,
118- // FIXME: not portable, this output is different on
119- // windows, macos
120- Mode: newYamlFileMode(info.Mode()),
121- }, nil
122-}
123-
124 func writeHashes(buildDir, dataTar string) error {
125
126 debianDir := filepath.Join(buildDir, "DEBIAN")
127@@ -244,10 +208,12 @@
128 return nil
129 }
130
131- hash, err := hashForFile(buildDir, path, info)
132+ hash, err := hashForFile(path)
133 if err != nil {
134 return err
135 }
136+ // adjust the name by removing the builddir
137+ hash.Name = hash.Name[len(buildDir)+1:]
138 hashes.Files = append(hashes.Files, hash)
139
140 return nil
141
142=== modified file 'snappy/build_test.go'
143--- snappy/build_test.go 2015-06-08 16:26:25 +0000
144+++ snappy/build_test.go 2015-06-30 03:40:28 +0000
145@@ -432,9 +432,7 @@
146 c.Skip("no /dev/kmsg")
147 }
148
149- stat, err := os.Stat("/dev/kmsg")
150- c.Assert(err, IsNil)
151- h, err := hashForFile("", "/dev/kmsg", stat)
152+ h, err := hashForFile("/dev/kmsg")
153 c.Assert(err, IsNil)
154 c.Assert(h.Name, Equals, "/dev/kmsg")
155 c.Assert(h.Device, Equals, "1,11")
156
157=== modified file 'snappy/hashes.go'
158--- snappy/hashes.go 2015-05-15 13:33:27 +0000
159+++ snappy/hashes.go 2015-06-30 03:40:28 +0000
160@@ -22,6 +22,8 @@
161 import (
162 "fmt"
163 "os"
164+
165+ "launchpad.net/snappy/helpers"
166 )
167
168 type yamlFileMode struct {
169@@ -111,6 +113,40 @@
170 XAttr map[string]string `yaml:"xattr,omitempty"`
171 }
172
173+func (fh *fileHash) Verify() error {
174+ fh2, err := hashForFile(fh.Name)
175+ if err != nil {
176+ return err
177+ }
178+
179+ // check permissions
180+ if fh2.Mode.mode != fh.Mode.mode {
181+ return fmt.Errorf("file mode mismatch for %v: 0%o != 0%o", fh.Name, fh.Mode.mode, fh2.Mode.mode)
182+ }
183+
184+ // check size (only files have them)
185+ if (fh2.Size == nil && fh.Size != nil) || (fh2.Size != nil && fh.Size == nil) {
186+ return fmt.Errorf("size data mismatch for %v", fh.Name)
187+ }
188+ if fh2.Size != nil && fh.Size != nil {
189+ if *fh2.Size != *fh.Size {
190+ return fmt.Errorf("size mismatch for %v: %v != %v", fh.Name, *fh.Size, *fh2.Size)
191+ }
192+ }
193+
194+ // check hash
195+ if fh2.Sha512 != fh.Sha512 {
196+ return fmt.Errorf("hash mismatch for %v: %v != %v", fh.Name, fh.Sha512, fh2.Sha512)
197+ }
198+
199+ // check device
200+ if fh2.Device != fh.Device {
201+ return fmt.Errorf("device info mismatch for %v: %v != %v", fh.Name, fh.Device, fh2.Device)
202+ }
203+
204+ return nil
205+}
206+
207 // the meta/hashes file
208 type hashesYaml struct {
209 // the archive hash
210@@ -119,3 +155,52 @@
211 // the hashes for the files in the archive
212 Files []*fileHash
213 }
214+
215+func (h *hashesYaml) Verify() error {
216+ for _, fh := range h.Files {
217+ if err := fh.Verify(); err != nil {
218+ return err
219+ }
220+ }
221+
222+ return nil
223+}
224+
225+// get the path for the given file
226+func hashForFile(path string) (h *fileHash, err error) {
227+ sha512sum := ""
228+
229+ info, err := os.Lstat(path)
230+ if err != nil {
231+ return nil, err
232+ }
233+
234+ // pointer so that omitempty works (we don't want size for
235+ // directories or symlinks)
236+ var size *int64
237+ if info.Mode().IsRegular() {
238+ sha512sum, err = helpers.Sha512sum(path)
239+ if err != nil {
240+ return nil, err
241+ }
242+ fsize := info.Size()
243+ size = &fsize
244+ }
245+
246+ // major/minor handling
247+ device := ""
248+ major, minor, err := helpers.MajorMinor(info)
249+ if err == nil {
250+ device = fmt.Sprintf("%v,%v", major, minor)
251+ }
252+
253+ return &fileHash{
254+ Name: path,
255+ Size: size,
256+ Sha512: sha512sum,
257+ Device: device,
258+ // FIXME: not portable, this output is different on
259+ // windows, macos
260+ Mode: newYamlFileMode(info.Mode()),
261+ }, nil
262+}
263
264=== modified file 'snappy/hashes_test.go'
265--- snappy/hashes_test.go 2015-06-02 20:46:07 +0000
266+++ snappy/hashes_test.go 2015-06-30 03:40:28 +0000
267@@ -152,3 +152,68 @@
268 mode: frw-r--r--
269 `)
270 }
271+
272+func makeTestFileWithHash(c *C) (*fileHash, string) {
273+ p := filepath.Join(c.MkDir(), "foo")
274+ err := ioutil.WriteFile(p, []byte("bar\n"), 0644)
275+ c.Assert(err, IsNil)
276+ st, err := os.Stat(p)
277+ c.Assert(err, IsNil)
278+
279+ size := int64(4)
280+ mode := newYamlFileMode(st.Mode())
281+ fh := fileHash{
282+ Name: p,
283+ Size: &size,
284+ Sha512: "cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063",
285+ Mode: mode,
286+ }
287+ return &fh, p
288+}
289+
290+func (s *SnapTestSuite) TestFileHashSimple(c *C) {
291+ fh, _ := makeTestFileWithHash(c)
292+ err := fh.Verify()
293+ c.Assert(err, IsNil)
294+}
295+
296+func (s *SnapTestSuite) TestFileHashWrongSize(c *C) {
297+ fh, _ := makeTestFileWithHash(c)
298+
299+ fakeSize := int64(1)
300+ fh.Size = &fakeSize
301+
302+ err := fh.Verify()
303+ c.Assert(err, ErrorMatches, "size mismatch for .*/foo: 1 != 4")
304+}
305+
306+func (s *SnapTestSuite) TestFileHashWrongMode(c *C) {
307+ fh, _ := makeTestFileWithHash(c)
308+ fh.Mode.mode = 0444
309+
310+ err := fh.Verify()
311+ c.Assert(err, ErrorMatches, "file mode mismatch for .*/foo: 0444 != 0644")
312+}
313+
314+func (s *SnapTestSuite) TestFileHashWrongHash(c *C) {
315+ fh, _ := makeTestFileWithHash(c)
316+ fh.Sha512 = "xx"
317+
318+ err := fh.Verify()
319+ c.Assert(err, ErrorMatches, "hash mismatch for .*/foo: xx != cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063")
320+}
321+
322+func (s *SnapTestSuite) TestFileHashForDir(c *C) {
323+ name := c.MkDir()
324+ st, err := os.Stat(name)
325+ c.Assert(err, IsNil)
326+
327+ mode := newYamlFileMode(st.Mode())
328+ fh := fileHash{
329+ Name: name,
330+ Mode: mode,
331+ }
332+
333+ err = fh.Verify()
334+ c.Assert(err, IsNil)
335+}
336
337=== modified file 'snappy/snapp.go'
338--- snappy/snapp.go 2015-06-12 03:55:01 +0000
339+++ snappy/snapp.go 2015-06-30 03:40:28 +0000
340@@ -608,9 +608,18 @@
341 part.description = description
342 }
343
344- // read hash, its ok if its not there, some older versions of
345- // snappy did not write this file
346- hashesData, err := ioutil.ReadFile(filepath.Join(part.basedir, "meta", "hashes.yaml"))
347+ // read hash
348+ h, err := part.hashesData()
349+ if err != nil {
350+ return nil, err
351+ }
352+ part.hash = h.ArchiveSha512
353+
354+ return part, nil
355+}
356+
357+func (s *SnapPart) hashesData() (*hashesYaml, error) {
358+ hashesData, err := ioutil.ReadFile(filepath.Join(s.basedir, "meta", "hashes.yaml"))
359 if err != nil {
360 return nil, err
361 }
362@@ -620,9 +629,8 @@
363 if err != nil {
364 return nil, &ErrInvalidYaml{file: "hashes.yaml", err: err, yaml: hashesData}
365 }
366- part.hash = h.ArchiveSha512
367
368- return part, nil
369+ return &h, nil
370 }
371
372 // Type returns the type of the SnapPart (app, oem, ...)
373@@ -1083,6 +1091,19 @@
374 return s.m.Frameworks, nil
375 }
376
377+// Verify checks the integrity
378+func (s *SnapPart) Verify(pb progress.Meter) error {
379+ hashesData, err := s.hashesData()
380+ if err != nil {
381+ return err
382+ }
383+
384+ helpers.ChDir(s.basedir, func() {
385+ err = hashesData.Verify()
386+ })
387+ return err
388+}
389+
390 // DependentNames returns a list of the names of apps installed that
391 // depend on this one
392 //
393
394=== added file 'snappy/verify.go'
395--- snappy/verify.go 1970-01-01 00:00:00 +0000
396+++ snappy/verify.go 2015-06-30 03:40:28 +0000
397@@ -0,0 +1,48 @@
398+// -*- Mode: Go; indent-tabs-mode: t -*-
399+
400+/*
401+ * Copyright (C) 2014-2015 Canonical Ltd
402+ *
403+ * This program is free software: you can redistribute it and/or modify
404+ * it under the terms of the GNU General Public License version 3 as
405+ * published by the Free Software Foundation.
406+ *
407+ * This program is distributed in the hope that it will be useful,
408+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
409+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
410+ * GNU General Public License for more details.
411+ *
412+ * You should have received a copy of the GNU General Public License
413+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
414+ *
415+ */
416+
417+package snappy
418+
419+import (
420+ "fmt"
421+
422+ "launchpad.net/snappy/progress"
423+)
424+
425+// VerifyInstalled verifies all installed snaps
426+func VerifyInstalled(pb progress.Meter) error {
427+ m := NewMetaLocalRepository()
428+ installed, err := m.Installed()
429+ if err != nil {
430+ return err
431+ }
432+
433+ for _, part := range installed {
434+ if _, ok := part.(*SnapPart); !ok {
435+ continue
436+ }
437+ pb.Notify(fmt.Sprintf("Verifying %s", part.Name()))
438+ if err := part.(*SnapPart).Verify(pb); err != nil {
439+ return fmt.Errorf("verify for %v failed: %v", part.Name(), err)
440+ }
441+ }
442+ pb.Notify(fmt.Sprintf("Verified %d successfully", len(installed)))
443+
444+ return nil
445+}
446
447=== added file 'snappy/verify_test.go'
448--- snappy/verify_test.go 1970-01-01 00:00:00 +0000
449+++ snappy/verify_test.go 2015-06-30 03:40:28 +0000
450@@ -0,0 +1,35 @@
451+// -*- Mode: Go; indent-tabs-mode: t -*-
452+
453+/*
454+ * Copyright (C) 2014-2015 Canonical Ltd
455+ *
456+ * This program is free software: you can redistribute it and/or modify
457+ * it under the terms of the GNU General Public License version 3 as
458+ * published by the Free Software Foundation.
459+ *
460+ * This program is distributed in the hope that it will be useful,
461+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
462+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
463+ * GNU General Public License for more details.
464+ *
465+ * You should have received a copy of the GNU General Public License
466+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
467+ *
468+ */
469+
470+package snappy
471+
472+import (
473+ "path/filepath"
474+
475+ . "gopkg.in/check.v1"
476+)
477+
478+func (s *SnapTestSuite) TestSnapVerifyWorksWithSystemImage(c *C) {
479+ systemImageRoot = c.MkDir()
480+
481+ makeFakeSystemImageChannelConfig(c, filepath.Join(systemImageRoot, systemImageChannelConfig), "1")
482+
483+ err := VerifyInstalled(&MockProgressMeter{})
484+ c.Assert(err, IsNil)
485+}

Subscribers

People subscribed via source and target branches