many: fix test cases to work with different DistroLibExecDir #3222

Merged
merged 23 commits into from Jun 19, 2017

Conversation

Projects
None yet
7 participants
Contributor

morphis commented Apr 24, 2017

This is needed to allow execution of our unit tests within the package build on other distributions like Fedora.

Just quick comments. I'll read more soon

cmd/cmd.go
@@ -90,7 +91,7 @@ func ExecInCoreSnap() {
// ensure we do not re-exec into an older version of snapd, look
// for info file and ignore version of core that do not yet have
// it
- fullInfo := filepath.Join(corePath, "/usr/lib/snapd/info")
@zyga

zyga Apr 24, 2017

Contributor

Wait, this feels wrong. If core path is the path where core is mounted the rest of the string can be a constant because it's not depending on the distribution anymore.

@morphis

morphis Apr 24, 2017

Contributor

I will look into this again. However all tests except refresh-delta-from-core on Ubuntu 14.04 seem to pass. However for Fedora/SUSE/Arch/... this might fail. We really need CI for all of these.

@zyga

zyga Apr 25, 2017

Contributor

This looks correct now.

@zyga

zyga Apr 27, 2017

Contributor

Wait, this is not what I expected (the diff looked different before). So on Fedora the core path is /var/lib/snapd/snap/core/current/ but then the dirs.DistroLibExecDir will contain /usr/libexec/snapd, I don't understand how this is supposed to work.

@morphis

morphis Apr 27, 2017

Contributor

@zyga Please note that the code now uses dirs.CoreLibExecDir which will expand to /usr/lib/snapd even on Fedora. A previous version of the code was using dirs.DistroLibExecDir which is, as you pointed out, wrong.

In summary: The new code filepath.Join(corePath, dirs.CoreLibExecDir, "info") will take the corePath as determined before through /proc/self/exe and append dirs.CoreLibExecDir so that we end up with /snap/core/current/usr/libexec/snapd/info on Ubuntu and /var/lib/snapd/snap/core/current/usr/libexec/snapd/info

cmd/snap/cmd_run_test.go
@@ -449,8 +450,8 @@ func (s *SnapSuite) TestSnapRunIsReexeced(c *check.C) {
readlink string
expected bool
}{
- {filepath.Join(dirs.SnapMountDir, "/usr/lib/snapd/snapd"), true},
- {"/usr/lib/snapd/snapd", false},
+ {filepath.Join(dirs.SnapMountDir, dirs.DistroLibExecDir, "snapd"), true},
@zyga

zyga Apr 24, 2017

Contributor

Again, isn't this constant?

@morphis

morphis Apr 24, 2017

Contributor

For now yes, but when we have switch to different core/base snaps it may not. Using dirs.CoreLibExecDir here now.

@zyga

zyga Apr 24, 2017

Contributor

using CoreLibExecDir is fine. For core/base split I don't know how it will work yet so it's hard to say.

cmd/cmd.go
@@ -90,7 +91,7 @@ func ExecInCoreSnap() {
// ensure we do not re-exec into an older version of snapd, look
// for info file and ignore version of core that do not yet have
// it
- fullInfo := filepath.Join(corePath, "/usr/lib/snapd/info")
@zyga

zyga Apr 24, 2017

Contributor

Wait, this feels wrong. If core path is the path where core is mounted the rest of the string can be a constant because it's not depending on the distribution anymore.

@morphis

morphis Apr 24, 2017

Contributor

I will look into this again. However all tests except refresh-delta-from-core on Ubuntu 14.04 seem to pass. However for Fedora/SUSE/Arch/... this might fail. We really need CI for all of these.

@zyga

zyga Apr 25, 2017

Contributor

This looks correct now.

@zyga

zyga Apr 27, 2017

Contributor

Wait, this is not what I expected (the diff looked different before). So on Fedora the core path is /var/lib/snapd/snap/core/current/ but then the dirs.DistroLibExecDir will contain /usr/libexec/snapd, I don't understand how this is supposed to work.

@morphis

morphis Apr 27, 2017

Contributor

@zyga Please note that the code now uses dirs.CoreLibExecDir which will expand to /usr/lib/snapd even on Fedora. A previous version of the code was using dirs.DistroLibExecDir which is, as you pointed out, wrong.

In summary: The new code filepath.Join(corePath, dirs.CoreLibExecDir, "info") will take the corePath as determined before through /proc/self/exe and append dirs.CoreLibExecDir so that we end up with /snap/core/current/usr/libexec/snapd/info on Ubuntu and /var/lib/snapd/snap/core/current/usr/libexec/snapd/info

cmd/snap/main_test.go
-func mockSnapConfine() func() {
- snapConfine := filepath.Join(dirs.DistroLibExecDir, "snap-confine")
- if err := os.MkdirAll(dirs.DistroLibExecDir, 0755); err != nil {
+func mockSnapConfine(reexec bool) func() {
@zyga

zyga Apr 25, 2017

Contributor

Just nitpick. I'd prefer a flag like ReExecEnabled or something similar. The false/true that is in tests above is hard to grok just by looking.

@morphis

morphis Apr 25, 2017

Contributor

Let me change that.

@niemeyer

niemeyer Apr 26, 2017

Contributor

Or even simpler, pass libExecDir as a parameter.

@morphis

morphis Apr 27, 2017

Contributor

Using libExecDir now.

cmd/snap/cmd_run.go
@@ -228,7 +228,11 @@ func isReexeced() bool {
}
func runSnapConfine(info *snap.Info, securityTag, snapApp, command, hook string, args []string) error {
- snapConfine := filepath.Join(dirs.DistroLibExecDir, "snap-confine")
+ libExecDir := dirs.DistroLibExecDir
+ if isReexeced() {
@niemeyer

niemeyer Apr 26, 2017

Contributor

This should be done only once in this function. Right now it's being done twice, and it's also bogus because code right below will test for a path that does not exist. Proper way is likely to bring the code currently at the bottom here, and make sure snapConfine points to the proper thing upfront.

@morphis

morphis Apr 27, 2017

Contributor

Reworked the logic a bit.

cmd/snap/main_test.go
-func mockSnapConfine() func() {
- snapConfine := filepath.Join(dirs.DistroLibExecDir, "snap-confine")
- if err := os.MkdirAll(dirs.DistroLibExecDir, 0755); err != nil {
+func mockSnapConfine(reexec bool) func() {
@zyga

zyga Apr 25, 2017

Contributor

Just nitpick. I'd prefer a flag like ReExecEnabled or something similar. The false/true that is in tests above is hard to grok just by looking.

@morphis

morphis Apr 25, 2017

Contributor

Let me change that.

@niemeyer

niemeyer Apr 26, 2017

Contributor

Or even simpler, pass libExecDir as a parameter.

@morphis

morphis Apr 27, 2017

Contributor

Using libExecDir now.

Contributor

Conan-Kudo commented Apr 27, 2017

I'm confused... Why are so many things changing from DistroLibExecDir to CoreLibExecDir? That seems to indicate it'd be running/testing a completely different/wrong thing...

Contributor

morphis commented Apr 27, 2017

@Conan-Kudo Many of the changes are for paths are in the code path for the re-execution feature which never worked on Fedora/Suse. So you never saw anything going wrong. This PR fixes those cases and brings us a bit closer to working re-execution too.

Collaborator

mvo5 commented Apr 27, 2017

Looks good, but some review comments (like #3222 (comment) are not addressed yet)

Contributor

morphis commented Apr 27, 2017

Fixed all review comments and rebased on latest master

Collaborator

mvo5 commented Apr 27, 2017

Contributor

morphis commented Apr 28, 2017

@mvo5 Pushed a change to fix the failing unit tests.

@@ -195,11 +199,11 @@ apps:
func (s *ContentSuite) TestResolveSpecialVariable(c *C) {
info := snaptest.MockInfo(c, "name: name", &snap.SideInfo{Revision: snap.R(42)})
- c.Check(builtin.ResolveSpecialVariable("foo", info), Equals, "/snap/name/42/foo")
@zyga

zyga May 5, 2017

Contributor

Sorry for taking so long to verify this. This part is incorrect. The generated mount profile is only processed after pivot_root has happened so it must use the internal layout.

@morphis

morphis May 5, 2017

Contributor

How that? On Debian & Ubuntu we have /snap and on Fedora we have /var/lib/snapd/snap in and outside of the snap or do I miss anything?

@zyga

zyga May 5, 2017

Contributor

You are missing one thing. After pivot_root whatever the snap mount directory is it ends up being /snap. Only then do we process those mount directives so the hard-coded paths are actually always correct.

@morphis

morphis May 5, 2017

Contributor

Running the following

$ snap run --shell hello-world
bash-4.3$ echo $SNAP
/var/lib/snapd/snap/hello-world/27

Please also note that I didn't change builting.ResolveSpecialVariable. It is using dirs.BaseSnapMountDir already without this PR. See https://github.com/snapcore/snapd/blob/master/interfaces/builtin/content.go#L123

@zyga

zyga May 5, 2017

Contributor

As discussed on IRC there are a host of bugs here:

  • snap execution environment should look the same everywhere
  • $SNAP is always /snap/$SNAP_NAME/$SNAP_REVISION when seen from inside snap execution environment
  • various other things are wrong but we'll discuss and fix them in separate PRs
@morphis

morphis May 8, 2017

Contributor

The code is now changed to use /snap as static prefix.

@mvo5

mvo5 Jun 7, 2017

Collaborator

Silly question - if the code got changed to use /snap as static prefix, do we still need to change:

- "/snap/foo"
+ filepath.Join(dirs.CoreSnapMountDir, "foo")

I guess the answer is yes to make it more obvious when we mean "context-is-inside-core" and "context-is-outside"(?)

@morphis

morphis Jun 7, 2017

Contributor

I would say, we should do it this way, With that we always have a clear bind to the context and know what we're dealing with.

tests/unit/go/task.yaml
execute: |
mkdir -p /tmp/unit-tests/src/github.com/snapcore
cp -ar $PROJECT_PATH /tmp/unit-tests/src/github.com/snapcore
chown -R test:12345 /tmp/unit-tests
su -l -c "cd /tmp/unit-tests/src/github.com/snapcore/snapd && GOPATH=/tmp/unit-tests ./run-checks --unit" test
+
+ mv /etc/os-release /etc/os-release.orig
@zyga

zyga May 5, 2017

Contributor

This is more of a prepare: stage to me but the actual section looks curious. Is this intentional?

@morphis

morphis May 5, 2017

Contributor

Fixed and reworked the test case a bit more.

dirs/dirs.go
@@ -126,9 +128,11 @@ func SetRootDir(rootdir string) {
switch release.ReleaseInfo.ID {
case "fedora", "centos", "rhel", "arch":
- SnapMountDir = filepath.Join(rootdir, "/var/lib/snapd/snap")
+ BaseSnapMountDir = "/var/lib/snapd/snap"
@niemeyer

niemeyer May 10, 2017

Contributor

We can't use the "BaseSnap" term here, even more when we have a matching variable with "CoreSnap". Base snaps are around the block and this is unrelated.

@Conan-Kudo

Conan-Kudo May 10, 2017

Contributor

HostSnapMountDir would make more sense, ne?

@morphis

morphis May 11, 2017

Contributor

HostSnapMountDir still has a notion of not saying that it is absolute or not. BaseSnapMountDir was my attempt to word that. What about RawSnapMountDir?

@niemeyer

niemeyer May 12, 2017

Contributor

That makes it Raw, except other things raw are not Raw. Still pretty misleading and error prone. Not to mention "raw" doesn't really say much in this context.

See the suggestion in the other point.

dirs/dirs.go
@@ -179,7 +183,8 @@ func SetRootDir(rootdir string) {
DistroLibExecDir = filepath.Join(rootdir, "/usr/lib/snapd")
}
- CoreLibExecDir = filepath.Join(rootdir, "/usr/lib/snapd")
+ CoreLibExecDir = "/usr/lib/snapd"
+ CoreSnapMountDir = "/snap"
@niemeyer

niemeyer May 10, 2017

Contributor

What's the line for deciding what has a prefix or not? Why do we have BaseSnapMountDir with rootdir, but the ones above not? Hard to guess on call sites, which makes it quite error prone.

@morphis

morphis May 11, 2017

Contributor

We need a variable which has just the clean mount dir without the root prefix as otherwise we end up with something $prefix/$prefix/$snapmountdir in the unit tests. See https://github.com/snapcore/snapd/pull/3222/files#diff-76f691a17e89c1330742e51bb468ef76R203 as an example. The only place where it is ever being used are unit tests. I can move these variables into test code and keep it away from dirs/dirs.go but actually it would be nice to keep these things in a central place.

Also CoreLibExecDir/CoreSnapMountDir are needed as these describe where in the core snap those directories are located. These are different on Fedora than the outside SnapMountDir/LibExecDir so we have to keep them distinct.

@pedronis

pedronis May 11, 2017

Contributor

if a unit test tries to write to those Core* dirs missing the rootdir will creates errors

@morphis

morphis May 11, 2017

Contributor

The unit test doesn't try to write into those dirs. They are used for example in the content interface where we check if the mount entries generated are ok. @zyga told me that those should never use SnapMountDir (which might be prefixed) but the mount dir without any prefix so /snap on Ubuntu and /var/lib/snapd/snap on Fedora.

@niemeyer

niemeyer May 12, 2017

Contributor

I suggest keeping the rootdir as originally, and stripping GlobalRootDir out of it when really needed. That makes it consistent.

@morphis

morphis May 15, 2017

Contributor

@niemeyer Doing that now. Please have another look.

dirs/dirs.go
+ // CoreLibExecDir is similar to CoreSnapMountDir but for the
+ // LibExecDir used by snapd.
+ CoreLibExecDir string
+ DistroLibExecDir string
SnapBlobDir string
@pedronis

pedronis May 11, 2017

Contributor

we probably want a newline to separate things here

@morphis

morphis May 11, 2017

Contributor

Will add one.

codecov-io commented May 17, 2017

Codecov Report

Merging #3222 into master will decrease coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3222      +/-   ##
==========================================
- Coverage   77.16%   77.15%   -0.01%     
==========================================
  Files         373      373              
  Lines       25793    25792       -1     
==========================================
- Hits        19902    19900       -2     
  Misses       4134     4134              
- Partials     1757     1758       +1
Impacted Files Coverage Δ
dirs/dirs.go 96.77% <ø> (-0.06%) ⬇️
cmd/snap/cmd_run.go 64.51% <100%> (ø) ⬆️
interfaces/builtin/content.go 80.73% <100%> (ø) ⬆️
cmd/cmd.go 100% <100%> (ø) ⬆️
overlord/snapstate/snapstate.go 81.28% <0%> (-0.24%) ⬇️
overlord/ifacestate/helpers.go 65.54% <0%> (ø) ⬆️
interfaces/sorting.go 96.66% <0%> (+3.33%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1495246...999789f. Read the comment docs.

dirs/dirs.go
+ SnapMountDir string
+
+ // CoreSnapMountDir is the path of the snap mount dir inside
+ // the snap confinement environment. Give this nature it will
@zyga

zyga May 19, 2017

Contributor

Given its nature maybe?

My head hurts with all the new snap variables everywhere. LGTM

dirs/dirs.go
+ // These are directories which are static inside the core snap and
+ // can never be prefixed as they will be always absolute once we
+ // are in the snap confinement environment.
+ CoreLibExecDir = "/usr/lib/snapd"
@zyga

zyga May 19, 2017

Contributor

Why don't we const those?

interfaces/builtin/content.go
@@ -116,13 +117,23 @@ func (iface *contentInterface) path(slot *interfaces.Slot, name string) []string
return out
}
+const (
@zyga

zyga May 19, 2017

Contributor

This would feel better with a dedicated type.

interfaces/builtin/content.go
+const (
+ SnapMountDir = iota
+ SnapDataDir
+ SnapCommonDir
@zyga

zyga May 19, 2017

Contributor

Wait, are those even used?

@morphis

morphis May 19, 2017

Contributor

Hm, they aren't. Seems to be a leftover. Good spot!

@morphis

morphis May 19, 2017

Contributor

Drop these now.

Simon Fels added some commits May 19, 2017

Contributor

morphis commented May 23, 2017

@pedronis @zyga @Conan-Kudo @niemeyer Can you have another look so we get this reviewed and merged soon?

Looks good, some comments inline. I am in favour of merging even if imperfect and do a followup to ease the burden of fixing this branch, it has a lot of conflicts potential.

@@ -98,7 +98,7 @@ func (s *SnapSuite) TestSnapRunAppIntegration(c *check.C) {
// mock installed snap
dirs.SetRootDir(c.MkDir())
defer func() { dirs.SetRootDir("/") }()
- defer mockSnapConfine()()
+ defer mockSnapConfine(dirs.DistroLibExecDir)()
@mvo5

mvo5 Jun 7, 2017

Collaborator

Pardon my ignorance, but it looks like the argument for mockSnapConfine() is always dirs.DistroLibExecDir - do we really need this new argument in this case?

@mvo5

mvo5 Jun 7, 2017

Collaborator

Aha, silly me. I even found this in subsequent reading but then forgot to delete the comment. Sorry for the noise.

cmd/snap/cmd_run_test.go
- {filepath.Join(dirs.SnapMountDir, "/usr/lib/snapd/snapd"), true},
- {"/usr/lib/snapd/snapd", false},
+ {filepath.Join(dirs.SnapMountDir, dirs.CoreLibExecDir, "snapd"), true},
+ {fmt.Sprintf("%s/snapd", dirs.DistroLibExecDir), false},
@mvo5

mvo5 Jun 7, 2017

Collaborator

filepath.Join() is probably more natural here

@morphis

morphis Jun 7, 2017

Contributor

Fixed.

@@ -195,11 +199,11 @@ apps:
func (s *ContentSuite) TestResolveSpecialVariable(c *C) {
info := snaptest.MockInfo(c, "name: name", &snap.SideInfo{Revision: snap.R(42)})
- c.Check(builtin.ResolveSpecialVariable("foo", info), Equals, "/snap/name/42/foo")
@zyga

zyga May 5, 2017

Contributor

Sorry for taking so long to verify this. This part is incorrect. The generated mount profile is only processed after pivot_root has happened so it must use the internal layout.

@morphis

morphis May 5, 2017

Contributor

How that? On Debian & Ubuntu we have /snap and on Fedora we have /var/lib/snapd/snap in and outside of the snap or do I miss anything?

@zyga

zyga May 5, 2017

Contributor

You are missing one thing. After pivot_root whatever the snap mount directory is it ends up being /snap. Only then do we process those mount directives so the hard-coded paths are actually always correct.

@morphis

morphis May 5, 2017

Contributor

Running the following

$ snap run --shell hello-world
bash-4.3$ echo $SNAP
/var/lib/snapd/snap/hello-world/27

Please also note that I didn't change builting.ResolveSpecialVariable. It is using dirs.BaseSnapMountDir already without this PR. See https://github.com/snapcore/snapd/blob/master/interfaces/builtin/content.go#L123

@zyga

zyga May 5, 2017

Contributor

As discussed on IRC there are a host of bugs here:

  • snap execution environment should look the same everywhere
  • $SNAP is always /snap/$SNAP_NAME/$SNAP_REVISION when seen from inside snap execution environment
  • various other things are wrong but we'll discuss and fix them in separate PRs
@morphis

morphis May 8, 2017

Contributor

The code is now changed to use /snap as static prefix.

@mvo5

mvo5 Jun 7, 2017

Collaborator

Silly question - if the code got changed to use /snap as static prefix, do we still need to change:

- "/snap/foo"
+ filepath.Join(dirs.CoreSnapMountDir, "foo")

I guess the answer is yes to make it more obvious when we mean "context-is-inside-core" and "context-is-outside"(?)

@morphis

morphis Jun 7, 2017

Contributor

I would say, we should do it this way, With that we always have a clear bind to the context and know what we're dealing with.

@@ -2689,7 +2719,7 @@ version: 1.0`)
{
// ensure only local install was run, i.e. first action is pseudo-action current
op: "current",
- old: "/snap/mock/100001",
+ old: filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "mock/100001"),
@mvo5

mvo5 Jun 7, 2017

Collaborator

This feels a bit repetitive and relatively long, I wonder if we could compute the dirs only once at the beginning of the test. OTOH it was repetitive before so maybe that is something for a later branch.

@morphis

morphis Jun 7, 2017

Contributor

+1 (change + later branch)

@@ -4987,6 +5017,9 @@ func (s *snapmgrTestSuite) TestTrySetsTryModeJailMode(c *C) {
s.testTrySetsTryMode(snapstate.Flags{JailMode: true}, c)
}
func (s *snapmgrTestSuite) TestTrySetsTryModeClassic(c *C) {
+ if !dirs.SupportsClassicConfinement() {
@mvo5

mvo5 Jun 7, 2017

Collaborator

Would it be possible to move everything that needs classic confinement into a single snapmgrWithClassicConfinementTestSuite and simply skip the entire suite on the above condition?

@morphis

morphis Jun 7, 2017

Contributor

Not sure, something I would have to look into but would prefer to do it with a follow up PR to not put more burden on this one.

@@ -5046,6 +5082,9 @@ func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesJailMode(c *C) {
s.testTrySetsTryMode(snapstate.Flags{JailMode: true}, c)
}
func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesClassic(c *C) {
+ if !dirs.SupportsClassicConfinement() {
@mvo5

mvo5 Jun 7, 2017

Collaborator

Slightly strange that SupportsClassicConfinement() is a member of dirs - I understand why but still feels wrong. Anyway, not something to be concerned about for this branch.

Simon Fels added some commits Jun 7, 2017

mvo5 approved these changes Jun 7, 2017

LGTM assuming it drops the function mentioned below.

snap/info.go
+}
+
+// MountDirWithBasePath returns the base directory where it gets mounted of the snap with the given name and revision.
+func MountDirWithBasePath(basePath, name string, revision Revision) string {
@niemeyer

niemeyer Jun 12, 2017

Contributor

Let's please drop this function. We don't need a special (and long) name for filepath.Join. :-)

@morphis

morphis Jun 19, 2017

Contributor

Done.

zyga approved these changes Jun 12, 2017

Simon Fels added some commits Jun 14, 2017

@mvo5 mvo5 merged commit 0719d6b into snapcore:master Jun 19, 2017

1 of 7 checks passed

artful-amd64 autopkgtest running
Details
xenial-amd64 autopkgtest running
Details
xenial-i386 autopkgtest running
Details
xenial-ppc64el autopkgtest running
Details
yakkety-amd64 autopkgtest running
Details
zesty-amd64 autopkgtest running
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment