-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
base: main
Are you sure you want to change the base?
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]>
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: 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 |
[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore. |
cmd/podman/volumes/export.go
Outdated
@@ -46,54 +38,20 @@ func init() { | |||
flags := exportCommand.Flags() | |||
|
|||
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
@@ -139,3 +144,59 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, | |||
|
|||
return response.IsSuccess(), nil | |||
} | |||
|
|||
// 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
@@ -51,8 +51,7 @@ func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, env []stri | |||
} | |||
|
|||
// 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 |
2a2f435
to
f6bbae0
Compare
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]>
Squashed, CI going green |
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
logrus.Debugf("untarring %s", tarball.Name()) | ||
return archive.Untar(tarball, dest, options) | ||
} | ||
|
||
// Creates a new tar file and writes bytes from io.ReadCloser | ||
func CreateTarFromSrc(source string, dest string) 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.
this is unused now so it should be removed
if err := containerEngine.VolumeExport(ctx, args[0], exportOpts); err != nil { | ||
return 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.
nit might as well just return directly here
containerEngine := registry.ContainerEngine() | ||
ctx := context.Background() | ||
|
||
if err := containerEngine.VolumeImport(ctx, args[0], opts); err != nil { |
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: same thing could return directly with the err != nil
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?