Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
interfaces,overlord: support unversioned data #1095
Conversation
zyga
reviewed
Apr 28, 2016
| +func (f *fakeSnappyBackend) RemoveSnapSharedData(info *snap.Info) error { | ||
| + f.ops = append(f.ops, fakeOp{ | ||
| + op: "remove-snap-shared-data", | ||
| + name: info.MountDir(), |
kyrofa
Apr 28, 2016
Member
You mean because it includes the version? Perhaps this is reason enough to create an info.BaseDir() function, then.
zyga
Apr 29, 2016
Contributor
My comment was based on just the name: ... MountDir connection but I see that other tests use this as well.
kyrofa
Apr 29, 2016
•
Member
Yeah, it's nice for testing that a given operation happens on a given revision of the snap. We may want to leave this as MountDir(), then.
niemeyer
Apr 29, 2016
Contributor
Yeah, let's not overthink this too much for now. It's just a way to print out what the backend had at hand when the given method was called.
zyga
reviewed
Apr 28, 2016
| @@ -63,6 +67,7 @@ func GetBasicSnapEnvVars(desc interface{}) []string { | ||
| return fillSnapEnvVars(desc, []string{ | ||
| "SNAP={{.SnapPath}}", | ||
| "SNAP_DATA=/var{{.SnapPath}}", | ||
| + "SNAP_SHARED_DATA={{CleanPath (printf \"/var%s/../shared\" .SnapPath)}}", |
kyrofa
Apr 28, 2016
•
Member
Yeah I wasn't a huge fan either, but the only nice alternative I could think of was to stop being clever using SnapPath for everything and actually specify each of these explicitly, which is quite a bit more invasive (e.g. create an info.BasePath() to get /snap/snapname without the version, and so on). Would that be better? I'll see if I can simplify this.
zyga
reviewed
Apr 28, 2016
| @@ -78,5 +83,6 @@ func GetBasicSnapEnvVars(desc interface{}) []string { | ||
| func GetUserSnapEnvVars(desc interface{}) []string { | ||
| return fillSnapEnvVars(desc, []string{ | ||
| "SNAP_USER_DATA={{.Home}}{{.SnapPath}}", | ||
| + "SNAP_USER_SHARED_DATA={{CleanPath (printf \"%s%s/../shared\" .Home .SnapPath)}}", |
zyga
reviewed
Apr 28, 2016
| + if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { | ||
| + return err | ||
| + } | ||
| + os.Remove(filepath.Dir(dir)) |
zyga
Apr 28, 2016
Contributor
Based on
zyga@zyga-thinkpad-w510:~$ go doc os.RemoveAll
func RemoveAll(path string) error
RemoveAll removes path and any children it contains. It removes everything
it can but returns the first error it encounters. If the path does not
exist, RemoveAll returns nil (no error).
I don't think you have to check for the os.IsNotExist or call os.Remove again later.
kyrofa
Apr 28, 2016
Member
Agreed, this was taken from RemoveSnapData(). They should use the same function anyway, so I'll refactor them both.
kyrofa
Apr 29, 2016
Member
Done. Though note: the later os.Remove() call is what removes the parent directories when they're empty, so I've left that, but added a comment to clarify.
zyga
reviewed
Apr 28, 2016
| @@ -211,8 +211,8 @@ X-Snappy=yes | ||
| [Service] | ||
| ExecStart=/usr/bin/ubuntu-core-launcher app aa-profile /apps/app/1.0/bin/start | ||
| Restart=on-failure | ||
| -WorkingDirectory=/var/apps/app/1.0/ | ||
| -Environment="SNAP=/apps/app/1.0/" "SNAP_DATA=/var/apps/app/1.0/" "SNAP_NAME=app" "SNAP_VERSION=1.0" "SNAP_REVISION=44" "SNAP_ARCH=%[3]s" "SNAP_LIBRARY_PATH=/var/lib/snapd/lib/gl:" "SNAP_USER_DATA=/root/apps/app/1.0/" |
zyga
Apr 28, 2016
Contributor
Wow, this stuff is stale! /apps/app/1.0 Where did that come from?
In either case I don't think /var/appps is what we want today. I'd look at something in /var/lib/snapd/
kyrofa
Apr 28, 2016
•
Member
Indeed, lots of test cruft here. Note that it's using version instead of revision as well. I thought cleaning it would make for a noisy diff, though. Should I anyway?
zyga
Apr 29, 2016
Contributor
Perhaps as a side branch. I think it's well worth not adding more /apps test data.
|
Some comments inline. |
niemeyer
reviewed
Apr 29, 2016
| @@ -233,13 +233,15 @@ var defaultTemplate = []byte(` | ||
| # Writable home area for this version. | ||
| owner @{HOME}/snap/@{SNAP_NAME}/@{SNAP_REVISION}/** wl, | ||
| + owner @{HOME}/snap/@{SNAP_NAME}/shared/** wl, |
niemeyer
Apr 29, 2016
Contributor
Although I'd like to sync up with everyone on this, the approach looks nice and simple.
One initial comment: shared is perhaps not the best word. It also means "make it public" (share with whom?), and it may imply concurrent use which will never be the case here (only the active snap will touch it). As a preliminary strawman, I'd suggest "common".
kyrofa
Apr 29, 2016
Member
shared is perhaps not the best word. It also means "make it public" (share with whom?), and it may imply concurrent use which will never be the case here (only the active snap will touch it).
Great point, I agree.
As a preliminary strawman, I'd suggest "common".
I'm good with "common." How about the variable name format: SNAP_COMMON_DATA and SNAP_USER_COMMON_DATA? I'm concerned the latter gives the impression that the data is common among users, which of course isn't the case. Can you think of something more clear?
niemeyer
Apr 29, 2016
Contributor
Quickly discussed this with the team and the response was positive.
So let's go ahead with the proposal, just replacing the "shared" term by "common" everywhere please.
didrocks
May 2, 2016
Contributor
Reading about this, "common" makes sense from an internal code perspective for sure, I'm less sure from a developer perspective it would though.
"common" is implicit (as for "shared") that multiple entities can access to it at the same time, which isn't the case as @niemeyer suggested.
What you do actually end up with are versioned and version-independant areas. It's even more obvious when you ls in the data directory as you will have:
version1/, version2/, …, current. Why not then rather using unversioned which I guess will live better next to those versions and the current symlink? Thoughts?
niemeyer
May 2, 2016
Contributor
"common" doesn't really have any access-related concurrency connotation that I'm aware of, and searching for these terms seems to agree with that. Do you have any reference pointing out otherwise?
The fact multiple entities can access it is intended, though. That's exactly what "not versioned" means, and it's always more pleasing to label something after what it means than after what it does not mean.
didrocks
May 2, 2016
Contributor
I partially agree with it's more pleasing to label something in a non negative way. However, I do fell that "unversioned" underly a fact that "common" doesn't hilight well enough: you need to ensure data you put there are forward compatible.
The "unversioned" term will be a little bit more scary to developers for this and it's a good reason: you really need to ensure that data you put there are working with any available version of your application. You don't want non forward-compatible schema of your database for instance (I'm afraid that this directory will be used a lot for databases in particular).
"common" is a little bit more gentle in this regard and don't underline this risk… at least from a non native speaker perspective (so take that with a grain of salt ;))
kyrofa
added some commits
Apr 29, 2016
niemeyer
reviewed
Apr 29, 2016
| + return err | ||
| + } | ||
| + | ||
| + // Only remove data shared between versions if this is the last version |
niemeyer
Apr 29, 2016
Contributor
This is a bit problematic, and makes me want to think further about the whole approach we're taking. Imagine a button that says "Reset snap data".. if we keep the shared data around we're not actually resetting the data. We'll restart the snap, and its data is still there, partially! How do we reset the data for a snap, ever, once we have a shared directory?
kyrofa
Apr 29, 2016
•
Member
That sounds like new functionality, though (i.e. not involving this particular bit of logic)-- do we currently support clearing data out from under an installed snap? As part of that feature I expect we'd also clear the shared data.
niemeyer
Apr 29, 2016
Contributor
Okay, the answer is that we'll need a separate task that kills the data for all snaps at once. If we intentionally want to reset the data for a snap, it should do so for all snaps rather than just the current revision. The experience would probably be awkward otherwise.
So this is fine for now, thanks.
niemeyer
reviewed
Apr 29, 2016
| @@ -36,7 +36,10 @@ type PlaceInfo interface { | ||
| // Name returns the name of the snap. | ||
| Name() string | ||
| - // MountDir returns the base directory of the snap. | ||
| + // BaseDir returns the base directory for all versions of the snap. | ||
| + BaseDir() string |
niemeyer
Apr 29, 2016
•
Contributor
We had a BaseDir very recently, which got renamed because it was unclear. It doesn't feel much more clear now. Which base dir?
kyrofa
Apr 29, 2016
Member
Agreed. This is part of why I used filepath.Clean initially-- this directory really has no meaning except for building paths. ParentDir()? MountsDir()? MountParentDir()?
niemeyer
Apr 29, 2016
Contributor
Let's please drop all BaseDir entries. The use case in the template is better handled by exposing App to the template directly, and using the proper method names instead of rebuilding the paths inside the template.
niemeyer
reviewed
Apr 29, 2016
| } | ||
| // MinimalPlaceInfo returns a PlaceInfo with just the location information for a snap of the given name and revision. | ||
| func MinimalPlaceInfo(name string, revision int) PlaceInfo { | ||
| return &Info{SideInfo: SideInfo{OfficialName: name, Revision: revision}} | ||
| } | ||
| +// BaseDir returns the base directory for all versions of the snap. | ||
| +func BaseDir(name string) string { |
niemeyer
reviewed
Apr 29, 2016
| @@ -142,6 +156,11 @@ func (s *Info) strRevno() string { | ||
| return strconv.Itoa(s.Revision) | ||
| } | ||
| +// BaseDir returns the base directory for all versions of the snap. | ||
| +func (s *Info) BaseDir() string { |
niemeyer
reviewed
Apr 29, 2016
| @@ -63,6 +63,7 @@ func GetBasicSnapEnvVars(desc interface{}) []string { | ||
| return fillSnapEnvVars(desc, []string{ | ||
| "SNAP={{.SnapPath}}", | ||
| "SNAP_DATA=/var{{.SnapPath}}", | ||
| + "SNAP_SHARED_DATA=/var{{.SnapBasePath}}/shared", |
niemeyer
Apr 29, 2016
Contributor
Why are we building this variable here when we have a method in info that returns the exact correct path? We should have the info here and use its methods (templates can do that) instead of inventing a new way to get the same data for each one of these variables.
kyrofa
Apr 29, 2016
Member
That would involve adding stripGlobalRootDir as a template function which makes this a little less pretty. Is that okay?
kyrofa
Apr 29, 2016
Member
Note that touches a lot of tests as well. It may be better suited for another PR.
niemeyer
Apr 29, 2016
Contributor
Per prior note, let's expose the App to the template and use the existing methods directly here instead of rebuilding the path locally.
niemeyer
reviewed
Apr 29, 2016
| - Home: "$HOME", | ||
| - Target: actualBinPath, | ||
| - AaProfile: app.SecurityTag(), | ||
| + SnapName: app.Snap.Name(), |
niemeyer
Apr 29, 2016
Contributor
Let's please have a single field here, "App", and kill all of these fields which are simply accessing it by doing so in the template itself.
kyrofa
Apr 29, 2016
Member
+1, but ditto my comment about stripGlobalRootDir and the fact that this will add a lot of diff noise. Do you want me to do that here, or in another PR?
zyga
Apr 29, 2016
Contributor
I can start a parallel branch that does that and we can see where it leads.
niemeyer
reviewed
Apr 29, 2016
| + Version: app.Snap.Version, | ||
| + Revision: app.Snap.Revision, | ||
| + UdevAppName: app.SecurityTag(), | ||
| + Home: "$HOME", |
niemeyer
Apr 29, 2016
Contributor
This also shouldn't be here. We can have the string in the template itself.
kyrofa
Apr 29, 2016
Member
I'm talking about https://github.com/ubuntu-core/snappy/blob/master/systemd/systemd.go#L388 . You can't use $HOME or %h in the service file.
kyrofa
added some commits
Apr 29, 2016
|
As agreed in Telegram, I've pushed updates renaming "shared" to "common," and removed the variables from the launchers. |
niemeyer
changed the title from
interfaces,overlord: Support unversioned data.
to
interfaces,overlord: support unversioned data
Apr 29, 2016
|
LGTM |
jdstrand
reviewed
Apr 29, 2016
| # Read-only system area for other versions | ||
| /var/snap/@{SNAP_NAME}/ r, | ||
| /var/snap/@{SNAP_NAME}/** mrkix, | ||
| # Writable system area only for this version | ||
| /var/snap/@{SNAP_NAME}/@{SNAP_REVISION}/** wl, | ||
| + /var/snap/@{SNAP_NAME}/common/** wl, |
zyga
reviewed
Apr 29, 2016
| @@ -45,8 +45,14 @@ type PlaceInfo interface { | ||
| // DataDir returns the data directory of the snap. | ||
| DataDir() string | ||
| + // CommonDataDir returns the data directory common between versions of the snap. |
zyga
reviewed
Apr 29, 2016
| // DataHomeDir returns the per user data directory of the snap. | ||
| DataHomeDir() string | ||
| + | ||
| + // CommonDataHomeDir returns the per user data directory common between versions of the snap. |
zyga
reviewed
Apr 29, 2016
| @@ -157,11 +163,21 @@ func (s *Info) DataDir() string { | ||
| return filepath.Join(dirs.SnapDataDir, s.Name(), s.strRevno()) | ||
| } | ||
| +// CommonDataDir returns the data directory common between versions of the snap. |
zyga
reviewed
Apr 29, 2016
| // DataHomeDir returns the per user data directory of the snap. | ||
| func (s *Info) DataHomeDir() string { | ||
| return filepath.Join(dirs.SnapDataHomeGlob, s.Name(), s.strRevno()) | ||
| } | ||
| +// CommonDataHomeDir returns the per user data directory common between versions of the snap. | ||
| +func (s *Info) CommonDataHomeDir() string { |
zyga
reviewed
Apr 29, 2016
| // DataHomeDir returns the per user data directory of the snap. | ||
| func (s *Info) DataHomeDir() string { | ||
| return filepath.Join(dirs.SnapDataHomeGlob, s.Name(), s.strRevno()) | ||
| } | ||
| +// CommonDataHomeDir returns the per user data directory common between versions of the snap. | ||
| +func (s *Info) CommonDataHomeDir() string { | ||
| + return filepath.Join(dirs.SnapDataHomeGlob, s.Name(), "common") |
zyga
Apr 29, 2016
Contributor
This would return /home/*/snap/$snap/common. This is not a new problem but it's not clear that the returned value is a glob.
kyrofa
Apr 29, 2016
•
Member
Indeed. I think both this and DataHomeDir() should have "Glob" at the end of their names. I can do that here if you like. I actually already did it once and decided it made too much noise that didn't relate to the PR, but it's really not that invasive.
niemeyer
Apr 29, 2016
Contributor
Instead of having a Glob in the name, let's please make it take a username parameter instead which can be set to "*" in current use cases.
kyrofa
Apr 29, 2016
•
Member
I actually did that once as well. You run into problems when you try to use '$HOME' with a global root. I couldn't make it pretty.
kyrofa
Apr 29, 2016
•
Member
Also, you actually need the globs for removal, so that would require new functions (probably part of @zyga's refactor).
pedronis
Apr 29, 2016
Contributor
DataHomeDir is my fault refactoring away and not noticing that it involved actually a glob, not sure one method and one dirs pkg variable can cover all our use cases though, it seems we need to produce:
/home/*/snap/... "/root/snap/..." and "$HOME/snap/..."
niemeyer
Apr 30, 2016
Contributor
We already have that problem today, so let's merge this and tackle that separately.
zyga
reviewed
Apr 29, 2016
| +func (s *Info) CommonDataDir() string { | ||
| + return filepath.Join(dirs.SnapDataDir, s.Name(), "common") | ||
| +} | ||
| + | ||
| // DataHomeDir returns the per user data directory of the snap. | ||
| func (s *Info) DataHomeDir() string { | ||
| return filepath.Join(dirs.SnapDataHomeGlob, s.Name(), s.strRevno()) |
|
LGTM |
kyrofa commentedApr 28, 2016
•
Edited 1 time
-
kyrofa
Apr 28, 2016
The current versioned data directories are an important aspect of the upgrade/rollback strategy, but they don't scale well for applications that need to hold a vast amount of non-version-specific data (LP: #1559248). This PR is an attempt to implement support for unversioned data (i.e. data that is shared between versions of a given snap), conforming to Jamie's suggestion (note that the naming is of course up for discussion). The shared directories are removed by overlord only once the final version is removed.
Note that this will also require changes to
ubuntu-core-launcherto create the user-specific shared data directory (as it creates the versioned data directory). That can come once this PR is approved.