Merge lp:~mvo/snappy/snappy-lp1460152-workaround-15.04 into lp:~snappy-dev/snappy/snappy-moved-to-github
- snappy-lp1460152-workaround-15.04
- Merge into snappy-moved-to-github
Status: | Superseded |
---|---|
Proposed branch: | lp:~mvo/snappy/snappy-lp1460152-workaround-15.04 |
Merge into: | lp:~snappy-dev/snappy/snappy-moved-to-github |
Diff against target: |
1022 lines (+690/-17) (has conflicts) 16 files modified
helpers/helpers.go (+186/-8) helpers/helpers_test.go (+116/-0) partition/bootloader_uboot.go (+1/-4) partition/partition.go (+3/-0) progress/progress.go (+6/-2) progress/progress_test.go (+1/-1) snappy/click.go (+260/-1) snappy/dirs.go (+4/-0) snappy/errors.go (+2/-1) snappy/snapp.go (+17/-0) snappy/snapp_test.go (+19/-0) snappy/systemimage.go (+23/-0) snappy/systemimage_native.go (+2/-0) snappy/systemimage_test.go (+30/-0) systemd/systemd.go (+14/-0) systemd/systemd_test.go (+6/-0) Text conflict in helpers/helpers.go Text conflict in helpers/helpers_test.go Text conflict in snappy/click.go Text conflict in snappy/snapp.go Text conflict in snappy/snapp_test.go Text conflict in systemd/systemd.go Text conflict in systemd/systemd_test.go |
To merge this branch: | bzr merge lp:~mvo/snappy/snappy-lp1460152-workaround-15.04 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Snappy Developers | Pending | ||
Review via email: mp+261147@code.launchpad.net |
Commit message
Description of the change
This is a cherry pick of the workaround for lp #1460152 for the 15.04
branch.
Unmerged revisions
- 451. By Michael Vogt
-
cherry pick r486 from lp:~mvo/snappy/snappy-lp1460152-workaround
- 450. By Sergio Schvezov
-
Don't allow installation of packages with unsupported architectures (backported from trunk with bzr merge -c 484 from trunk) by sergiusens approved by chipaca
- 449. By Michael Terry
-
Allow ".." in file paths as long as it isn't an entire element. (i.e. allow "hello..you" or "...")
We call filepath.Clean right before our ".." check. So the only instances of ".." will be in the first element. That lets us simplify the check a lot.
This is a backport of r457 from trunk. by mterry approved by chipaca
- 448. By Michael Terry
-
Fix intermittent "permission denied" errors when installing snaps by making sure that we always unpack in the same OS thread that dropped its privileges.
This is a backport of r481 from trunk. by mterry approved by chipaca
- 447. By Michael Terry
-
Unify handling of app environment variables. Now hooks, binaries, and services all have the same variables (mostly).
This is a backport of r483 from trunk. by mterry approved by chipaca
- 446. By Sergio Schvezov
-
Backport revno 470 from trunk: Avoid writing to snappy-system.txt if not required by sergiusens approved by mvo
- 445. By Michael Vogt
-
Improve error reporting for failed hooks. by mvo approved by chipaca
- 444. By Michael Vogt
-
Only update changed files in SyncBootloader(). by mvo approved by chipaca
- 443. By David Callé
-
Sync markdown of docs/meta.md with trunk by davidc3 approved by mvo
Preview Diff
1 | === modified file 'clickdeb/deb.go' | |||
2 | === modified file 'clickdeb/deb_test.go' | |||
3 | === modified file 'cmd/snappy/cmd_build.go' | |||
4 | === modified file 'cmd/snappy/cmd_internal_unpack.go' | |||
5 | === modified file 'helpers/helpers.go' | |||
6 | --- helpers/helpers.go 2015-06-04 12:55:54 +0000 | |||
7 | +++ helpers/helpers.go 2015-06-04 20:51:08 +0000 | |||
8 | @@ -26,6 +26,7 @@ | |||
9 | 26 | "encoding/hex" | 26 | "encoding/hex" |
10 | 27 | "fmt" | 27 | "fmt" |
11 | 28 | "io" | 28 | "io" |
12 | 29 | "io/ioutil" | ||
13 | 29 | "math/rand" | 30 | "math/rand" |
14 | 30 | "os" | 31 | "os" |
15 | 31 | "os/exec" | 32 | "os/exec" |
16 | @@ -197,14 +198,37 @@ | |||
17 | 197 | } | 198 | } |
18 | 198 | } | 199 | } |
19 | 199 | 200 | ||
28 | 200 | // IsSupportedArchitecture returns true if the system architecture is in the | 201 | <<<<<<< TREE |
29 | 201 | // list of architectures. | 202 | // IsSupportedArchitecture returns true if the system architecture is in the |
30 | 202 | func IsSupportedArchitecture(architectures []string) bool { | 203 | // list of architectures. |
31 | 203 | systemArch := UbuntuArchitecture() | 204 | func IsSupportedArchitecture(architectures []string) bool { |
32 | 204 | 205 | systemArch := UbuntuArchitecture() | |
33 | 205 | for _, arch := range architectures { | 206 | |
34 | 206 | if arch == "all" || arch == systemArch { | 207 | for _, arch := range architectures { |
35 | 207 | return true | 208 | if arch == "all" || arch == systemArch { |
36 | 209 | return true | ||
37 | 210 | ======= | ||
38 | 211 | // IsSupportedArchitecture returns true if the system architecture is in the | ||
39 | 212 | // list of architectures. | ||
40 | 213 | func IsSupportedArchitecture(architectures []string) bool { | ||
41 | 214 | systemArch := UbuntuArchitecture() | ||
42 | 215 | |||
43 | 216 | for _, arch := range architectures { | ||
44 | 217 | if arch == "all" || arch == systemArch { | ||
45 | 218 | return true | ||
46 | 219 | } | ||
47 | 220 | } | ||
48 | 221 | |||
49 | 222 | return false | ||
50 | 223 | } | ||
51 | 224 | |||
52 | 225 | // EnsureDir ensures that the given directory exists and if | ||
53 | 226 | // not creates it with the given permissions. | ||
54 | 227 | func EnsureDir(dir string, perm os.FileMode) (err error) { | ||
55 | 228 | if _, err := os.Stat(dir); os.IsNotExist(err) { | ||
56 | 229 | if err := os.MkdirAll(dir, perm); err != nil { | ||
57 | 230 | return err | ||
58 | 231 | >>>>>>> MERGE-SOURCE | ||
59 | 208 | } | 232 | } |
60 | 209 | } | 233 | } |
61 | 210 | 234 | ||
62 | @@ -345,6 +369,7 @@ | |||
63 | 345 | return syscall.Getuid() == 0 || syscall.Getgid() == 0 | 369 | return syscall.Getuid() == 0 || syscall.Getgid() == 0 |
64 | 346 | 370 | ||
65 | 347 | } | 371 | } |
66 | 372 | <<<<<<< TREE | ||
67 | 348 | 373 | ||
68 | 349 | // MajorMinor returns the major/minor number of the given os.FileInfo | 374 | // MajorMinor returns the major/minor number of the given os.FileInfo |
69 | 350 | func MajorMinor(info os.FileInfo) (uint32, uint32, error) { | 375 | func MajorMinor(info os.FileInfo) (uint32, uint32, error) { |
70 | @@ -436,3 +461,156 @@ | |||
71 | 436 | "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}", | 461 | "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}", |
72 | 437 | }) | 462 | }) |
73 | 438 | } | 463 | } |
74 | 464 | ======= | ||
75 | 465 | |||
76 | 466 | func makeDirMap(dirName string) (map[string]struct{}, error) { | ||
77 | 467 | dir, err := ioutil.ReadDir(dirName) | ||
78 | 468 | if err != nil { | ||
79 | 469 | return nil, err | ||
80 | 470 | } | ||
81 | 471 | |||
82 | 472 | dirMap := make(map[string]struct{}) | ||
83 | 473 | for _, dent := range dir { | ||
84 | 474 | if dent.IsDir() { | ||
85 | 475 | return nil, fmt.Errorf("subdirs are not supported") | ||
86 | 476 | } | ||
87 | 477 | dirMap[dent.Name()] = struct{}{} | ||
88 | 478 | } | ||
89 | 479 | |||
90 | 480 | return dirMap, nil | ||
91 | 481 | } | ||
92 | 482 | |||
93 | 483 | // RSyncWithDelete syncs srcDir to destDir | ||
94 | 484 | func RSyncWithDelete(srcDirName, destDirName string) error { | ||
95 | 485 | |||
96 | 486 | // first remove everything thats not in srcdir | ||
97 | 487 | err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error { | ||
98 | 488 | if err != nil { | ||
99 | 489 | return err | ||
100 | 490 | } | ||
101 | 491 | |||
102 | 492 | // relative to the root "destDirName" | ||
103 | 493 | relPath := path[len(destDirName):] | ||
104 | 494 | if !FileExists(filepath.Join(srcDirName, relPath)) { | ||
105 | 495 | if err := os.RemoveAll(path); err != nil { | ||
106 | 496 | return err | ||
107 | 497 | } | ||
108 | 498 | if info.IsDir() { | ||
109 | 499 | return filepath.SkipDir | ||
110 | 500 | } | ||
111 | 501 | } | ||
112 | 502 | return nil | ||
113 | 503 | }) | ||
114 | 504 | if err != nil { | ||
115 | 505 | return err | ||
116 | 506 | } | ||
117 | 507 | |||
118 | 508 | // then copy or update the data from srcdir to destdir | ||
119 | 509 | err = filepath.Walk(srcDirName, func(path string, info os.FileInfo, err error) error { | ||
120 | 510 | if err != nil { | ||
121 | 511 | return err | ||
122 | 512 | } | ||
123 | 513 | |||
124 | 514 | // relative to the root "srcDirName" | ||
125 | 515 | relPath := path[len(srcDirName):] | ||
126 | 516 | if info.IsDir() { | ||
127 | 517 | return os.MkdirAll(filepath.Join(destDirName, relPath), info.Mode()) | ||
128 | 518 | } | ||
129 | 519 | src := path | ||
130 | 520 | dst := filepath.Join(destDirName, relPath) | ||
131 | 521 | if !FilesAreEqual(src, dst) { | ||
132 | 522 | // XXX: on snappy-trunk we can use CopyFile here | ||
133 | 523 | output, err := exec.Command("cp", "-va", src, dst).CombinedOutput() | ||
134 | 524 | if err != nil { | ||
135 | 525 | fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output) | ||
136 | 526 | } | ||
137 | 527 | } | ||
138 | 528 | return nil | ||
139 | 529 | }) | ||
140 | 530 | |||
141 | 531 | return err | ||
142 | 532 | } | ||
143 | 533 | |||
144 | 534 | func fillSnapEnvVars(desc interface{}, vars []string) []string { | ||
145 | 535 | for i, v := range vars { | ||
146 | 536 | var templateOut bytes.Buffer | ||
147 | 537 | t := template.Must(template.New("wrapper").Parse(v)) | ||
148 | 538 | if err := t.Execute(&templateOut, desc); err != nil { | ||
149 | 539 | // this can never happen, except we forget a variable | ||
150 | 540 | logger.LogAndPanic(err) | ||
151 | 541 | } | ||
152 | 542 | vars[i] = templateOut.String() | ||
153 | 543 | } | ||
154 | 544 | return vars | ||
155 | 545 | } | ||
156 | 546 | |||
157 | 547 | // GetBasicSnapEnvVars returns the app-level environment variables for a snap. | ||
158 | 548 | // Despite this being a bit snap-specific, this is in helpers.go because it's | ||
159 | 549 | // used by so many other modules, we run into circular dependencies if it's | ||
160 | 550 | // somewhere more reasonable like the snappy module. | ||
161 | 551 | func GetBasicSnapEnvVars(desc interface{}) []string { | ||
162 | 552 | return fillSnapEnvVars(desc, []string{ | ||
163 | 553 | "TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp", | ||
164 | 554 | "TEMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp", | ||
165 | 555 | "SNAP_APP_PATH={{.AppPath}}", | ||
166 | 556 | "SNAP_APP_DATA_PATH=/var/lib{{.AppPath}}", | ||
167 | 557 | "SNAP_APP_TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp", | ||
168 | 558 | "SNAP_NAME={{.AppName}}", | ||
169 | 559 | "SNAP_VERSION={{.Version}}", | ||
170 | 560 | "SNAP_ORIGIN={{.Namespace}}", | ||
171 | 561 | "SNAP_FULLNAME={{.UdevAppName}}", | ||
172 | 562 | "SNAP_ARCH={{.AppArch}}", | ||
173 | 563 | }) | ||
174 | 564 | } | ||
175 | 565 | |||
176 | 566 | // GetUserSnapEnvVars returns the user-level environment variables for a snap. | ||
177 | 567 | // Despite this being a bit snap-specific, this is in helpers.go because it's | ||
178 | 568 | // used by so many other modules, we run into circular dependencies if it's | ||
179 | 569 | // somewhere more reasonable like the snappy module. | ||
180 | 570 | func GetUserSnapEnvVars(desc interface{}) []string { | ||
181 | 571 | return fillSnapEnvVars(desc, []string{ | ||
182 | 572 | "SNAP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}", | ||
183 | 573 | }) | ||
184 | 574 | } | ||
185 | 575 | |||
186 | 576 | // GetDeprecatedBasicSnapEnvVars returns the app-level deprecated environment | ||
187 | 577 | // variables for a snap. | ||
188 | 578 | // Despite this being a bit snap-specific, this is in helpers.go because it's | ||
189 | 579 | // used by so many other modules, we run into circular dependencies if it's | ||
190 | 580 | // somewhere more reasonable like the snappy module. | ||
191 | 581 | func GetDeprecatedBasicSnapEnvVars(desc interface{}) []string { | ||
192 | 582 | return fillSnapEnvVars(desc, []string{ | ||
193 | 583 | "SNAPP_APP_PATH={{.AppPath}}", | ||
194 | 584 | "SNAPP_APP_DATA_PATH=/var/lib{{.AppPath}}", | ||
195 | 585 | "SNAPP_APP_TMPDIR=/tmp/snaps/{{.UdevAppName}}/{{.Version}}/tmp", | ||
196 | 586 | "SNAPPY_APP_ARCH={{.AppArch}}", | ||
197 | 587 | }) | ||
198 | 588 | } | ||
199 | 589 | |||
200 | 590 | // GetDeprecatedUserSnapEnvVars returns the user-level deprecated environment | ||
201 | 591 | // variables for a snap. | ||
202 | 592 | // Despite this being a bit snap-specific, this is in helpers.go because it's | ||
203 | 593 | // used by so many other modules, we run into circular dependencies if it's | ||
204 | 594 | // somewhere more reasonable like the snappy module. | ||
205 | 595 | func GetDeprecatedUserSnapEnvVars(desc interface{}) []string { | ||
206 | 596 | return fillSnapEnvVars(desc, []string{ | ||
207 | 597 | "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}", | ||
208 | 598 | }) | ||
209 | 599 | } | ||
210 | 600 | |||
211 | 601 | // CleanDir cleans all regular files in the given directory | ||
212 | 602 | func CleanDir(dir string) error { | ||
213 | 603 | dents, err := ioutil.ReadDir(dir) | ||
214 | 604 | if err != nil { | ||
215 | 605 | return err | ||
216 | 606 | } | ||
217 | 607 | for _, dent := range dents { | ||
218 | 608 | if dent.Mode().IsRegular() { | ||
219 | 609 | if err := os.Remove(filepath.Join(dir, dent.Name())); err != nil { | ||
220 | 610 | return err | ||
221 | 611 | } | ||
222 | 612 | } | ||
223 | 613 | } | ||
224 | 614 | return nil | ||
225 | 615 | } | ||
226 | 616 | >>>>>>> MERGE-SOURCE | ||
227 | 439 | 617 | ||
228 | === modified file 'helpers/helpers_test.go' | |||
229 | --- helpers/helpers_test.go 2015-06-04 12:55:54 +0000 | |||
230 | +++ helpers/helpers_test.go 2015-06-04 20:51:08 +0000 | |||
231 | @@ -21,6 +21,7 @@ | |||
232 | 21 | 21 | ||
233 | 22 | import ( | 22 | import ( |
234 | 23 | "compress/gzip" | 23 | "compress/gzip" |
235 | 24 | "fmt" | ||
236 | 24 | "io/ioutil" | 25 | "io/ioutil" |
237 | 25 | "math/rand" | 26 | "math/rand" |
238 | 26 | "os" | 27 | "os" |
239 | @@ -270,6 +271,7 @@ | |||
240 | 270 | c.Assert(err, IsNil) | 271 | c.Assert(err, IsNil) |
241 | 271 | c.Assert(home, Equals, oldHome) | 272 | c.Assert(home, Equals, oldHome) |
242 | 272 | } | 273 | } |
243 | 274 | <<<<<<< TREE | ||
244 | 273 | 275 | ||
245 | 274 | func skipOnMissingDevKmsg(c *C) { | 276 | func skipOnMissingDevKmsg(c *C) { |
246 | 275 | _, err := os.Stat("/dev/kmsg") | 277 | _, err := os.Stat("/dev/kmsg") |
247 | @@ -328,3 +330,117 @@ | |||
248 | 328 | c.Assert(mknodWasCalled, Equals, true) | 330 | c.Assert(mknodWasCalled, Equals, true) |
249 | 329 | 331 | ||
250 | 330 | } | 332 | } |
251 | 333 | ======= | ||
252 | 334 | |||
253 | 335 | func makeTestFiles(c *C, srcDir, destDir string) { | ||
254 | 336 | // a new file | ||
255 | 337 | err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644) | ||
256 | 338 | c.Assert(err, IsNil) | ||
257 | 339 | |||
258 | 340 | // a existing file that needs update | ||
259 | 341 | err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644) | ||
260 | 342 | c.Assert(err, IsNil) | ||
261 | 343 | err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644) | ||
262 | 344 | c.Assert(err, IsNil) | ||
263 | 345 | |||
264 | 346 | // existing file that needs no update | ||
265 | 347 | err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644) | ||
266 | 348 | c.Assert(err, IsNil) | ||
267 | 349 | err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run() | ||
268 | 350 | c.Assert(err, IsNil) | ||
269 | 351 | |||
270 | 352 | // a file that needs removal | ||
271 | 353 | err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644) | ||
272 | 354 | c.Assert(err, IsNil) | ||
273 | 355 | } | ||
274 | 356 | |||
275 | 357 | func compareDirs(c *C, srcDir, destDir string) { | ||
276 | 358 | d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput() | ||
277 | 359 | c.Assert(err, IsNil) | ||
278 | 360 | d2, err := exec.Command("ls", "-al", destDir).CombinedOutput() | ||
279 | 361 | c.Assert(err, IsNil) | ||
280 | 362 | c.Assert(string(d1), Equals, string(d2)) | ||
281 | 363 | // ensure content got updated | ||
282 | 364 | c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput() | ||
283 | 365 | c.Assert(err, IsNil) | ||
284 | 366 | c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput() | ||
285 | 367 | c.Assert(err, IsNil) | ||
286 | 368 | c.Assert(string(c1), Equals, string(c2)) | ||
287 | 369 | } | ||
288 | 370 | |||
289 | 371 | func (ts *HTestSuite) TestSyncDirs(c *C) { | ||
290 | 372 | |||
291 | 373 | for _, l := range [][2]string{ | ||
292 | 374 | [2]string{"src-short", "dst-loooooooooooong"}, | ||
293 | 375 | [2]string{"src-loooooooooooong", "dst-short"}, | ||
294 | 376 | [2]string{"src-eq", "dst-eq"}, | ||
295 | 377 | } { | ||
296 | 378 | |||
297 | 379 | // ensure we have src, dest dirs with different length | ||
298 | 380 | srcDir := filepath.Join(c.MkDir(), l[0]) | ||
299 | 381 | err := os.MkdirAll(srcDir, 0755) | ||
300 | 382 | c.Assert(err, IsNil) | ||
301 | 383 | destDir := filepath.Join(c.MkDir(), l[1]) | ||
302 | 384 | err = os.MkdirAll(destDir, 0755) | ||
303 | 385 | c.Assert(err, IsNil) | ||
304 | 386 | |||
305 | 387 | // add a src subdir | ||
306 | 388 | subdir := filepath.Join(srcDir, "subdir") | ||
307 | 389 | err = os.Mkdir(subdir, 0755) | ||
308 | 390 | c.Assert(err, IsNil) | ||
309 | 391 | makeTestFiles(c, subdir, destDir) | ||
310 | 392 | |||
311 | 393 | // add a dst subdir that needs to get deleted | ||
312 | 394 | subdir2 := filepath.Join(destDir, "to-be-deleted-subdir") | ||
313 | 395 | err = os.Mkdir(subdir2, 0755) | ||
314 | 396 | subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir") | ||
315 | 397 | err = os.Mkdir(subdir3, 0755) | ||
316 | 398 | |||
317 | 399 | // and a toplevel | ||
318 | 400 | makeTestFiles(c, srcDir, destDir) | ||
319 | 401 | |||
320 | 402 | // do it | ||
321 | 403 | err = RSyncWithDelete(srcDir, destDir) | ||
322 | 404 | c.Assert(err, IsNil) | ||
323 | 405 | |||
324 | 406 | // ensure meta-data is identical | ||
325 | 407 | compareDirs(c, srcDir, destDir) | ||
326 | 408 | compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir")) | ||
327 | 409 | } | ||
328 | 410 | } | ||
329 | 411 | |||
330 | 412 | func (ts *HTestSuite) TestSyncDirFails(c *C) { | ||
331 | 413 | srcDir := c.MkDir() | ||
332 | 414 | err := os.MkdirAll(srcDir, 0755) | ||
333 | 415 | c.Assert(err, IsNil) | ||
334 | 416 | |||
335 | 417 | destDir := c.MkDir() | ||
336 | 418 | err = os.MkdirAll(destDir, 0755) | ||
337 | 419 | c.Assert(err, IsNil) | ||
338 | 420 | |||
339 | 421 | err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644) | ||
340 | 422 | c.Assert(err, IsNil) | ||
341 | 423 | |||
342 | 424 | // ensure remove fails | ||
343 | 425 | err = os.Chmod(destDir, 0100) | ||
344 | 426 | c.Assert(err, IsNil) | ||
345 | 427 | // make tempdir cleanup work again | ||
346 | 428 | defer os.Chmod(destDir, 0755) | ||
347 | 429 | |||
348 | 430 | // do it | ||
349 | 431 | err = RSyncWithDelete(srcDir, destDir) | ||
350 | 432 | c.Check(err, NotNil) | ||
351 | 433 | c.Check(err, ErrorMatches, ".*permission denied.*") | ||
352 | 434 | } | ||
353 | 435 | |||
354 | 436 | func (ts *HTestSuite) TestCleanDir(c *C) { | ||
355 | 437 | d := c.MkDir() | ||
356 | 438 | canary := filepath.Join(d, "foo") | ||
357 | 439 | err := ioutil.WriteFile(canary, []byte(nil), 0644) | ||
358 | 440 | c.Assert(err, IsNil) | ||
359 | 441 | |||
360 | 442 | err = CleanDir(d) | ||
361 | 443 | c.Assert(err, IsNil) | ||
362 | 444 | c.Assert(FileExists(canary), Equals, false) | ||
363 | 445 | } | ||
364 | 446 | >>>>>>> MERGE-SOURCE | ||
365 | 331 | 447 | ||
366 | === modified file 'partition/bootloader_uboot.go' | |||
367 | --- partition/bootloader_uboot.go 2015-05-29 09:55:15 +0000 | |||
368 | +++ partition/bootloader_uboot.go 2015-06-04 20:51:08 +0000 | |||
369 | @@ -221,10 +221,7 @@ | |||
370 | 221 | srcDir := u.currentBootPath | 221 | srcDir := u.currentBootPath |
371 | 222 | destDir := u.otherBootPath | 222 | destDir := u.otherBootPath |
372 | 223 | 223 | ||
377 | 224 | // always start from scratch: all files here are owned by us. | 224 | return helpers.RSyncWithDelete(srcDir, destDir) |
374 | 225 | os.RemoveAll(destDir) | ||
375 | 226 | |||
376 | 227 | return runCommand("/bin/cp", "-a", srcDir, destDir) | ||
378 | 228 | } | 225 | } |
379 | 229 | 226 | ||
380 | 230 | func (u *uboot) HandleAssets() (err error) { | 227 | func (u *uboot) HandleAssets() (err error) { |
381 | 231 | 228 | ||
382 | === modified file 'partition/bootloader_uboot_test.go' | |||
383 | === modified file 'partition/partition.go' | |||
384 | --- partition/partition.go 2015-05-29 09:55:15 +0000 | |||
385 | +++ partition/partition.go 2015-06-04 20:51:08 +0000 | |||
386 | @@ -814,6 +814,9 @@ | |||
387 | 814 | return err | 814 | return err |
388 | 815 | } | 815 | } |
389 | 816 | 816 | ||
390 | 817 | // XXX: first toggle roofs and then handle assets? that seems | ||
391 | 818 | // wrong given that handleAssets may fails and we will | ||
392 | 819 | // knowingly boot into a broken system | ||
393 | 817 | err = p.RunWithOther(RW, func(otherRoot string) (err error) { | 820 | err = p.RunWithOther(RW, func(otherRoot string) (err error) { |
394 | 818 | return bootloader.ToggleRootFS() | 821 | return bootloader.ToggleRootFS() |
395 | 819 | }) | 822 | }) |
396 | 820 | 823 | ||
397 | === modified file 'progress/progress.go' | |||
398 | --- progress/progress.go 2015-05-15 13:33:27 +0000 | |||
399 | +++ progress/progress.go 2015-06-04 20:51:08 +0000 | |||
400 | @@ -129,8 +129,9 @@ | |||
401 | 129 | // Finished stops displaying the progress | 129 | // Finished stops displaying the progress |
402 | 130 | func (t *TextProgress) Finished() { | 130 | func (t *TextProgress) Finished() { |
403 | 131 | if t.pbar != nil { | 131 | if t.pbar != nil { |
405 | 132 | t.pbar.FinishPrint("Done") | 132 | t.pbar.Finish() |
406 | 133 | } | 133 | } |
407 | 134 | fmt.Println("Done") | ||
408 | 134 | } | 135 | } |
409 | 135 | 136 | ||
410 | 136 | // Write is there so that progress can implment a Writer and can be | 137 | // Write is there so that progress can implment a Writer and can be |
411 | @@ -143,7 +144,10 @@ | |||
412 | 143 | // that have a unknown duration | 144 | // that have a unknown duration |
413 | 144 | func (t *TextProgress) Spin(msg string) { | 145 | func (t *TextProgress) Spin(msg string) { |
414 | 145 | states := `|/-\` | 146 | states := `|/-\` |
416 | 146 | fmt.Printf("\r%s[%c]", msg, states[t.spinStep]) | 147 | |
417 | 148 | // clear until end of line | ||
418 | 149 | clearUntilEOL := "\033[K" | ||
419 | 150 | fmt.Printf("\r%s[%c]%s", msg, states[t.spinStep], clearUntilEOL) | ||
420 | 147 | t.spinStep++ | 151 | t.spinStep++ |
421 | 148 | if t.spinStep >= len(states) { | 152 | if t.spinStep >= len(states) { |
422 | 149 | t.spinStep = 0 | 153 | t.spinStep = 0 |
423 | 150 | 154 | ||
424 | === modified file 'progress/progress_test.go' | |||
425 | --- progress/progress_test.go 2015-06-02 20:53:10 +0000 | |||
426 | +++ progress/progress_test.go 2015-06-04 20:51:08 +0000 | |||
427 | @@ -60,7 +60,7 @@ | |||
428 | 60 | f.Seek(0, 0) | 60 | f.Seek(0, 0) |
429 | 61 | progress, err := ioutil.ReadAll(f) | 61 | progress, err := ioutil.ReadAll(f) |
430 | 62 | c.Assert(err, IsNil) | 62 | c.Assert(err, IsNil) |
432 | 63 | c.Assert(string(progress), Equals, "\rm[|]\rm[/]\rm[-]\rm[\\]\rm[|]\rm[/]") | 63 | c.Assert(string(progress), Equals, "\rm[|]\x1b[K\rm[/]\x1b[K\rm[-]\x1b[K\rm[\\]\x1b[K\rm[|]\x1b[K\rm[/]\x1b[K") |
433 | 64 | } | 64 | } |
434 | 65 | 65 | ||
435 | 66 | func (ts *ProgressTestSuite) testAgreed(answer string, value bool, c *C) { | 66 | func (ts *ProgressTestSuite) testAgreed(answer string, value bool, c *C) { |
436 | 67 | 67 | ||
437 | === modified file 'snappy/click.go' | |||
438 | --- snappy/click.go 2015-05-29 14:28:36 +0000 | |||
439 | +++ snappy/click.go 2015-06-04 20:51:08 +0000 | |||
440 | @@ -92,9 +92,10 @@ | |||
441 | 92 | func execHook(execCmd string) (err error) { | 92 | func execHook(execCmd string) (err error) { |
442 | 93 | // the spec says this is passed to the shell | 93 | // the spec says this is passed to the shell |
443 | 94 | cmd := exec.Command("sh", "-c", execCmd) | 94 | cmd := exec.Command("sh", "-c", execCmd) |
445 | 95 | if err = cmd.Run(); err != nil { | 95 | if output, err := cmd.CombinedOutput(); err != nil { |
446 | 96 | if exitCode, err := helpers.ExitCode(err); err == nil { | 96 | if exitCode, err := helpers.ExitCode(err); err == nil { |
447 | 97 | return &ErrHookFailed{cmd: execCmd, | 97 | return &ErrHookFailed{cmd: execCmd, |
448 | 98 | output: string(output), | ||
449 | 98 | exitCode: exitCode} | 99 | exitCode: exitCode} |
450 | 99 | } | 100 | } |
451 | 100 | return err | 101 | return err |
452 | @@ -362,9 +363,15 @@ | |||
453 | 362 | AppArch string | 363 | AppArch string |
454 | 363 | AppPath string | 364 | AppPath string |
455 | 364 | Version string | 365 | Version string |
456 | 366 | <<<<<<< TREE | ||
457 | 365 | UdevAppName string | 367 | UdevAppName string |
458 | 366 | Origin string | 368 | Origin string |
459 | 367 | Home string | 369 | Home string |
460 | 370 | ======= | ||
461 | 371 | UdevAppName string | ||
462 | 372 | Namespace string | ||
463 | 373 | Home string | ||
464 | 374 | >>>>>>> MERGE-SOURCE | ||
465 | 368 | Target string | 375 | Target string |
466 | 369 | AaProfile string | 376 | AaProfile string |
467 | 370 | OldAppVars string | 377 | OldAppVars string |
468 | @@ -374,9 +381,15 @@ | |||
469 | 374 | AppArch: helpers.UbuntuArchitecture(), | 381 | AppArch: helpers.UbuntuArchitecture(), |
470 | 375 | AppPath: pkgPath, | 382 | AppPath: pkgPath, |
471 | 376 | Version: m.Version, | 383 | Version: m.Version, |
472 | 384 | <<<<<<< TREE | ||
473 | 377 | UdevAppName: udevPartName, | 385 | UdevAppName: udevPartName, |
474 | 378 | Origin: origin, | 386 | Origin: origin, |
475 | 379 | Home: "$HOME", | 387 | Home: "$HOME", |
476 | 388 | ======= | ||
477 | 389 | UdevAppName: udevPartName, | ||
478 | 390 | Namespace: namespace, | ||
479 | 391 | Home: "$HOME", | ||
480 | 392 | >>>>>>> MERGE-SOURCE | ||
481 | 380 | Target: actualBinPath, | 393 | Target: actualBinPath, |
482 | 381 | AaProfile: aaProfile, | 394 | AaProfile: aaProfile, |
483 | 382 | } | 395 | } |
484 | @@ -761,6 +774,7 @@ | |||
485 | 761 | 774 | ||
486 | 762 | func installClick(snapFile string, flags InstallFlags, inter progress.Meter, origin string) (name string, err error) { | 775 | func installClick(snapFile string, flags InstallFlags, inter progress.Meter, origin string) (name string, err error) { |
487 | 763 | allowUnauthenticated := (flags & AllowUnauthenticated) != 0 | 776 | allowUnauthenticated := (flags & AllowUnauthenticated) != 0 |
488 | 777 | <<<<<<< TREE | ||
489 | 764 | part, err := NewSnapPartFromSnapFile(snapFile, origin, allowUnauthenticated) | 778 | part, err := NewSnapPartFromSnapFile(snapFile, origin, allowUnauthenticated) |
490 | 765 | if err != nil { | 779 | if err != nil { |
491 | 766 | return "", err | 780 | return "", err |
492 | @@ -768,6 +782,251 @@ | |||
493 | 768 | defer part.deb.Close() | 782 | defer part.deb.Close() |
494 | 769 | 783 | ||
495 | 770 | return part.Install(inter, flags) | 784 | return part.Install(inter, flags) |
496 | 785 | ======= | ||
497 | 786 | if err := auditClick(snapFile, allowUnauthenticated); err != nil { | ||
498 | 787 | return "", err | ||
499 | 788 | // ? | ||
500 | 789 | //return SnapAuditError | ||
501 | 790 | } | ||
502 | 791 | |||
503 | 792 | d, err := clickdeb.Open(snapFile) | ||
504 | 793 | if err != nil { | ||
505 | 794 | return "", err | ||
506 | 795 | } | ||
507 | 796 | defer d.Close() | ||
508 | 797 | |||
509 | 798 | manifestData, err := d.ControlMember("manifest") | ||
510 | 799 | if err != nil { | ||
511 | 800 | log.Printf("Snap inspect failed: %s", snapFile) | ||
512 | 801 | return "", err | ||
513 | 802 | } | ||
514 | 803 | |||
515 | 804 | manifest, err := readClickManifest([]byte(manifestData)) | ||
516 | 805 | if err != nil { | ||
517 | 806 | return "", err | ||
518 | 807 | } | ||
519 | 808 | |||
520 | 809 | yamlData, err := d.MetaMember("package.yaml") | ||
521 | 810 | if err != nil { | ||
522 | 811 | return "", err | ||
523 | 812 | } | ||
524 | 813 | |||
525 | 814 | m, err := parsePackageYamlData(yamlData) | ||
526 | 815 | if err != nil { | ||
527 | 816 | return "", err | ||
528 | 817 | } | ||
529 | 818 | |||
530 | 819 | if err := m.checkForPackageInstalled(namespace); err != nil { | ||
531 | 820 | return "", err | ||
532 | 821 | } | ||
533 | 822 | |||
534 | 823 | // verify we have a valid architecture | ||
535 | 824 | if !helpers.IsSupportedArchitecture(m.Architectures) { | ||
536 | 825 | return "", &ErrArchitectureNotSupported{m.Architectures} | ||
537 | 826 | } | ||
538 | 827 | |||
539 | 828 | if err := m.checkForNameClashes(); err != nil { | ||
540 | 829 | return "", err | ||
541 | 830 | } | ||
542 | 831 | |||
543 | 832 | if err := m.checkForFrameworks(); err != nil { | ||
544 | 833 | return "", err | ||
545 | 834 | } | ||
546 | 835 | |||
547 | 836 | targetDir := snapAppsDir | ||
548 | 837 | // the "oem" parts are special | ||
549 | 838 | if manifest.Type == SnapTypeOem { | ||
550 | 839 | targetDir = snapOemDir | ||
551 | 840 | |||
552 | 841 | // TODO do the following at a higher level once the store publishes snap types | ||
553 | 842 | // this is horrible | ||
554 | 843 | if allowOEM := (flags & AllowOEM) != 0; !allowOEM { | ||
555 | 844 | if currentOEM, err := getOem(); err == nil { | ||
556 | 845 | if currentOEM.Name != manifest.Name { | ||
557 | 846 | fmt.Println(currentOEM.Name, manifest.Name) | ||
558 | 847 | return "", ErrOEMPackageInstall | ||
559 | 848 | } | ||
560 | 849 | } else { | ||
561 | 850 | // there should always be an oem package now | ||
562 | 851 | return "", ErrOEMPackageInstall | ||
563 | 852 | } | ||
564 | 853 | } | ||
565 | 854 | |||
566 | 855 | if err := installOemHardwareUdevRules(m); err != nil { | ||
567 | 856 | return "", err | ||
568 | 857 | } | ||
569 | 858 | } | ||
570 | 859 | |||
571 | 860 | fullName := manifest.Name | ||
572 | 861 | // namespacing only applies to apps. | ||
573 | 862 | if manifest.Type != SnapTypeFramework && manifest.Type != SnapTypeOem { | ||
574 | 863 | fullName += "." + namespace | ||
575 | 864 | } | ||
576 | 865 | instDir := filepath.Join(targetDir, fullName, manifest.Version) | ||
577 | 866 | currentActiveDir, _ := filepath.EvalSymlinks(filepath.Join(instDir, "..", "current")) | ||
578 | 867 | |||
579 | 868 | if err := m.checkLicenseAgreement(inter, d, currentActiveDir); err != nil { | ||
580 | 869 | return "", err | ||
581 | 870 | } | ||
582 | 871 | |||
583 | 872 | dataDir := filepath.Join(snapDataDir, fullName, manifest.Version) | ||
584 | 873 | |||
585 | 874 | if err := helpers.EnsureDir(instDir, 0755); err != nil { | ||
586 | 875 | log.Printf("WARNING: Can not create %s", instDir) | ||
587 | 876 | } | ||
588 | 877 | |||
589 | 878 | // if anything goes wrong here we cleanup | ||
590 | 879 | defer func() { | ||
591 | 880 | if err != nil { | ||
592 | 881 | if e := os.RemoveAll(instDir); e != nil && !os.IsNotExist(e) { | ||
593 | 882 | log.Printf("Warning: failed to remove %s: %s", instDir, e) | ||
594 | 883 | } | ||
595 | 884 | } | ||
596 | 885 | }() | ||
597 | 886 | |||
598 | 887 | // we need to call the external helper so that we can reliable drop | ||
599 | 888 | // privs | ||
600 | 889 | if err := unpackWithDropPrivs(d, instDir); err != nil { | ||
601 | 890 | return "", err | ||
602 | 891 | } | ||
603 | 892 | |||
604 | 893 | // legacy, the hooks (e.g. apparmor) need this. Once we converted | ||
605 | 894 | // all hooks this can go away | ||
606 | 895 | clickMetaDir := path.Join(instDir, ".click", "info") | ||
607 | 896 | if err := os.MkdirAll(clickMetaDir, 0755); err != nil { | ||
608 | 897 | return "", err | ||
609 | 898 | } | ||
610 | 899 | if err := writeCompatManifestJSON(clickMetaDir, manifestData, namespace); err != nil { | ||
611 | 900 | return "", err | ||
612 | 901 | } | ||
613 | 902 | |||
614 | 903 | // write the hashes now | ||
615 | 904 | if err := writeHashesFile(d, instDir); err != nil { | ||
616 | 905 | return "", err | ||
617 | 906 | } | ||
618 | 907 | |||
619 | 908 | inhibitHooks := (flags & InhibitHooks) != 0 | ||
620 | 909 | |||
621 | 910 | // deal with the data: | ||
622 | 911 | // | ||
623 | 912 | // if there was a previous version, stop it | ||
624 | 913 | // from being active so that it stops running and can no longer be | ||
625 | 914 | // started then copy the data | ||
626 | 915 | // | ||
627 | 916 | // otherwise just create a empty data dir | ||
628 | 917 | if currentActiveDir != "" { | ||
629 | 918 | oldManifest, err := readClickManifestFromClickDir(currentActiveDir) | ||
630 | 919 | if err != nil { | ||
631 | 920 | return "", err | ||
632 | 921 | } | ||
633 | 922 | |||
634 | 923 | // we need to stop making it active | ||
635 | 924 | err = unsetActiveClick(currentActiveDir, inhibitHooks, inter) | ||
636 | 925 | defer func() { | ||
637 | 926 | if err != nil { | ||
638 | 927 | if cerr := setActiveClick(currentActiveDir, inhibitHooks, inter); cerr != nil { | ||
639 | 928 | log.Printf("setting old version back to active failed: %v", cerr) | ||
640 | 929 | } | ||
641 | 930 | } | ||
642 | 931 | }() | ||
643 | 932 | if err != nil { | ||
644 | 933 | return "", err | ||
645 | 934 | } | ||
646 | 935 | |||
647 | 936 | err = copySnapData(fullName, oldManifest.Version, manifest.Version) | ||
648 | 937 | } else { | ||
649 | 938 | err = helpers.EnsureDir(dataDir, 0755) | ||
650 | 939 | } | ||
651 | 940 | |||
652 | 941 | defer func() { | ||
653 | 942 | if err != nil { | ||
654 | 943 | if cerr := removeSnapData(fullName, manifest.Version); cerr != nil { | ||
655 | 944 | log.Printf("when clenaning up data for %s %s: %v", manifest.Name, manifest.Version, cerr) | ||
656 | 945 | } | ||
657 | 946 | } | ||
658 | 947 | }() | ||
659 | 948 | |||
660 | 949 | if err != nil { | ||
661 | 950 | return "", err | ||
662 | 951 | } | ||
663 | 952 | |||
664 | 953 | // and finally make active | ||
665 | 954 | err = setActiveClick(instDir, inhibitHooks, inter) | ||
666 | 955 | defer func() { | ||
667 | 956 | if err != nil && currentActiveDir != "" { | ||
668 | 957 | if cerr := setActiveClick(currentActiveDir, inhibitHooks, inter); cerr != nil { | ||
669 | 958 | log.Printf("when setting old %s version back to active: %v", manifest.Name, cerr) | ||
670 | 959 | } | ||
671 | 960 | } | ||
672 | 961 | }() | ||
673 | 962 | if err != nil { | ||
674 | 963 | return "", err | ||
675 | 964 | } | ||
676 | 965 | |||
677 | 966 | // oh, one more thing: refresh the security bits | ||
678 | 967 | if !inhibitHooks { | ||
679 | 968 | part, err := NewSnapPartFromYaml(filepath.Join(instDir, "meta", "package.yaml"), namespace, m) | ||
680 | 969 | if err != nil { | ||
681 | 970 | return "", err | ||
682 | 971 | } | ||
683 | 972 | |||
684 | 973 | deps, err := part.Dependents() | ||
685 | 974 | if err != nil { | ||
686 | 975 | return "", err | ||
687 | 976 | } | ||
688 | 977 | |||
689 | 978 | sysd := systemd.New(globalRootDir, inter) | ||
690 | 979 | stopped := make(map[string]time.Duration) | ||
691 | 980 | defer func() { | ||
692 | 981 | if err != nil { | ||
693 | 982 | for serviceName := range stopped { | ||
694 | 983 | if e := sysd.Start(serviceName); e != nil { | ||
695 | 984 | inter.Notify(fmt.Sprintf("unable to restart %s with the old %s: %s", serviceName, part.Name(), e)) | ||
696 | 985 | } | ||
697 | 986 | } | ||
698 | 987 | } | ||
699 | 988 | }() | ||
700 | 989 | |||
701 | 990 | for _, dep := range deps { | ||
702 | 991 | if !dep.IsActive() { | ||
703 | 992 | continue | ||
704 | 993 | } | ||
705 | 994 | for _, svc := range dep.Services() { | ||
706 | 995 | serviceName := filepath.Base(generateServiceFileName(dep.m, svc)) | ||
707 | 996 | timeout := time.Duration(svc.StopTimeout) | ||
708 | 997 | if err = sysd.Stop(serviceName, timeout); err != nil { | ||
709 | 998 | inter.Notify(fmt.Sprintf("unable to stop %s; aborting install: %s", serviceName, err)) | ||
710 | 999 | return "", err | ||
711 | 1000 | } | ||
712 | 1001 | stopped[serviceName] = timeout | ||
713 | 1002 | } | ||
714 | 1003 | } | ||
715 | 1004 | |||
716 | 1005 | if err := part.RefreshDependentsSecurity(currentActiveDir, inter); err != nil { | ||
717 | 1006 | return "", err | ||
718 | 1007 | } | ||
719 | 1008 | |||
720 | 1009 | started := make(map[string]time.Duration) | ||
721 | 1010 | defer func() { | ||
722 | 1011 | if err != nil { | ||
723 | 1012 | for serviceName, timeout := range started { | ||
724 | 1013 | if e := sysd.Stop(serviceName, timeout); e != nil { | ||
725 | 1014 | inter.Notify(fmt.Sprintf("unable to stop %s with the old %s: %s", serviceName, part.Name(), e)) | ||
726 | 1015 | } | ||
727 | 1016 | } | ||
728 | 1017 | } | ||
729 | 1018 | }() | ||
730 | 1019 | for serviceName, timeout := range stopped { | ||
731 | 1020 | if err = sysd.Start(serviceName); err != nil { | ||
732 | 1021 | inter.Notify(fmt.Sprintf("unable to restart %s; aborting install: %s", serviceName, err)) | ||
733 | 1022 | return "", err | ||
734 | 1023 | } | ||
735 | 1024 | started[serviceName] = timeout | ||
736 | 1025 | } | ||
737 | 1026 | } | ||
738 | 1027 | |||
739 | 1028 | return manifest.Name, nil | ||
740 | 1029 | >>>>>>> MERGE-SOURCE | ||
741 | 771 | } | 1030 | } |
742 | 772 | 1031 | ||
743 | 773 | // removeSnapData removes the data for the given version of the given snap | 1032 | // removeSnapData removes the data for the given version of the given snap |
744 | 774 | 1033 | ||
745 | === modified file 'snappy/click_test.go' | |||
746 | === modified file 'snappy/dirs.go' | |||
747 | --- snappy/dirs.go 2015-05-15 13:33:27 +0000 | |||
748 | +++ snappy/dirs.go 2015-06-04 20:51:08 +0000 | |||
749 | @@ -33,6 +33,8 @@ | |||
750 | 33 | snapSeccompDir string | 33 | snapSeccompDir string |
751 | 34 | snapUdevRulesDir string | 34 | snapUdevRulesDir string |
752 | 35 | 35 | ||
753 | 36 | systemApparmorProfileCacheDir string | ||
754 | 37 | |||
755 | 36 | snapBinariesDir string | 38 | snapBinariesDir string |
756 | 37 | snapServicesDir string | 39 | snapServicesDir string |
757 | 38 | snapBusPolicyDir string | 40 | snapBusPolicyDir string |
758 | @@ -62,4 +64,6 @@ | |||
759 | 62 | cloudMetaDataFile = filepath.Join(rootdir, "/var/lib/cloud/seed/nocloud-net/meta-data") | 64 | cloudMetaDataFile = filepath.Join(rootdir, "/var/lib/cloud/seed/nocloud-net/meta-data") |
760 | 63 | 65 | ||
761 | 64 | snapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d") | 66 | snapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d") |
762 | 67 | |||
763 | 68 | systemApparmorProfileCacheDir = filepath.Join(rootdir, "/etc/apparmor.d/cache") | ||
764 | 65 | } | 69 | } |
765 | 66 | 70 | ||
766 | === modified file 'snappy/errors.go' | |||
767 | --- snappy/errors.go 2015-06-04 13:04:30 +0000 | |||
768 | +++ snappy/errors.go 2015-06-04 20:51:08 +0000 | |||
769 | @@ -166,11 +166,12 @@ | |||
770 | 166 | // ErrHookFailed is returned if a hook command fails | 166 | // ErrHookFailed is returned if a hook command fails |
771 | 167 | type ErrHookFailed struct { | 167 | type ErrHookFailed struct { |
772 | 168 | cmd string | 168 | cmd string |
773 | 169 | output string | ||
774 | 169 | exitCode int | 170 | exitCode int |
775 | 170 | } | 171 | } |
776 | 171 | 172 | ||
777 | 172 | func (e *ErrHookFailed) Error() string { | 173 | func (e *ErrHookFailed) Error() string { |
779 | 173 | return fmt.Sprintf("hook command %v failed with exit status %d", e.cmd, e.exitCode) | 174 | return fmt.Sprintf("hook command %v failed with exit status %d (output: '%s')", e.cmd, e.exitCode, e.output) |
780 | 174 | } | 175 | } |
781 | 175 | 176 | ||
782 | 176 | // ErrDataCopyFailed is returned if copying the snap data fialed | 177 | // ErrDataCopyFailed is returned if copying the snap data fialed |
783 | 177 | 178 | ||
784 | === modified file 'snappy/snapp.go' | |||
785 | --- snappy/snapp.go 2015-06-04 12:55:54 +0000 | |||
786 | +++ snappy/snapp.go 2015-06-04 20:51:08 +0000 | |||
787 | @@ -1559,6 +1559,7 @@ | |||
788 | 1559 | // are required when calling a meta/hook/ script and that will override | 1559 | // are required when calling a meta/hook/ script and that will override |
789 | 1560 | // any already existing SNAP_* variables in os.Environment() | 1560 | // any already existing SNAP_* variables in os.Environment() |
790 | 1561 | func makeSnapHookEnv(part *SnapPart) (env []string) { | 1561 | func makeSnapHookEnv(part *SnapPart) (env []string) { |
791 | 1562 | <<<<<<< TREE | ||
792 | 1562 | desc := struct { | 1563 | desc := struct { |
793 | 1563 | AppName string | 1564 | AppName string |
794 | 1564 | AppArch string | 1565 | AppArch string |
795 | @@ -1573,6 +1574,22 @@ | |||
796 | 1573 | part.Version(), | 1574 | part.Version(), |
797 | 1574 | QualifiedName(part), | 1575 | QualifiedName(part), |
798 | 1575 | part.Origin(), | 1576 | part.Origin(), |
799 | 1577 | ======= | ||
800 | 1578 | desc := struct { | ||
801 | 1579 | AppName string | ||
802 | 1580 | AppArch string | ||
803 | 1581 | AppPath string | ||
804 | 1582 | Version string | ||
805 | 1583 | UdevAppName string | ||
806 | 1584 | Namespace string | ||
807 | 1585 | }{ | ||
808 | 1586 | part.Name(), | ||
809 | 1587 | helpers.UbuntuArchitecture(), | ||
810 | 1588 | part.basedir, | ||
811 | 1589 | part.Version(), | ||
812 | 1590 | Dirname(part), | ||
813 | 1591 | part.Namespace(), | ||
814 | 1592 | >>>>>>> MERGE-SOURCE | ||
815 | 1576 | } | 1593 | } |
816 | 1577 | snapEnv := helpers.MakeMapFromEnvList(helpers.GetBasicSnapEnvVars(desc)) | 1594 | snapEnv := helpers.MakeMapFromEnvList(helpers.GetBasicSnapEnvVars(desc)) |
817 | 1578 | 1595 | ||
818 | 1579 | 1596 | ||
819 | === modified file 'snappy/snapp_test.go' | |||
820 | --- snappy/snapp_test.go 2015-06-04 13:34:57 +0000 | |||
821 | +++ snappy/snapp_test.go 2015-06-04 20:51:08 +0000 | |||
822 | @@ -709,6 +709,7 @@ | |||
823 | 709 | c.Check(p.notified[0], Matches, "Waiting for .* stop.") | 709 | c.Check(p.notified[0], Matches, "Waiting for .* stop.") |
824 | 710 | } | 710 | } |
825 | 711 | 711 | ||
826 | 712 | <<<<<<< TREE | ||
827 | 712 | func (s *SnapTestSuite) TestErrorOnUnsupportedArchitecture(c *C) { | 713 | func (s *SnapTestSuite) TestErrorOnUnsupportedArchitecture(c *C) { |
828 | 713 | const packageHello = `name: hello-app | 714 | const packageHello = `name: hello-app |
829 | 714 | version: 1.10 | 715 | version: 1.10 |
830 | @@ -728,6 +729,24 @@ | |||
831 | 728 | c.Assert(err.Error(), Equals, errorMsg) | 729 | c.Assert(err.Error(), Equals, errorMsg) |
832 | 729 | } | 730 | } |
833 | 730 | 731 | ||
834 | 732 | ======= | ||
835 | 733 | func (s *SnapTestSuite) TestErrorOnUnsupportedArchitecture(c *C) { | ||
836 | 734 | const packageHello = `name: hello-app | ||
837 | 735 | version: 1.10 | ||
838 | 736 | vendor: Somebody | ||
839 | 737 | icon: meta/hello.svg | ||
840 | 738 | architectures: | ||
841 | 739 | - yadayada | ||
842 | 740 | - blahblah | ||
843 | 741 | ` | ||
844 | 742 | |||
845 | 743 | snapPkg := makeTestSnapPackage(c, packageHello) | ||
846 | 744 | _, err := installClick(snapPkg, 0, nil, testNamespace) | ||
847 | 745 | errorMsg := fmt.Sprintf("package's supported architectures (yadayada, blahblah) is incompatible with this system (%s)", helpers.UbuntuArchitecture()) | ||
848 | 746 | c.Assert(err.Error(), Equals, errorMsg) | ||
849 | 747 | } | ||
850 | 748 | |||
851 | 749 | >>>>>>> MERGE-SOURCE | ||
852 | 731 | func (s *SnapTestSuite) TestRemoteSnapErrors(c *C) { | 750 | func (s *SnapTestSuite) TestRemoteSnapErrors(c *C) { |
853 | 732 | snap := RemoteSnapPart{} | 751 | snap := RemoteSnapPart{} |
854 | 733 | 752 | ||
855 | 734 | 753 | ||
856 | === modified file 'snappy/systemimage.go' | |||
857 | --- snappy/systemimage.go 2015-05-20 17:24:29 +0000 | |||
858 | +++ snappy/systemimage.go 2015-06-04 20:51:08 +0000 | |||
859 | @@ -198,6 +198,9 @@ | |||
860 | 198 | 198 | ||
861 | 199 | // Ensure there is always a kernel + initrd to boot with, even | 199 | // Ensure there is always a kernel + initrd to boot with, even |
862 | 200 | // if the update does not provide new versions. | 200 | // if the update does not provide new versions. |
863 | 201 | if pb != nil { | ||
864 | 202 | pb.Notify("Syncing boot files") | ||
865 | 203 | } | ||
866 | 201 | err = s.partition.SyncBootloaderFiles() | 204 | err = s.partition.SyncBootloaderFiles() |
867 | 202 | if err != nil { | 205 | if err != nil { |
868 | 203 | return "", err | 206 | return "", err |
869 | @@ -226,12 +229,32 @@ | |||
870 | 226 | return "", err | 229 | return "", err |
871 | 227 | } | 230 | } |
872 | 228 | 231 | ||
873 | 232 | // XXX: ToggleNextBoot() calls handleAssets() (but not SyncBootloader | ||
874 | 233 | // files :/) - handleAssets() may copy kernel/initramfs to the | ||
875 | 234 | // sync mounted /boot/uboot, so its very slow, tell the user | ||
876 | 235 | // at least that something is going on | ||
877 | 236 | if pb != nil { | ||
878 | 237 | pb.Notify("Updating boot files") | ||
879 | 238 | } | ||
880 | 239 | |||
881 | 240 | // this needs to go once we have a clean fix for #1460152 | ||
882 | 241 | if err := s.postUpgradeHacks(); err != nil { | ||
883 | 242 | return "", err | ||
884 | 243 | } | ||
885 | 244 | |||
886 | 229 | if err = s.partition.ToggleNextBoot(); err != nil { | 245 | if err = s.partition.ToggleNextBoot(); err != nil { |
887 | 230 | return "", err | 246 | return "", err |
888 | 231 | } | 247 | } |
889 | 232 | return systemImagePartName, nil | 248 | return systemImagePartName, nil |
890 | 233 | } | 249 | } |
891 | 234 | 250 | ||
892 | 251 | // this makes me cry | ||
893 | 252 | func (s *SystemImagePart) postUpgradeHacks() error { | ||
894 | 253 | // Apply HACK to deal with bug #1460152 where the apparmor | ||
895 | 254 | // cache is confused after the upgrade | ||
896 | 255 | return helpers.CleanDir(systemApparmorProfileCacheDir) | ||
897 | 256 | } | ||
898 | 257 | |||
899 | 235 | // Ensure the expected version update was applied to the expected partition. | 258 | // Ensure the expected version update was applied to the expected partition. |
900 | 236 | func (s *SystemImagePart) verifyUpgradeWasApplied() error { | 259 | func (s *SystemImagePart) verifyUpgradeWasApplied() error { |
901 | 237 | // The upgrade has now been applied, so check that the expected | 260 | // The upgrade has now been applied, so check that the expected |
902 | 238 | 261 | ||
903 | === modified file 'snappy/systemimage_native.go' | |||
904 | --- snappy/systemimage_native.go 2015-05-15 13:33:27 +0000 | |||
905 | +++ snappy/systemimage_native.go 2015-06-04 20:51:08 +0000 | |||
906 | @@ -158,6 +158,8 @@ | |||
907 | 158 | pb.Set(genericData.Now) | 158 | pb.Set(genericData.Now) |
908 | 159 | } | 159 | } |
909 | 160 | } | 160 | } |
910 | 161 | // ugly: avoid Spin() artifacts | ||
911 | 162 | pb.Notify("\nApply done") | ||
912 | 161 | 163 | ||
913 | 162 | if err := scanner.Err(); err != nil { | 164 | if err := scanner.Err(); err != nil { |
914 | 163 | return err | 165 | return err |
915 | 164 | 166 | ||
916 | === modified file 'snappy/systemimage_test.go' | |||
917 | --- snappy/systemimage_test.go 2015-06-02 20:53:10 +0000 | |||
918 | +++ snappy/systemimage_test.go 2015-06-04 20:51:08 +0000 | |||
919 | @@ -58,6 +58,13 @@ | |||
920 | 58 | // setup fake /other partition | 58 | // setup fake /other partition |
921 | 59 | makeFakeSystemImageChannelConfig(c, filepath.Join(tempdir, "other", systemImageChannelConfig), "2") | 59 | makeFakeSystemImageChannelConfig(c, filepath.Join(tempdir, "other", systemImageChannelConfig), "2") |
922 | 60 | 60 | ||
923 | 61 | // fake the apparmor.d/cache | ||
924 | 62 | systemApparmorProfileCacheDir = filepath.Join(systemImageRoot, "etc", "apparmor.d", "cache") | ||
925 | 63 | err := os.MkdirAll(systemApparmorProfileCacheDir, 0755) | ||
926 | 64 | c.Assert(err, IsNil) | ||
927 | 65 | err = ioutil.WriteFile(filepath.Join(systemApparmorProfileCacheDir, "some-cache"), []byte(nil), 0644) | ||
928 | 66 | c.Assert(err, IsNil) | ||
929 | 67 | |||
930 | 61 | // run test webserver instead of talking to the real one | 68 | // run test webserver instead of talking to the real one |
931 | 62 | // | 69 | // |
932 | 63 | // The mock webserver versions "1" and "2" | 70 | // The mock webserver versions "1" and "2" |
933 | @@ -192,6 +199,29 @@ | |||
934 | 192 | c.Assert(pb.progress, DeepEquals, []float64{20.0, 40.0, 60.0, 80.0, 100.0}) | 199 | c.Assert(pb.progress, DeepEquals, []float64{20.0, 40.0, 60.0, 80.0, 100.0}) |
935 | 193 | } | 200 | } |
936 | 194 | 201 | ||
937 | 202 | func (s *SITestSuite) TestSystemImagePartInstalAppliesHackLp1460152(c *C) { | ||
938 | 203 | // add a update | ||
939 | 204 | mockSystemImageIndexJSON = fmt.Sprintf(mockSystemImageIndexJSONTemplate, "2") | ||
940 | 205 | parts, err := s.systemImage.Updates() | ||
941 | 206 | |||
942 | 207 | sp := parts[0].(*SystemImagePart) | ||
943 | 208 | mockPartition := MockPartition{} | ||
944 | 209 | sp.partition = &mockPartition | ||
945 | 210 | |||
946 | 211 | dents, err := ioutil.ReadDir(systemApparmorProfileCacheDir) | ||
947 | 212 | c.Assert(err, IsNil) | ||
948 | 213 | c.Assert(dents, HasLen, 1) | ||
949 | 214 | |||
950 | 215 | pb := &MockProgressMeter{} | ||
951 | 216 | // do the install | ||
952 | 217 | _, err = sp.Install(pb, 0) | ||
953 | 218 | c.Assert(err, IsNil) | ||
954 | 219 | |||
955 | 220 | dents, err = ioutil.ReadDir(systemApparmorProfileCacheDir) | ||
956 | 221 | c.Assert(err, IsNil) | ||
957 | 222 | c.Assert(dents, HasLen, 0) | ||
958 | 223 | } | ||
959 | 224 | |||
960 | 195 | func (s *SITestSuite) TestSystemImagePartInstallUpdatesBroken(c *C) { | 225 | func (s *SITestSuite) TestSystemImagePartInstallUpdatesBroken(c *C) { |
961 | 196 | // fake a broken upgrade | 226 | // fake a broken upgrade |
962 | 197 | scriptContent := `#!/bin/sh | 227 | scriptContent := `#!/bin/sh |
963 | 198 | 228 | ||
964 | === modified file 'systemd/systemd.go' | |||
965 | --- systemd/systemd.go 2015-06-03 14:01:51 +0000 | |||
966 | +++ systemd/systemd.go 2015-06-04 20:51:08 +0000 | |||
967 | @@ -207,10 +207,17 @@ | |||
968 | 207 | FullPathPostStop string | 207 | FullPathPostStop string |
969 | 208 | AppTriple string | 208 | AppTriple string |
970 | 209 | ServiceSystemdTarget string | 209 | ServiceSystemdTarget string |
971 | 210 | <<<<<<< TREE | ||
972 | 210 | Origin string | 211 | Origin string |
973 | 211 | AppArch string | 212 | AppArch string |
974 | 212 | Home string | 213 | Home string |
975 | 213 | EnvVars string | 214 | EnvVars string |
976 | 215 | ======= | ||
977 | 216 | Namespace string | ||
978 | 217 | AppArch string | ||
979 | 218 | Home string | ||
980 | 219 | EnvVars string | ||
981 | 220 | >>>>>>> MERGE-SOURCE | ||
982 | 214 | }{ | 221 | }{ |
983 | 215 | *desc, | 222 | *desc, |
984 | 216 | filepath.Join(desc.AppPath, desc.Start), | 223 | filepath.Join(desc.AppPath, desc.Start), |
985 | @@ -218,10 +225,17 @@ | |||
986 | 218 | filepath.Join(desc.AppPath, desc.PostStop), | 225 | filepath.Join(desc.AppPath, desc.PostStop), |
987 | 219 | fmt.Sprintf("%s_%s_%s", desc.AppName, desc.ServiceName, desc.Version), | 226 | fmt.Sprintf("%s_%s_%s", desc.AppName, desc.ServiceName, desc.Version), |
988 | 220 | servicesSystemdTarget, | 227 | servicesSystemdTarget, |
989 | 228 | <<<<<<< TREE | ||
990 | 221 | origin, | 229 | origin, |
991 | 222 | helpers.UbuntuArchitecture(), | 230 | helpers.UbuntuArchitecture(), |
992 | 223 | "%h", | 231 | "%h", |
993 | 224 | "", | 232 | "", |
994 | 233 | ======= | ||
995 | 234 | namespace, | ||
996 | 235 | helpers.UbuntuArchitecture(), | ||
997 | 236 | "%h", | ||
998 | 237 | "", | ||
999 | 238 | >>>>>>> MERGE-SOURCE | ||
1000 | 225 | } | 239 | } |
1001 | 226 | allVars := helpers.GetBasicSnapEnvVars(wrapperData) | 240 | allVars := helpers.GetBasicSnapEnvVars(wrapperData) |
1002 | 227 | allVars = append(allVars, helpers.GetUserSnapEnvVars(wrapperData)...) | 241 | allVars = append(allVars, helpers.GetUserSnapEnvVars(wrapperData)...) |
1003 | 228 | 242 | ||
1004 | === modified file 'systemd/systemd_test.go' | |||
1005 | --- systemd/systemd_test.go 2015-06-03 14:03:45 +0000 | |||
1006 | +++ systemd/systemd_test.go 2015-06-04 20:51:08 +0000 | |||
1007 | @@ -26,9 +26,15 @@ | |||
1008 | 26 | "testing" | 26 | "testing" |
1009 | 27 | "time" | 27 | "time" |
1010 | 28 | 28 | ||
1011 | 29 | <<<<<<< TREE | ||
1012 | 29 | . "gopkg.in/check.v1" | 30 | . "gopkg.in/check.v1" |
1013 | 30 | 31 | ||
1014 | 31 | "launchpad.net/snappy/helpers" | 32 | "launchpad.net/snappy/helpers" |
1015 | 33 | ======= | ||
1016 | 34 | . "launchpad.net/gocheck" | ||
1017 | 35 | |||
1018 | 36 | "launchpad.net/snappy/helpers" | ||
1019 | 37 | >>>>>>> MERGE-SOURCE | ||
1020 | 32 | ) | 38 | ) |
1021 | 33 | 39 | ||
1022 | 34 | type testreporter struct { | 40 | type testreporter struct { |