Skip to content

Support DriveMount API in CreateVM. #297

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

Merged
merged 2 commits into from
Oct 25, 2019

Conversation

sipsma
Copy link
Contributor

@sipsma sipsma commented Oct 17, 2019

This implements the proposal found in docs/drive-mounts-proposal.md, with the
exception for supporting RateLimiters and IsReadOnly in the DriveMount objects,
due to the need for further refactoring of the internal stub drive code (will
be followed up in #296).

Signed-off-by: Erik Sipsma [email protected]

This change will require a fair bit of non-trivial rebasing with the jailer PR, so I'd like to let that PR get merged first and then rebase this one on top if at all possible.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

}

for _, systemDir := range bannedSystemDirs {
if strings.HasPrefix(filepath.Clean(resolvedDest), filepath.Clean(systemDir)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

  • You don't have to call filepath.Clean() on systemDir, while it is harmless
  • Wouldn't it match a path that technically contain systemDir such as /system?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point on it matching /system. Will fix

logger := log.G(ctx).WithField("MountDriveRequest", req.String())
logger.Debug()

driveID := strings.TrimSpace(req.DriveID)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we just use req.DriveID as is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I think so, this was just copied from the previous implementation, but unless I'm missing something TrimSpace shouldn't be needed, will update

xibz
xibz previously requested changes Oct 17, 2019
// provided path does not exist. It will evaluate symlinks present in the path up to a component that doesn't
// exist, at which point it will just append the rest of the provided path to what has been resolved so far.
func evalAnySymlinks(path string) (string, error) {
curPath := "/"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a comment here saying that we validate that the curPath must be at /?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, not only do we validate it, but we override it if it isn't.

IMO this function should be given a name that more clearly indicates what it does. Converting a relative path to an absolute path doesn't have anything to do with symlinks.

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 point of this function is not to convert a relative path into an absolute path, it's to see if any part of the provided path already exists and, if so, evaluate any symlinks in the part of the path that exists. I needed to add it because the standard EvalSymlinks function in the standard lib will always return an error if any part of the provided path doesn't exist yet.

I.e. if you are given /foo/bar/baz/ and /foo is a symlink to /other, but /other/bar/baz doesn't exist yet, then EvalSymlinks will return an error. evalAnySymlinks, on the other hand, will return /other/bar/baz.

I updated the comment on the function to also state that it's assumed path is an absolute path, which is validation we do earlier. Given this is just a private, one-off util function only used in this package, I'm okay with that, but let me know if there's disagreement.

agent/service.go Outdated
if err != nil {
cleanupErr := ts.doCleanup(taskID, execID)
if cleanupErr != nil {
logger.WithError(err).Error("failed to cleanup task")
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we not want to log the cleanup error as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do, this was an oversight, will fix

agent/service.go Outdated
if err != nil {
cleanupErr := ts.doCleanup(taskID, execID)
if cleanupErr != nil {
logger.WithError(err).Error("failed to cleanup task")
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here regarding not logging cleanupErr

}

// see man 5 fstab for the format of /proc/mounts
var source string
Copy link
Contributor

Choose a reason for hiding this comment

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

would prefer

var (
    source string
    dest string
    ...
)

}

_, err = s.driveMountClient.MountDrive(requestCtx, &drivemount.MountDriveRequest{
DriveID: *driveID,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should use firecracker.StringValue here to prevent panics

retryDelay = 10 * time.Millisecond
)

for i := 0; i < maxRetries; i++ {
Copy link
Contributor

Choose a reason for hiding this comment

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

In which cases drive might not be mountable?

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 can happen when we've patched a stub drive but the OS has not yet realized a change was made, which is rare but did happen in practice occasionally. We needed these retries in the previous implementation too, I just migrated the code as part of this change.

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think this is useful context and it worth to leave comments explaining the motivation behind retries.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah good call, added a comment w/ link to original issue


func (s *service) mountDrives(requestCtx context.Context, driveMounts []*proto.FirecrackerDriveMount) error {
for _, driveMount := range driveMounts {
driveID, err := s.stubDriveHandler.PatchStubDrive(requestCtx, s.machine, driveMount.HostPath)
Copy link
Contributor

@xibz xibz Oct 17, 2019

Choose a reason for hiding this comment

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

PatchStubDrive should return a string as opposed to *string.

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

Copy link
Contributor

@mxpv mxpv left a comment

Choose a reason for hiding this comment

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

LGTM

retryDelay = 10 * time.Millisecond
)

for i := 0; i < maxRetries; i++ {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think this is useful context and it worth to leave comments explaining the motivation behind retries.

Copy link
Contributor

@kzys kzys left a comment

Choose a reason for hiding this comment

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

LGTM

@sipsma
Copy link
Contributor Author

sipsma commented Oct 21, 2019

Have the approvals now, but will be waiting until the Jailer PR is merged and do a rebase on top of that before merging this one. Any more comments in the meantime are welcome though of course.

@sipsma sipsma force-pushed the drivemount branch 3 times, most recently from 807b42b to 0f7ac63 Compare October 24, 2019 22:18
@sipsma
Copy link
Contributor Author

sipsma commented Oct 24, 2019

Rebased on the Jailer PR. It ended up being straightforward to resolve conflicts, nothing substantive had to change in the drive mount logic or the jailer logic.

@sipsma sipsma requested review from mxpv, xibz and kzys October 24, 2019 22:37
@sipsma
Copy link
Contributor Author

sipsma commented Oct 25, 2019

I just realized that because the jailer config is not enabled by default there currently aren't integ-tests that use both drive mounts and the jailer, I'm gonna update with that before merging too. Looking at the jailer code again I think this will require some updates to the "ExposeDeviceToJail" method in order to handle exposing flat files in addition to exposing block devices.

This implements the proposal found in docs/drive-mounts-proposal.md, with the
exception for supporting RateLimiters and IsReadOnly in the DriveMount objects,
due to the need for further refactoring of the internal stub drive code (will
be followed up in firecracker-microvm#296).

Signed-off-by: Erik Sipsma <[email protected]>
The primary change required is for the Jailer to support exposing regular files
to the jail in addition to the pre-existing support for block devices.

Signed-off-by: Erik Sipsma <[email protected]>
@sipsma sipsma dismissed xibz’s stale review October 25, 2019 22:34

Addressed @xibz's comments

@sipsma sipsma merged commit 38742ee into firecracker-microvm:master Oct 25, 2019
@sipsma sipsma deleted the drivemount branch October 25, 2019 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants