-
Notifications
You must be signed in to change notification settings - Fork 200
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
Conversation
agent/drive_handler.go
Outdated
} | ||
|
||
for _, systemDir := range bannedSystemDirs { | ||
if strings.HasPrefix(filepath.Clean(resolvedDest), filepath.Clean(systemDir)) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
agent/drive_handler.go
Outdated
logger := log.G(ctx).WithField("MountDriveRequest", req.String()) | ||
logger.Debug() | ||
|
||
driveID := strings.TrimSpace(req.DriveID) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
// 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 := "/" |
There was a problem hiding this comment.
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 /
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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
internal/fsutil.go
Outdated
} | ||
|
||
// see man 5 fstab for the format of /proc/mounts | ||
var source string |
There was a problem hiding this comment.
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
...
)
runtime/service.go
Outdated
} | ||
|
||
_, err = s.driveMountClient.MountDrive(requestCtx, &drivemount.MountDriveRequest{ | ||
DriveID: *driveID, |
There was a problem hiding this comment.
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++ { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this 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++ { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
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. |
807b42b
to
0f7ac63
Compare
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. |
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]>
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.