Conversation
miguelpires
left a comment
There was a problem hiding this comment.
Just a small code-focused review, mostly on naming and stuff. I looked at the draft PR to get some context but I'm likely still missing a lot of it =) Didn't look at the tests yet, will do later
| PersistentUnit UnitLifetime = iota | ||
| TransientUnit |
There was a problem hiding this comment.
Nitpick: "Unit" is repeated across the value and type, and could be removed from the former.
This would be enough IMO:
| PersistentUnit UnitLifetime = iota | |
| TransientUnit | |
| Persistent UnitLifetime = iota | |
| Transient |
| case "FragmentPath": | ||
| fragmentPath = splitVal[1] | ||
| default: | ||
| return nil, fmt.Errorf("Unexpected property %q", splitVal[0]) |
There was a problem hiding this comment.
Error messages should start with lowercase since they're often composed
| return nil, fmt.Errorf("Unexpected property %q", splitVal[0]) | |
| return nil, fmt.Errorf("unexpected property %q", splitVal[0]) |
| return filepath.Join(dirs.SnapServicesDir, escapedPath+".mount") | ||
| } | ||
|
|
||
| // MountUnitPathWithLifetime returns the path of a transient {,auto}mount unit |
There was a problem hiding this comment.
I think the word transient is out of place in this comment right?
There was a problem hiding this comment.
Yes, it makes no sense -- probably a leftover from a previous formulation :-)
Fixed now.
| LogReader(services []string, n int, follow bool) (io.ReadCloser, error) | ||
| // AddMountUnitFile adds/enables/starts a mount unit. | ||
| AddMountUnitFile(name, revision, what, where, fstype string) (string, error) | ||
| // AddMountUnitFileFull adds/enables/starts a mount unit with options. |
There was a problem hiding this comment.
| // AddMountUnitFileFull adds/enables/starts a mount unit with options. | |
| // AddMountUnitFileWithOptions adds/enables/starts a mount unit with options. |
| for _, line := range lines { | ||
| splitVal := strings.SplitN(string(line), "=", 2) | ||
| if len(splitVal) != 2 { | ||
| return nil, fmt.Errorf("Cannot parse systemctl output: %q", line) |
There was a problem hiding this comment.
Error messages should start with lowercase since they're often composed
| return nil, fmt.Errorf("Cannot parse systemctl output: %q", line) | |
| return nil, fmt.Errorf("cannot parse systemctl output: %q", line) |
|
|
||
| // Is this an error, or are there valid cases when this might happen? | ||
| if where == "" { | ||
| continue |
There was a problem hiding this comment.
I'd say this is an error since the documentation states it's a mandatory option in the mount file but I'll leave people more knowledgeable than me to comment. If, in case of doubt, we want to not fail, we can also log that it happened unexpectedly
There was a problem hiding this comment.
Yeah I agree this is probably an error, also the point by which we got here we know that this mount unit was written by us, and we should never purposefully write a mount unit without a Where= so this is an error
Codecov Report
@@ Coverage Diff @@
## master #10653 +/- ##
==========================================
- Coverage 78.35% 78.34% -0.01%
==========================================
Files 888 888
Lines 99852 100012 +160
==========================================
+ Hits 78237 78355 +118
- Misses 16712 16746 +34
- Partials 4903 4911 +8
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
|
|
||
| func (b Backend) RemoveSnapMountUnits(s snap.PlaceInfo, meter progress.Meter) error { | ||
| if err := wrappers.RemoveMountUnitFiles(s, "", meter); err != nil { | ||
| logger.Noticef("Cannot remove mount units for %q: %v", s.InstanceName(), err) |
There was a problem hiding this comment.
I'm not sure what the intended behaviour is here regarding failures. Can you explain to me why is this logged? wrappers.RemoveMountUnitFiles returns an error immediately on failure to remove a mount so, if one removal fails, RemoveMountUnitFiles won't try to remove the others but the caller of this method also can't react since it never gets an error.
There was a problem hiding this comment.
You are right, this does not make much sense. It's a leftover from when I moved this code away from link.go, where this actually made sense. I'll fix it. :-)
There was a problem hiding this comment.
Thanks 🙂 I think there's some unit tests failing, probably related to this change
anonymouse64
left a comment
There was a problem hiding this comment.
some nitpicks, but otherwise lgtm
| type MountUnitOptions struct { | ||
| // Whether the unit is transient or persistent across reboots | ||
| Lifetime UnitLifetime | ||
| SnapName, Revision, What, Where, Fstype string |
There was a problem hiding this comment.
nitpick, I think it's easier to read if these were all on their own line
| SnapName, Revision, What, Where, Fstype string | |
| SnapName string | |
| Revision string | |
| What string | |
| Where string | |
| Fstype string |
| content := fmt.Sprintf(mountUnitTemplate, snapName, revision, what, where, fstype, strings.Join(options, ",")) | ||
| mu := MountUnitPath(where) | ||
| const ( | ||
| snappyCreatorModule = "X-SnapdOrigin" |
There was a problem hiding this comment.
nitpick: if the setting we put into the mount unit file is Origin, can we use Origin instead of Creator for all the properties/variables/etc. here too?
|
|
||
| // Is this an error, or are there valid cases when this might happen? | ||
| if where == "" { | ||
| continue |
There was a problem hiding this comment.
Yeah I agree this is probably an error, also the point by which we got here we know that this mount unit was written by us, and we should never purposefully write a mount unit without a Where= so this is an error
| } | ||
| } | ||
|
|
||
| type Interacter interface { |
There was a problem hiding this comment.
if the function below is using the exported Interactor from the wrappers package, why do we also introduce one here?
|
|
||
| sysd := New(SystemMode, nil) | ||
| units, err := sysd.ListMountUnits("some-snap", "") | ||
| c.Check(units, IsNil) |
There was a problem hiding this comment.
I have better luck when I expect a list to be empty to use
| c.Check(units, IsNil) | |
| c.Check(units, HasLen, 0) |
otherwise I will inevitably end up refactoring the function to return an empty list instead of a nil one and the IsNil condition is false
|
|
||
| sysd := New(SystemMode, nil) | ||
| units, err := sysd.ListMountUnits("some-snap", "") | ||
| c.Check(units, IsNil) |
There was a problem hiding this comment.
in error cases usually I will omit the first argument and just check that the error is what we expect it to be, but if you want to keep this case I would suggest changing it like above to be HasLen based
| c.Check(err, ErrorMatches, "cannot parse systemctl output:.*") | ||
| } | ||
|
|
||
| func (s *SystemdTestSuite) TestListMountUnitsHappy(c *C) { |
There was a problem hiding this comment.
It would be nice to see a test with both units created by AddMountUnitFileWithOptions and then those units returned/found by ListMountUnits, since we expect them to work together IRL too
There was a problem hiding this comment.
I don't think it's possible to do it without also mocking the systemctl command, at which point I don't see an advantage in combining the unit file creation and listing in the same tests (because we would still have a mock between them).
pedronis
left a comment
There was a problem hiding this comment.
I didn't quite finish a full review but some things that caught my eye for now
| "github.com/snapcore/snapd/progress" | ||
| "github.com/snapcore/snapd/snap" | ||
| "github.com/snapcore/snapd/systemd" | ||
| "github.com/snapcore/snapd/wrappers" |
There was a problem hiding this comment.
I didn't notice originally that this used systemd directly, from that POV maybe it doesn't make sense to have the helper in wrappers, the code could just live here directly.
| } | ||
|
|
||
| func (b Backend) RemoveSnapMountUnits(s snap.PlaceInfo, meter progress.Meter) error { | ||
| return wrappersRemoveMountUnitFiles(s, "", meter) |
There was a problem hiding this comment.
relatedly this is mocked in the tests here and the code in wrappers doesn't have unit tests. Anyway as I said maybe the code in wrappers should just move herere because the other functions here use systemd directly anyway...
There was a problem hiding this comment.
Removed. For adding more tests, I'd like to get #10704 merged first. Or if you are fine with the current amount of tests, of course we can land this first and add tests later. :-)
994d202 to
9938875
Compare
pedronis
left a comment
There was a problem hiding this comment.
looks good, some detail comments and questions
There was a problem hiding this comment.
baseDir is not the clearest name? wouldn't mountpointDir be a better name?
There was a problem hiding this comment.
is this going later to be used outside of systemd? because it's not in the current PR
There was a problem hiding this comment.
Done. This is not currently used, but it will be used by the snapctl mount command.
There was a problem hiding this comment.
same comments as for the previous helper
2ae2e17 to
dd472d6
Compare
mvo5
left a comment
There was a problem hiding this comment.
Looks fine, some nitpicks/ideas inline
There was a problem hiding this comment.
(nitpick^2) I think it's fine to have this and the following in a single line, go has no hard rules here and we are not really consistent but I personally like (slightly) longer lines over this wrapping. But it's fine, no need to change it, just couldn't not comment on it :)
There was a problem hiding this comment.
(nitpick) maybe this shold include the lifetime valid that it did not understand? i.e. panin(fmt.Sprintf("unknown systemd unit lifetime %q", lifetime)
There was a problem hiding this comment.
Could this instead be an error? I am a bit worried about panics in snapd in general and given that we already return errors here at least snap would not crash and potentially run into an endless crash loop (if this is part of a task snapd would keep trying to run the task) with an error instead of a panic?
There was a problem hiding this comment.
My reasoning was that passing nil for the option is a programming error, something that in C would be handled by an assert(): panic when running tests (or in debug mode), and invisible in production. But I see that golang has not such a thing, so I think the best option here is to follow your advice and return an error.
There was a problem hiding this comment.
(nitpick^2) I wonder if text.Template would make things easier to read here - probably followup material and maybe/likely not really helpful but another one of my idle thoughts :)
There was a problem hiding this comment.
I'll take this suggestion in a follow-up PR, since the change is not trivial and I don't want to delay this too much :-)
Taking a struct with parameters makes the method more extensible. We also add the information about which module caused the creation of the mount unit, and do not write the revision unless explicitly requested.
fee9abb to
793e210
Compare
This includes the changes in systemd, overlord and wrappers to create, list and remove mount unit files.