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

mount-control: step 3 #10864

Merged
merged 3 commits into from
Jan 18, 2022
Merged

mount-control: step 3 #10864

merged 3 commits into from
Jan 18, 2022

Conversation

mardy
Copy link
Contributor

@mardy mardy commented Sep 30, 2021

This adds support for persistent mounts, via a snapctl subcommand.

@codecov-commenter
Copy link

codecov-commenter commented Sep 30, 2021

Codecov Report

Merging #10864 (0559698) into master (7594879) will increase coverage by 0.02%.
The diff coverage is 98.15%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #10864      +/-   ##
==========================================
+ Coverage   78.37%   78.40%   +0.02%     
==========================================
  Files         923      925       +2     
  Lines      105246   105409     +163     
==========================================
+ Hits        82484    82643     +159     
- Misses      17624    17627       +3     
- Partials     5138     5139       +1     
Flag Coverage Δ
unittests 78.40% <98.15%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
overlord/hookstate/ctlcmd/mount.go 97.70% <97.70%> (ø)
interfaces/builtin/mount_control.go 98.28% <100.00%> (+0.02%) ⬆️
overlord/hookstate/ctlcmd/umount.go 100.00% <100.00%> (ø)
overlord/ifacestate/helpers.go 76.96% <0.00%> (-0.49%) ⬇️
cmd/snap/cmd_debug_state.go 70.64% <0.00%> (+0.45%) ⬆️
store/cache.go 71.15% <0.00%> (+1.92%) ⬆️

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 7594879...0559698. Read the comment docs.

@mardy mardy force-pushed the mount-control-3 branch 3 times, most recently from f4d6bb6 to e19b4d8 Compare October 1, 2021 11:58
@pedronis pedronis self-requested a review October 7, 2021 13:26
@woodrow-shen
Copy link
Contributor

@mardy @pedronis do you think it's reasonable to add mountpoint command into mount-control as we have a use case that can rely on it to query the path is mounted or not?

@mardy
Copy link
Contributor Author

mardy commented Nov 11, 2021

@mardy @pedronis do you think it's reasonable to add mountpoint command into mount-control as we have a use case that can rely on it to query the path is mounted or not?

Yes, to me it makes sense. But I'll let @pedronis make the decision (this will also require a rule like @{PROC}/@{pid}/mountinfo r)

@mardy mardy removed the ⛔ Blocked label Dec 9, 2021
@pedronis pedronis added the Needs Samuele review Needs a review from Samuele before it can land label Dec 9, 2021
@woodrow-shen
Copy link
Contributor

@mardy @pedronis do you think it's reasonable to add mountpoint command into mount-control as we have a use case that can rely on it to query the path is mounted or not?

Yes, to me it makes sense. But I'll let @pedronis make the decision (this will also require a rule like @{PROC}/@{pid}/mountinfo r)

@pedronis hi, do you have any concern about supporting mountpoint in this interface? Thanks.

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

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

overall looks good, bunch of initial comments

var (
shortMountHelp = i18n.G("Create a temporary or permanent mount")
longMountHelp = i18n.G(`
The mount command mounts the given source onto the given destination path.`)
Copy link
Collaborator

Choose a reason for hiding this comment

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

the long help should mention mount-control

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

Where string `positional-arg-name:"<where>" required:"yes" description:"path to the destination mount point"`
} `positional-args:"yes" required:"yes"`
Persistent bool `long:"persistent" description:"make the mount persist across reboots"`
Type string `long:"type" short:"t" description:"partition type"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/partition/filesystem/

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

sysd := systemd.New(systemd.SystemMode, nil)
name, err := m.createMountUnit(sysd)
if err != nil {
return fmt.Errorf("Failed to create mount unit: %v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

error should be lowercase, s/Failed to/cannot/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

}

if err := sysd.Start(name); err != nil {
// TODO remove mount unit?
Copy link
Collaborator

Choose a reason for hiding this comment

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

yes, that makes sense I think

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


if err := sysd.Start(name); err != nil {
// TODO remove mount unit?
return fmt.Errorf("Failed to start mount unit: %v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

same about the error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

var (
shortUmountHelp = i18n.G("Undo a temporary or permanent mount")
longUmountHelp = i18n.G(`
The umount command unmounts the given mount point.`)
Copy link
Collaborator

Choose a reason for hiding this comment

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

umount should mention that the mount point should have been created with snapctl mount

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

}

if m.Type != "" {
// TODO: what should we do if the type is not given?
Copy link
Collaborator

Choose a reason for hiding this comment

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

we should accept Type to be empty only if it's empty also in the attributes. In that case we also will omit from the unit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

return false
}

expandedPattern := snapInfo.ExpandSnapVariables(pattern)
Copy link
Collaborator

Choose a reason for hiding this comment

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

afaict the unit tests don't exercise this code, we need test for $SNAP_DATA and $SNAP_COMMON and the valid/invalid combinations with persistent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added more tests.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see them, forgot to push something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code is now tested (it appears green in the coverage report), and the combinatio with persistent is tested here. You didn't mention $SNAP_COMMON in your other comment, so I didn't treat it specially. Let me know if I should.

Copy link
Collaborator

Choose a reason for hiding this comment

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

coverage and tested are not the same thing. I haven't rechecked but if this line is removed no tests fail, so this is not tested.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, I added one test now. I wanted to mock snapInfo.ExpandSnapVariables() but snap.Info is not an interface, so I fell back on using the production code and just compose the "where" in such a way that it would replicate the logic of info.DataDir().

st.Lock()
defer st.Unlock()

conns, err := ifacestate.ConnectionStates(st)
Copy link
Collaborator

Choose a reason for hiding this comment

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

for a follow up but now I think we have 2 or 3 places in ctlcmd doing matches against this, maybe they can share a helper? worth checking at least

@pedronis pedronis self-requested a review December 16, 2021 14:04
Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

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

another pass, some feedback changes seems not pushed

@@ -37,7 +38,9 @@ import (
var (
shortMountHelp = i18n.G("Create a temporary or permanent mount")
longMountHelp = i18n.G(`
The mount command mounts the given source onto the given destination path.`)
The mount command mounts the given source onto the given destination path,
provided that the snap has a plug for the mount-control interface which allow
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/allow/allows/

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

return false
}

expandedPattern := snapInfo.ExpandSnapVariables(pattern)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see them, forgot to push something?

return false
}
} else {
// The filesystem type was not given; we let it through only if the
Copy link
Collaborator

@pedronis pedronis Dec 16, 2021

Choose a reason for hiding this comment

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

is this scenario tested?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, one branch was tested by the existing tests, and this new else is tested with this small addition

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see a test for the happy case where both the plug and the command don't have type

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 newly added TestHappyWithVariableExpansion() incidentally tests that. Let me know if you'd rather have a separate test.

@pedronis pedronis self-requested a review December 17, 2021 09:02
Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

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

thank you, one question about expanding the spread test


echo "Verify that a mount not matching the allowed FS type will fail"
mkdir -p /media/somedir
if "${SNAP_NAME}".cmd mount -t debugfs "/dev/sda" "/media/somedir"; then
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 add a happy/positive test using -t ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah! I tried it with tmpfs, just to realize that the interface does not really support it. I created #11190 to address the issue (and this also adds a positive test for the -t option)

Copy link
Contributor

@miguelpires miguelpires left a comment

Choose a reason for hiding this comment

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

LGTM

return err
}

if m.Positional.Where == "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this already guaranteed by the required:"yes" in line 43?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't seem so; at least, the unit test at https://github.com/mardy/snapd/blob/mount-control-3/overlord/hookstate/ctlcmd/umount_test.go#L108-L111 is able to trigger this condition.

Copy link
Contributor

@miguelpires miguelpires Jan 13, 2022

Choose a reason for hiding this comment

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

It doesn't seem so; at least, the unit test at https://github.com/mardy/snapd/blob/mount-control-3/overlord/hookstate/ctlcmd/umount_test.go#L108-L111 is able to trigger this condition.

The test isn't triggering the go-flags validation because of the empty string arg (I left a comment in the test). Fixing that means we can also remove this.
(I quoted your comment because even though I replied in the thread, it doesn't show in the new review's comment which makes this look like I'm talking to myself)


found := false
for _, where := range mountPoints {
if where == m.Positional.Where {
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: you could invert the condition and continue to remove a level of nesting but the code isn't contrived as it is so might not be worth it

// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2021 Canonical Ltd
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Copyright (C) 2021 Canonical Ltd
* Copyright (C) 2022 Canonical Ltd

// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2021 Canonical Ltd
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Copyright (C) 2021 Canonical Ltd
* Copyright (C) 2022 Canonical Ltd

// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2021 Canonical Ltd
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Copyright (C) 2021 Canonical Ltd
* Copyright (C) 2022 Canonical Ltd

// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2021 Canonical Ltd
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Copyright (C) 2021 Canonical Ltd
* Copyright (C) 2022 Canonical Ltd

Copy link
Contributor

@stolowski stolowski left a comment

Choose a reason for hiding this comment

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

LGTM

)

var (
shortUmountHelp = i18n.G("Undo a temporary or permanent mount")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick^2, maybe "Remove a temporary.." or "Unmount a temporary.." ?

Comment on lines +109 to +110
_, _, err := ctlcmd.Run(s.mockContext, []string{"umount", ""}, 0)
c.Check(err, ErrorMatches, `mount point cannot be empty`)
Copy link
Contributor

Choose a reason for hiding this comment

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

This test isn't triggering the go-flags validation because of the empty string arg. Removing it fixes it (which means we also don't need to do the manual validation in umount.go)

Suggested change
_, _, err := ctlcmd.Run(s.mockContext, []string{"umount", ""}, 0)
c.Check(err, ErrorMatches, `mount point cannot be empty`)
_, _, err := ctlcmd.Run(s.mockContext, []string{"umount"}, 0)
c.Check(err, ErrorMatches, "the required argument `<where>` was not provided")

return err
}

if m.Positional.Where == "" {
Copy link
Contributor

@miguelpires miguelpires Jan 13, 2022

Choose a reason for hiding this comment

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

It doesn't seem so; at least, the unit test at https://github.com/mardy/snapd/blob/mount-control-3/overlord/hookstate/ctlcmd/umount_test.go#L108-L111 is able to trigger this condition.

The test isn't triggering the go-flags validation because of the empty string arg (I left a comment in the test). Fixing that means we can also remove this.
(I quoted your comment because even though I replied in the thread, it doesn't show in the new review's comment which makes this look like I'm talking to myself)

@mvo5 mvo5 merged commit 235cbb4 into canonical:master Jan 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Samuele review Needs a review from Samuele before it can land
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants