-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add remote support for podman volume import and podman volume export
#26434
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
Previously, our approach was to inspect the volume, grab its mountpoint, and tar that up, all in the CLI code. There's no reason why that has to be in the CLI - if we move it into Libpod, and add a REST endpoint to stream the tar, we can enable it for the remote client as well. As a bonus, previously, we could not properly handle volumes that needed to be mounted. Now, we can mount the volume if necessary, and as such export works with more types of volumes, including volume drivers. Signed-off-by: Matt Heon <[email protected]>
|
[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore. |
cmd/podman/volumes/export.go
Outdated
|
|
||
| outputFlagName := "output" | ||
| flags.StringVarP(&cliExportOpts.Output, outputFlagName, "o", "/dev/stdout", "Write to a specified file (default: stdout, which must be redirected)") | ||
| flags.StringVarP(&cliExportOpts.OutputPath, outputFlagName, "o", "/dev/stdout", "Write to a specified file (default: stdout, which must be redirected)") |
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.
/dev/stdout is not a working default for windows
cmd/podman/volumes/import.go
Outdated
| tarPath := args[1] | ||
| filePath := args[1] | ||
| if filePath == "-" { | ||
| filePath = "/dev/stdin" |
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.
again not a proper path for windows it would be best if you could just pass an io.Writer/io.Reader all the ways through the stack
then you can just use os.Stdin
| err := v.mount() | ||
| mountPoint := v.mountPoint() | ||
| v.lock.Unlock() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer func() { | ||
| v.lock.Lock() | ||
| defer v.lock.Unlock() | ||
|
|
||
| if err := v.unmount(false); err != nil { | ||
| logrus.Errorf("Error unmounting volume %s: %v", v.Name(), err) | ||
| } | ||
| }() |
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 actually want to unlock, I realize this is what the code did before but like in what world is it sane to allow volume rm while export/import is running
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 unlock for commands like podman cp so I see no reason not to here?
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.
Mhh, I guess fair enough as we don't want things like a volume ls being blocked for a long time but when I make the listing parts not holding the lock I like to revalue.
| v.lock.Lock() | ||
| err := v.mount() | ||
| mountPoint := v.mountPoint() | ||
| v.lock.Unlock() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer func() { | ||
| v.lock.Lock() | ||
| defer v.lock.Unlock() | ||
|
|
||
| if err := v.unmount(false); err != nil { | ||
| logrus.Errorf("Error unmounting volume %s: %v", v.Name(), err) | ||
| } | ||
| }() |
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 thing here
pkg/api/handlers/libpod/volumes.go
Outdated
| tmpfile, err := os.CreateTemp("", "podman-volume-import-") | ||
| if err != nil { | ||
| utils.Error(w, http.StatusInternalServerError, fmt.Errorf("creating temporary file: %w", err)) | ||
| return | ||
| } | ||
| defer func() { | ||
| tmpfile.Close() | ||
| os.Remove(tmpfile.Name()) | ||
| }() | ||
|
|
||
| if _, err := io.Copy(tmpfile, r.Body); err != nil { | ||
| utils.Error(w, http.StatusInternalServerError, fmt.Errorf("copying archive to temporary file: %w", err)) | ||
| return | ||
| } |
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.
why a temporary copy at all, just pipe the body to import to avoid any copies.
Other than that I see no seek(0) called on tmpfile so wouldn't it get EOF right away?
pkg/bindings/volumes/volumes.go
Outdated
| } | ||
|
|
||
| // Export exports a volume to the given path | ||
| func Export(ctx context.Context, nameOrID string, options entities.VolumeExportOptions) error { |
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.
I would expect the bindings to just return the stream so the caller can write or do what they want, otherwise you force all bindings users to a temporary file if they need to inspect the stream for whatever reasons
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.
Oh and also bindings options don't use entities structs, they should use specific bindings types
pkg/bindings/volumes/volumes.go
Outdated
| func Import(ctx context.Context, nameOrID string, options entities.VolumeImportOptions) error { | ||
| if options.InputPath == "" { | ||
| return errors.New("must provide valid path for file to write to") | ||
| } | ||
|
|
||
| targetFile, err := os.Create(options.InputPath) | ||
| if err != nil { | ||
| return fmt.Errorf("unable to create target file path %q: %w", options.InputPath, err) | ||
| } | ||
| defer targetFile.Close() |
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 here I would expect the caller to provider a io.Reader
utils/utils.go
Outdated
| // UntarToFileSystem untars an os.file of a tarball to a destination in the filesystem | ||
| func UntarToFileSystem(dest string, tarball *os.File, options *archive.TarOptions) error { | ||
| logrus.Debugf("untarring %s", tarball.Name()) | ||
| func UntarToFileSystem(dest string, tarball io.Reader, options *archive.TarOptions) error { |
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.
what do we gain from this function? the caller should just use archive.Untar directly then
e4a277b to
029c547
Compare
|
Note "Address review comments" is general not a useful commit title for me, I suggest that gets squashed |
|
Yep, once CI is passing I'll squash down |
f6bbae0 to
da6580b
Compare
|
Squashed, CI going green |
Luap99
left a comment
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.
just some minor nits, overall LGTM
As with `volume export`, this was coded up exclusively in cmd/ instead of in libpod. Move it into Libpod, add a REST endpoint, add bindings, and now everything talks using the ContainerEngine wiring. Also similar to `volume export` this also makes things work much better with volumes that require mounting - we can now guarantee they're actually mounted, instead of just hoping. Includes some refactoring of `volume export` as well, to simplify its implementation and ensure both Import and Export work with readers/writers, as opposed to just files. Fixes containers#26409 Signed-off-by: Matt Heon <[email protected]>
Luap99
left a comment
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, but FYI your branch is not rebased and thus a bit out of date. Not a blocker I as I don't expect other changes to cause a conflict but still try to remember to rebase on pushes
|
@containers/podman-maintainers This is ready to merge, PTAL |
lsm5
left a comment
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
lsm5
left a comment
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
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: lsm5, Luap99, mheon The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
683e9b2
into
containers:main
These were coded up entirely in cmd/, which meant no API support. Refactor them into libpod/, add API endpoints, all that fun stuff!
Fixes #26409
Does this PR introduce a user-facing change?