/
image_save.go
114 lines (93 loc) · 2.67 KB
/
image_save.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package cmd
import (
"fmt"
"io"
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/vbauerster/mpb/v7"
"golang.org/x/term"
"github.com/LINBIT/virter/pkg/pullpolicy"
)
func imageSaveCommand() *cobra.Command {
saveCmd := &cobra.Command{
Use: "save name [file]",
Short: "Save an image",
Long: `Saves an image as a standalone qcow2 image. If the image uses multiple layers, all layers will be
squashed into a single file.`,
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
v, err := InitVirter()
if err != nil {
log.Fatal(err)
}
defer v.ForceDisconnect()
image := args[0]
var out io.Writer
if len(args) == 1 {
out = os.Stdout
if term.IsTerminal(int(os.Stdout.Fd())) {
log.Fatal("refusing to write image to terminal, redirect or specify a filename")
}
} else {
f, err := os.Create(args[1])
if err != nil {
log.WithError(err).Fatal("failed to create output file")
}
defer func() {
_ = f.Close()
if ctx.Err() != nil {
_ = os.Remove(args[1])
}
}()
out = f
}
p := mpb.NewWithContext(ctx, DefaultContainerOpt())
imgRef, err := GetLocalImage(ctx, image, image, v, pullpolicy.Never, DefaultProgressFormat(p))
if err != nil {
log.WithError(err).Fatalf("error searching image %s", image)
}
if imgRef == nil {
log.Fatalf("could not find a local image %s", image)
}
top := imgRef.TopLayer()
squashed, err := top.Squashed()
if err != nil {
log.WithError(err).Fatal("could not squash image")
}
defer func() {
err := squashed.Delete()
if err != nil {
log.WithError(err).Warn("failed to delete squashed volume")
}
}()
desc, err := squashed.Descriptor()
if err != nil {
log.WithError(err).Fatal("could not get description of squashed volume")
}
bar := DefaultProgressFormat(p).NewBar(imgRef.Name(), "save", int64(desc.Physical.Value))
reader, err := squashed.Uncompressed()
if err != nil {
log.WithError(err).Fatal("could not get reader from volume")
}
reader = bar.ProxyReader(reader)
defer reader.Close()
_, err = io.Copy(out, reader)
if err != nil {
log.WithError(err).Fatal("failed to copy volume content to output")
}
p.Wait()
_, _ = fmt.Fprintf(os.Stderr, "Saved %s\n", imgRef.Name())
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// Only suggest first argument
return suggestImageNames(cmd, args, toComplete)
}
// Allow files here
return nil, cobra.ShellCompDirectiveDefault
},
}
return saveCmd
}