Skip to content

Commit e4a277b

Browse files
committed
Address review comments
Instead of using files in the ContainerEngine API for volume import/export, expose readers/writers, so we can use os.Stdin and os.Stdout - meaning that things will work on Windows, where /dev/stdin and /dev/stdout don't exist. Avoid the use of a temporary file on import by reading straight from the request body. Remove a utility function that doesn't actually do anything. Signed-off-by: Matt Heon <[email protected]>
1 parent 37af2af commit e4a277b

File tree

11 files changed

+67
-98
lines changed

11 files changed

+67
-98
lines changed

cmd/podman/volumes/export.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package volumes
33
import (
44
"context"
55
"errors"
6+
"fmt"
7+
"os"
68

79
"github.com/containers/common/pkg/completion"
810
"github.com/containers/podman/v5/cmd/podman/common"
@@ -27,7 +29,7 @@ Allow content of volume to be exported into external tar.`
2729
)
2830

2931
var (
30-
cliExportOpts entities.VolumeExportOptions
32+
targetPath string
3133
)
3234

3335
func init() {
@@ -38,19 +40,32 @@ func init() {
3840
flags := exportCommand.Flags()
3941

4042
outputFlagName := "output"
41-
flags.StringVarP(&cliExportOpts.OutputPath, outputFlagName, "o", "/dev/stdout", "Write to a specified file (default: stdout, which must be redirected)")
43+
flags.StringVarP(&targetPath, outputFlagName, "o", "", "Write to a specified file (default: stdout, which must be redirected)")
4244
_ = exportCommand.RegisterFlagCompletionFunc(outputFlagName, completion.AutocompleteDefault)
4345
}
4446

4547
func export(cmd *cobra.Command, args []string) error {
4648
containerEngine := registry.ContainerEngine()
4749
ctx := context.Background()
4850

49-
if cliExportOpts.OutputPath == "" {
50-
return errors.New("expects output path, use --output=[path]")
51+
if targetPath == "" && cmd.Flag("output").Changed {
52+
return errors.New("must provide valid path for file to write to")
5153
}
5254

53-
if err := containerEngine.VolumeExport(ctx, args[0], cliExportOpts); err != nil {
55+
exportOpts := entities.VolumeExportOptions{}
56+
57+
if targetPath != "" {
58+
targetFile, err := os.Create(targetPath)
59+
if err != nil {
60+
return fmt.Errorf("unable to create target file path %q: %w", targetPath, err)
61+
}
62+
defer targetFile.Close()
63+
exportOpts.Output = targetFile
64+
} else {
65+
exportOpts.Output = os.Stdout
66+
}
67+
68+
if err := containerEngine.VolumeExport(ctx, args[0], exportOpts); err != nil {
5469
return err
5570
}
5671
return nil

cmd/podman/volumes/import.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package volumes
22

33
import (
44
"context"
5+
"fmt"
6+
"os"
57

68
"github.com/containers/podman/v5/cmd/podman/common"
9+
"github.com/containers/podman/v5/cmd/podman/parse"
710
"github.com/containers/podman/v5/cmd/podman/registry"
811
"github.com/containers/podman/v5/pkg/domain/entities"
912
"github.com/spf13/cobra"
@@ -31,18 +34,27 @@ func init() {
3134
}
3235

3336
func importVol(cmd *cobra.Command, args []string) error {
34-
filePath := args[1]
35-
if filePath == "-" {
36-
filePath = "/dev/stdin"
37+
opts := entities.VolumeImportOptions{}
38+
39+
filepath := args[1]
40+
if filepath == "-" {
41+
opts.Input = os.Stdin
42+
} else {
43+
if err := parse.ValidateFileName(filepath); err != nil {
44+
return err
45+
}
46+
47+
targetFile, err := os.Open(filepath)
48+
if err != nil {
49+
return fmt.Errorf("unable to create target file path %q: %w", filepath, err)
50+
}
51+
defer targetFile.Close()
52+
opts.Input = targetFile
3753
}
3854

3955
containerEngine := registry.ContainerEngine()
4056
ctx := context.Background()
4157

42-
opts := entities.VolumeImportOptions{
43-
InputPath: filePath,
44-
}
45-
4658
if err := containerEngine.VolumeImport(ctx, args[0], opts); err != nil {
4759
return err
4860
}

libpod/volume.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/containers/podman/v5/libpod/lock"
1212
"github.com/containers/podman/v5/libpod/plugin"
1313
"github.com/containers/podman/v5/utils"
14+
"github.com/containers/storage/pkg/archive"
1415
"github.com/containers/storage/pkg/directory"
1516
"github.com/sirupsen/logrus"
1617
)
@@ -344,8 +345,8 @@ func (v *Volume) Import(r io.Reader) error {
344345
}
345346
}()
346347

347-
if err := utils.UntarToFileSystem(mountPoint, r, nil); err != nil {
348-
return err
348+
if err := archive.Untar(r, mountPoint, nil); err != nil {
349+
return fmt.Errorf("extracting into volume %s: %w", v.Name(), err)
349350
}
350351

351352
return nil

pkg/api/handlers/libpod/volumes.go

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ package libpod
44

55
import (
66
"encoding/json"
7+
"errors"
78
"fmt"
8-
"io"
99
"net/http"
1010
"net/url"
11-
"os"
12-
13-
"errors"
1411

1512
"github.com/containers/podman/v5/libpod"
1613
"github.com/containers/podman/v5/libpod/define"
@@ -248,22 +245,13 @@ func ImportVolume(w http.ResponseWriter, r *http.Request) {
248245
return
249246
}
250247

251-
tmpfile, err := os.CreateTemp("", "podman-volume-import-")
252-
if err != nil {
253-
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("creating temporary file: %w", err))
254-
return
255-
}
256-
defer func() {
257-
tmpfile.Close()
258-
os.Remove(tmpfile.Name())
259-
}()
260-
261-
if _, err := io.Copy(tmpfile, r.Body); err != nil {
262-
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("copying archive to temporary file: %w", err))
248+
if r.Body == nil {
249+
utils.Error(w, http.StatusInternalServerError, errors.New("must provide tar file to import in request body"))
263250
return
264251
}
252+
defer r.Body.Close()
265253

266-
if err := vol.Import(tmpfile); err != nil {
254+
if err := vol.Import(r.Body); err != nil {
267255
utils.Error(w, http.StatusInternalServerError, err)
268256
return
269257
}

pkg/api/server/register_volumes.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,19 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
161161
// description: the name or ID of the volume
162162
// produces:
163163
// - application/x-tar
164-
// responses:
165-
// 200:
164+
// responses:
165+
// 200:
166166
// description: no error
167167
// schema:
168168
// type: string
169-
// format: binary
169+
// format: binary
170170
// 404:
171171
// $ref: "#/responses/volumeNotFound"
172172
// 500:
173173
// $ref: "#/responses/internalError"
174174
r.Handle(VersionedPath("/libpod/volumes/{name}/export"), s.APIHandler(libpod.ExportVolume)).Methods(http.MethodGet)
175175

176-
// swagger:operation PUT /libpod/volumes/{name}/import libpod VolumeImportLibpod
176+
// swagger:operation POST /libpod/volumes/{name}/import libpod VolumeImportLibpod
177177
// ---
178178
// tags:
179179
// - volumes
@@ -200,7 +200,7 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
200200
// $ref: "#/responses/volumeNotFound"
201201
// 500:
202202
// $ref: "#/responses/internalError"
203-
r.Handle(VersionedPath("/libpod/volumes/{name}/import"), s.APIHandler(libpod.ImportVolume)).Methods(http.MethodPut)
203+
r.Handle(VersionedPath("/libpod/volumes/{name}/import"), s.APIHandler(libpod.ImportVolume)).Methods(http.MethodPost)
204204

205205
/*
206206
* Docker compatibility endpoints

pkg/bindings/volumes/volumes.go

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ package volumes
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"io"
87
"net/http"
9-
"os"
108
"strings"
119

1210
"github.com/containers/podman/v5/pkg/bindings"
13-
"github.com/containers/podman/v5/pkg/domain/entities"
1411
"github.com/containers/podman/v5/pkg/domain/entities/reports"
1512
entitiesTypes "github.com/containers/podman/v5/pkg/domain/entities/types"
1613
jsoniter "github.com/json-iterator/go"
@@ -146,17 +143,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool,
146143
}
147144

148145
// Export exports a volume to the given path
149-
func Export(ctx context.Context, nameOrID string, options entities.VolumeExportOptions) error {
150-
if options.OutputPath == "" {
151-
return errors.New("must provide valid path for file to write to")
152-
}
153-
154-
targetFile, err := os.Create(options.OutputPath)
155-
if err != nil {
156-
return fmt.Errorf("unable to create target file path %q: %w", options.OutputPath, err)
157-
}
158-
defer targetFile.Close()
159-
146+
func Export(ctx context.Context, nameOrID string, exportTo io.Writer) error {
160147
conn, err := bindings.GetClient(ctx)
161148
if err != nil {
162149
return err
@@ -168,31 +155,21 @@ func Export(ctx context.Context, nameOrID string, options entities.VolumeExportO
168155
defer response.Body.Close()
169156

170157
if response.IsSuccess() || response.IsRedirection() {
171-
if _, err := io.Copy(targetFile, response.Body); err != nil {
158+
if _, err := io.Copy(exportTo, response.Body); err != nil {
172159
return fmt.Errorf("writing volume %s contents to file: %w", nameOrID, err)
173160
}
174161
}
175162
return response.Process(nil)
176163
}
177164

178165
// Import imports the given tar into the given volume
179-
func Import(ctx context.Context, nameOrID string, options entities.VolumeImportOptions) error {
180-
if options.InputPath == "" {
181-
return errors.New("must provide valid path for file to write to")
182-
}
183-
184-
targetFile, err := os.Create(options.InputPath)
185-
if err != nil {
186-
return fmt.Errorf("unable to create target file path %q: %w", options.InputPath, err)
187-
}
188-
defer targetFile.Close()
189-
166+
func Import(ctx context.Context, nameOrID string, importFrom io.Reader) error {
190167
conn, err := bindings.GetClient(ctx)
191168
if err != nil {
192169
return err
193170
}
194171

195-
response, err := conn.DoRequest(ctx, targetFile, http.MethodPut, "/volumes/%s/import", nil, nil, nameOrID)
172+
response, err := conn.DoRequest(ctx, importFrom, http.MethodPost, "/volumes/%s/import", nil, nil, nameOrID)
196173
if err != nil {
197174
return err
198175
}

pkg/domain/entities/volumes.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package entities
22

33
import (
4+
"io"
45
"net/url"
56

67
"github.com/containers/podman/v5/pkg/domain/entities/types"
@@ -45,10 +46,11 @@ type VolumeUnmountReport = types.VolumeUnmountReport
4546

4647
// VolumeExportOptions describes the options required to export a volume.
4748
type VolumeExportOptions struct {
48-
OutputPath string
49+
Output io.Writer
4950
}
5051

5152
// VolumeImportOptions describes the options required to import a volume
5253
type VolumeImportOptions struct {
53-
InputPath string
54+
// Input will be closed upon being fully consumed
55+
Input io.Reader
5456
}

pkg/domain/infra/abi/play.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import (
3838
"github.com/containers/podman/v5/pkg/specgenutil"
3939
"github.com/containers/podman/v5/pkg/systemd/notifyproxy"
4040
"github.com/containers/podman/v5/pkg/util"
41-
"github.com/containers/podman/v5/utils"
41+
"github.com/containers/storage/pkg/archive"
4242
"github.com/containers/storage/pkg/fileutils"
4343
"github.com/coreos/go-systemd/v22/daemon"
4444
"github.com/opencontainers/go-digest"
@@ -1504,7 +1504,7 @@ func (ic *ContainerEngine) importVolume(ctx context.Context, vol *libpod.Volume,
15041504
}
15051505

15061506
// dont care if volume is mounted or not we are gonna import everything to mountPoint
1507-
return utils.UntarToFileSystem(mountPoint, tarFile, nil)
1507+
return archive.Untar(tarFile, mountPoint, nil)
15081508
}
15091509

15101510
// readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag

pkg/domain/infra/abi/volumes.go

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"errors"
88
"fmt"
99
"io"
10-
"os"
1110

1211
"github.com/containers/podman/v5/libpod"
1312
"github.com/containers/podman/v5/libpod/define"
@@ -243,16 +242,6 @@ func (ic *ContainerEngine) VolumeReload(ctx context.Context) (*entities.VolumeRe
243242
}
244243

245244
func (ic *ContainerEngine) VolumeExport(ctx context.Context, nameOrID string, options entities.VolumeExportOptions) error {
246-
if options.OutputPath == "" {
247-
return errors.New("must provide valid path for file to write to")
248-
}
249-
250-
targetFile, err := os.Create(options.OutputPath)
251-
if err != nil {
252-
return fmt.Errorf("unable to create target file path %q: %w", options.OutputPath, err)
253-
}
254-
defer targetFile.Close()
255-
256245
vol, err := ic.Libpod.GetVolume(nameOrID)
257246
if err != nil {
258247
return err
@@ -264,30 +253,20 @@ func (ic *ContainerEngine) VolumeExport(ctx context.Context, nameOrID string, op
264253
}
265254
defer contents.Close()
266255

267-
if _, err := io.Copy(targetFile, contents); err != nil {
268-
return fmt.Errorf("writing volume %s to file: %w", vol.Name(), err)
256+
if _, err := io.Copy(options.Output, contents); err != nil {
257+
return fmt.Errorf("writing volume %s contents: %w", vol.Name(), err)
269258
}
270259

271260
return nil
272261
}
273262

274263
func (ic *ContainerEngine) VolumeImport(ctx context.Context, nameOrID string, options entities.VolumeImportOptions) error {
275-
if options.InputPath == "" {
276-
return errors.New("must provide valid path to import from")
277-
}
278-
279-
inputFile, err := os.Open(options.InputPath)
280-
if err != nil {
281-
return fmt.Errorf("opening import file %q: %w", options.InputPath, err)
282-
}
283-
defer inputFile.Close()
284-
285264
vol, err := ic.Libpod.GetVolume(nameOrID)
286265
if err != nil {
287266
return err
288267
}
289268

290-
if err := vol.Import(inputFile); err != nil {
269+
if err := vol.Import(options.Input); err != nil {
291270
return err
292271
}
293272

pkg/domain/infra/tunnel/volumes.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ func (ic *ContainerEngine) VolumeReload(ctx context.Context) (*entities.VolumeRe
115115
}
116116

117117
func (ic *ContainerEngine) VolumeExport(ctx context.Context, nameOrID string, options entities.VolumeExportOptions) error {
118-
return volumes.Export(ic.ClientCtx, nameOrID, options)
118+
return volumes.Export(ic.ClientCtx, nameOrID, options.Output)
119119
}
120120

121121
func (ic *ContainerEngine) VolumeImport(ctx context.Context, nameOrID string, options entities.VolumeImportOptions) error {
122-
return volumes.Import(ic.ClientCtx, nameOrID, options)
122+
return volumes.Import(ic.ClientCtx, nameOrID, options.Input)
123123
}

0 commit comments

Comments
 (0)