Merge lp:~mvo/snappy/snappy-lp1460152-workaround into lp:~snappy-dev/snappy/snappy-moved-to-github

Proposed by Michael Vogt
Status: Work in progress
Proposed branch: lp:~mvo/snappy/snappy-lp1460152-workaround
Merge into: lp:~snappy-dev/snappy/snappy-moved-to-github
Diff against target: 157 lines (+78/-6)
5 files modified
helpers/helpers.go (+17/-0)
helpers/helpers_test.go (+11/-1)
snappy/dirs.go (+4/-0)
snappy/systemimage.go (+16/-5)
snappy/systemimage_test.go (+30/-0)
To merge this branch: bzr merge lp:~mvo/snappy/snappy-lp1460152-workaround
Reviewer Review Type Date Requested Status
Ricardo Salveti (community) Needs Information
Review via email: mp+261144@code.launchpad.net

Commit message

This branch adds a workaround for LP: #1460152 to prevent that the
apparmor profile and cache get out of sync.

Description of the change

This branch adds a workaround for LP: #1460152 to prevent that the
apparmor profile and cache get out of sync.

To post a comment you must log in.
Revision history for this message
Sergio Schvezov (sergiusens) wrote :

I linked the bug, we can add a task for a proper implementation.

Revision history for this message
Ricardo Salveti (rsalveti) wrote :

Wouldn't this only be triggered after doing a second update? The major problem we have is that we're currently over a stable/older image, and we want to update to edge, and make sure that the new cache is not broken on the next reboot, but we're still updating to edge using the tools available at stable (not using the extra logic you added here).

So the workaround would need to be either something triggered by the update process coming from stable, or something done as part of the first boot after updating the image.

review: Needs Information
Revision history for this message
Sergio Schvezov (sergiusens) wrote :

Tears aside ;-) this is lacking some sort of systemd unit to trigger apparmor cache cleaning on boot (we can probably leverage the one apparmor has itself).

Revision history for this message
Michael Vogt (mvo) wrote :

@Ricardo: Indeed you are right :/ Sorry for overlooking this, at some point we need to add the ability for the upgrader to upgrade itself. So the best way seems to be going a systemd unit route (as Sergio suggested).

@Sergio: I pushed an alternative solution based on systemd to http://bazaar.launchpad.net/~mvo/ubuntu/vivid/ubuntu-core-config/lp1460152-workaround/revision/28 and then http://bazaar.launchpad.net/~mvo/ubuntu/vivid/ubuntu-core-config/lp1460152-workaround/revision/29. My original mtime based approach did not work, the md5sum one looks promising though.

The full diff is here:
https://code.launchpad.net/~mvo/ubuntu/vivid/ubuntu-core-config/lp1460152-workaround/+merge/261179

If the approach looks good this needs to go to the ppa and get a real-world test (i.e. 15.04 stable->edge upgrade). Please keep me updated via mail or telegram as I won't be on irc today.

Unmerged revisions

486. By Michael Vogt

Remove /etc/apparmor.d/cache/* on upgrade to workaround lp1460152

This works around the issue that the way apparmor creates the cache
is based on the mtime of the profile. So if the mtime of the profile
is older than the mtime of the cache file the cache is not re-generated.

This is a problem because:
- boot stable, /etc/apparmor.d/cache/usr.bin.ubuntu-core-launcher is mtime of now because we generate the cache on boot
- upgrade to edge, /etc/apparmor.d/usr.bin.ubuntu-core-launcher is updated and has the mtime of T (yesterday) when the file was put into the package
- on the next reboot the apparmor_parser compares the mtime of the cache/usr.bin.ubuntu-core-launcher (very very recent) with the mtime of the souce usr.bin.ubuntu-core-launcher (much older)
-> cache does is *not* re-generate

The real fix is IMO that apparmor adds the mtime of the profile into
the header of the cache file (or makes the mtime of the cache file)
the mtime of the profile and re-generated if they get out of sync
(instead of checking for newer).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'helpers/helpers.go'
--- helpers/helpers.go 2015-06-04 12:55:54 +0000
+++ helpers/helpers.go 2015-06-04 20:40:35 +0000
@@ -26,6 +26,7 @@
26 "encoding/hex"26 "encoding/hex"
27 "fmt"27 "fmt"
28 "io"28 "io"
29 "io/ioutil"
29 "math/rand"30 "math/rand"
30 "os"31 "os"
31 "os/exec"32 "os/exec"
@@ -436,3 +437,19 @@
436 "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",437 "SNAPP_APP_USER_DATA_PATH={{.Home}}{{.AppPath}}",
437 })438 })
438}439}
440
441// CleanDir cleans all regular files in the given directory
442func CleanDir(dir string) error {
443 dents, err := ioutil.ReadDir(dir)
444 if err != nil {
445 return err
446 }
447 for _, dent := range dents {
448 if dent.Mode().IsRegular() {
449 if err := os.Remove(filepath.Join(dir, dent.Name())); err != nil {
450 return err
451 }
452 }
453 }
454 return nil
455}
439456
=== modified file 'helpers/helpers_test.go'
--- helpers/helpers_test.go 2015-06-04 12:55:54 +0000
+++ helpers/helpers_test.go 2015-06-04 20:40:35 +0000
@@ -326,5 +326,15 @@
326 err = UnpackTar(f, c.MkDir(), nil)326 err = UnpackTar(f, c.MkDir(), nil)
327 c.Assert(err, IsNil)327 c.Assert(err, IsNil)
328 c.Assert(mknodWasCalled, Equals, true)328 c.Assert(mknodWasCalled, Equals, true)
329329}
330
331func (ts *HTestSuite) TestCleanDir(c *C) {
332 d := c.MkDir()
333 canary := filepath.Join(d, "foo")
334 err := ioutil.WriteFile(canary, []byte(nil), 0644)
335 c.Assert(err, IsNil)
336
337 err = CleanDir(d)
338 c.Assert(err, IsNil)
339 c.Assert(FileExists(canary), Equals, false)
330}340}
331341
=== modified file 'snappy/dirs.go'
--- snappy/dirs.go 2015-05-15 13:33:27 +0000
+++ snappy/dirs.go 2015-06-04 20:40:35 +0000
@@ -33,6 +33,8 @@
33 snapSeccompDir string33 snapSeccompDir string
34 snapUdevRulesDir string34 snapUdevRulesDir string
3535
36 systemApparmorProfileCacheDir string
37
36 snapBinariesDir string38 snapBinariesDir string
37 snapServicesDir string39 snapServicesDir string
38 snapBusPolicyDir string40 snapBusPolicyDir string
@@ -62,4 +64,6 @@
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")
6365
64 snapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d")66 snapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d")
67
68 systemApparmorProfileCacheDir = filepath.Join(rootdir, "/etc/apparmor.d/cache")
65}69}
6670
=== modified file 'snappy/systemimage.go'
--- snappy/systemimage.go 2015-05-20 17:24:29 +0000
+++ snappy/systemimage.go 2015-06-04 20:40:35 +0000
@@ -222,16 +222,27 @@
222 }222 }
223223
224 // Check that the final system state is as expected.224 // Check that the final system state is as expected.
225 if err = s.verifyUpgradeWasApplied(); err != nil {225 if err := s.verifyUpgradeWasApplied(); err != nil {
226 return "", err226 return "", err
227 }227 }
228228
229 if err = s.partition.ToggleNextBoot(); err != nil {229 if err := s.postUpgradeHacks(); err != nil {
230 return "", err
231 }
232
233 if err := s.partition.ToggleNextBoot(); err != nil {
230 return "", err234 return "", err
231 }235 }
232 return systemImagePartName, nil236 return systemImagePartName, nil
233}237}
234238
239// this makes me cry
240func (s *SystemImagePart) postUpgradeHacks() error {
241 // Apply HACK to deal with bug #1460152 where the apparmor
242 // cache is confused after the upgrade
243 return helpers.CleanDir(systemApparmorProfileCacheDir)
244}
245
235// Ensure the expected version update was applied to the expected partition.246// Ensure the expected version update was applied to the expected partition.
236func (s *SystemImagePart) verifyUpgradeWasApplied() error {247func (s *SystemImagePart) verifyUpgradeWasApplied() error {
237 // The upgrade has now been applied, so check that the expected248 // The upgrade has now been applied, so check that the expected
238249
=== modified file 'snappy/systemimage_test.go'
--- snappy/systemimage_test.go 2015-06-02 20:53:10 +0000
+++ snappy/systemimage_test.go 2015-06-04 20:40:35 +0000
@@ -58,6 +58,13 @@
58 // setup fake /other partition58 // setup fake /other partition
59 makeFakeSystemImageChannelConfig(c, filepath.Join(tempdir, "other", systemImageChannelConfig), "2")59 makeFakeSystemImageChannelConfig(c, filepath.Join(tempdir, "other", systemImageChannelConfig), "2")
6060
61 // fake the apparmor.d/cache
62 systemApparmorProfileCacheDir = filepath.Join(systemImageRoot, "etc", "apparmor.d", "cache")
63 err := os.MkdirAll(systemApparmorProfileCacheDir, 0755)
64 c.Assert(err, IsNil)
65 err = ioutil.WriteFile(filepath.Join(systemApparmorProfileCacheDir, "some-cache"), []byte(nil), 0644)
66 c.Assert(err, IsNil)
67
61 // run test webserver instead of talking to the real one68 // run test webserver instead of talking to the real one
62 //69 //
63 // The mock webserver versions "1" and "2"70 // The mock webserver versions "1" and "2"
@@ -192,6 +199,29 @@
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})
193}200}
194201
202func (s *SITestSuite) TestSystemImagePartInstalAppliesHackLp1460152(c *C) {
203 // add a update
204 mockSystemImageIndexJSON = fmt.Sprintf(mockSystemImageIndexJSONTemplate, "2")
205 parts, err := s.systemImage.Updates()
206
207 sp := parts[0].(*SystemImagePart)
208 mockPartition := MockPartition{}
209 sp.partition = &mockPartition
210
211 dents, err := ioutil.ReadDir(systemApparmorProfileCacheDir)
212 c.Assert(err, IsNil)
213 c.Assert(dents, HasLen, 1)
214
215 pb := &MockProgressMeter{}
216 // do the install
217 _, err = sp.Install(pb, 0)
218 c.Assert(err, IsNil)
219
220 dents, err = ioutil.ReadDir(systemApparmorProfileCacheDir)
221 c.Assert(err, IsNil)
222 c.Assert(dents, HasLen, 0)
223}
224
195func (s *SITestSuite) TestSystemImagePartInstallUpdatesBroken(c *C) {225func (s *SITestSuite) TestSystemImagePartInstallUpdatesBroken(c *C) {
196 // fake a broken upgrade226 // fake a broken upgrade
197 scriptContent := `#!/bin/sh227 scriptContent := `#!/bin/sh

Subscribers

People subscribed via source and target branches