Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 23 commits into from Jun 19, 2017

Conversation

morphis
Copy link
Contributor

@morphis morphis commented Apr 24, 2017

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

Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just quick comments. I'll read more soon

cmd/cmd.go Outdated
@@ -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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

@morphis morphis Apr 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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

@@ -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},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, isn't this constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch from 5f64f6c to 417b113 Compare April 25, 2017 06:17
cmd/cmd.go Outdated
@@ -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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct now.

func mockSnapConfine() func() {
snapConfine := filepath.Join(dirs.DistroLibExecDir, "snap-confine")
if err := os.MkdirAll(dirs.DistroLibExecDir, 0755); err != nil {
func mockSnapConfine(reexec bool) func() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me change that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even simpler, pass libExecDir as a parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using libExecDir now.

@@ -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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked the logic a bit.

func mockSnapConfine() func() {
snapConfine := filepath.Join(dirs.DistroLibExecDir, "snap-confine")
if err := os.MkdirAll(dirs.DistroLibExecDir, 0755); err != nil {
func mockSnapConfine(reexec bool) func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even simpler, pass libExecDir as a parameter.

@Conan-Kudo
Copy link
Contributor

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...

@morphis
Copy link
Contributor Author

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.

@mvo5
Copy link
Collaborator

mvo5 commented Apr 27, 2017

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

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch from 417b113 to 8c30864 Compare April 27, 2017 12:05
@morphis
Copy link
Contributor Author

morphis commented Apr 27, 2017

Fixed all review comments and rebased on latest master

@mvo5
Copy link
Collaborator

mvo5 commented Apr 27, 2017

Some unit test failures, e.g. https://travis-ci.org/snapcore/snapd/builds/226369117#L2580

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch from 1d1612e to 76d7ec3 Compare April 28, 2017 09:59
@morphis
Copy link
Contributor Author

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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

@morphis morphis May 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"(?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.


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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and reworked the test case a bit more.

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch 8 times, most recently from 448507e to fe8cb4f Compare May 10, 2017 04:50
dirs/dirs.go Outdated
@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HostSnapMountDir would make more sense, ne?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Outdated
@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

@morphis morphis May 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@niemeyer Doing that now. Please have another look.

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch from 9d17651 to b22a99f Compare May 11, 2017 09:32
dirs/dirs.go Outdated
// CoreLibExecDir is similar to CoreSnapMountDir but for the
// LibExecDir used by snapd.
CoreLibExecDir string
DistroLibExecDir string
SnapBlobDir string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably want a newline to separate things here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will add one.

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch 4 times, most recently from dc66df4 to 003c870 Compare May 15, 2017 10:08
@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch from 003c870 to 9b771dd Compare May 17, 2017 12:51
@codecov-io
Copy link

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 Outdated
SnapMountDir string

// CoreSnapMountDir is the path of the snap mount dir inside
// the snap confinement environment. Give this nature it will
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given its nature maybe?

Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

dirs/dirs.go Outdated
// 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"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we const those?

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

const (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would feel better with a dedicated type.

const (
SnapMountDir = iota
SnapDataDir
SnapCommonDir
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, are those even used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop these now.

@morphis
Copy link
Contributor Author

morphis commented May 23, 2017

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

Copy link
Collaborator

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

{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},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filepath.Join() is probably more natural here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"(?)

@@ -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"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+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() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

@niemeyer niemeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM assuming it drops the function mentioned below.

snap/info.go Outdated
}

// 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@morphis morphis force-pushed the f/dynamic-mount-dir-in-tests branch from 1f78b74 to 999789f Compare June 19, 2017 08:01
@mvo5 mvo5 merged commit 0719d6b into snapcore:master Jun 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
7 participants