Skip to content

Commit c5825df

Browse files
committed
Extend sealer save to support multi-image-archive and tmp-dir
feature: Extend the save flag option and function Signed-off-by: Xinwei Xiong(cubxxw) <[email protected]>
1 parent 035b662 commit c5825df

File tree

7 files changed

+99
-54
lines changed

7 files changed

+99
-54
lines changed

cmd/sealer/cmd/image/load.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ import (
2424
"github.com/sealerio/sealer/pkg/imageengine"
2525
)
2626

27-
var loadOpts *options.LoadOptions
27+
var (
28+
loadOpts *options.LoadOptions
2829

29-
var longNewLoadCmdDescription = `Load a sealer image from a tar archive`
30+
longNewLoadCmdDescription = `
31+
Load a sealer image from a tar archive
32+
Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.`
3033

31-
var exampleForLoadCmd = `
34+
exampleForLoadCmd = `
3235
sealer load -i kubernetes.tar
33-
`
36+
sealer save abc:v1 -o my.tar --tmp-dir /root/my-tmp`
37+
)
3438

3539
// NewLoadCmd loadCmd represents the load command
3640
func NewLoadCmd() *cobra.Command {
@@ -58,7 +62,7 @@ func NewLoadCmd() *cobra.Command {
5862
flags := loadCmd.Flags()
5963
flags.StringVarP(&loadOpts.Input, "input", "i", "", "Load image from file")
6064
flags.BoolVarP(&loadOpts.Quiet, "quiet", "q", false, "Suppress the output")
61-
flags.StringVar(&loadOpts.TmpDir, "tmp-dir", "", "set temporary directory when load image. if not set, use system`s temporary directory")
65+
flags.StringVar(&loadOpts.TmpDir, "tmp-dir", "", "Set temporary directory when load image. if not set, use system temporary directory(`/var/tmp/`)")
6266
if err := loadCmd.MarkFlagRequired("input"); err != nil {
6367
logrus.Errorf("failed to init flag: %v", err)
6468
os.Exit(1)

cmd/sealer/cmd/image/save.go

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
package image
1616

1717
import (
18-
"os"
19-
2018
"github.com/sirupsen/logrus"
2119
"github.com/spf13/cobra"
2220

@@ -25,14 +23,24 @@ import (
2523
"github.com/sealerio/sealer/pkg/imageengine/buildah"
2624
)
2725

28-
var saveOpts *options.SaveOptions
26+
var (
27+
saveOpts *options.SaveOptions
28+
29+
longNewSaveCmdDescription = `
30+
sealer save -o [output file name] [image name]
31+
Save an image to docker-archive or oci-archive on the local machine. Default is docker-archive.`
32+
33+
exampleForSaveCmd = `
34+
sealer save docker.io/sealerio/kubernetes:v1-22-15-sealerio-2
2935
30-
var longNewSaveCmdDescription = `sealer save -o [output file name] [image name]`
36+
Image to kubernetes.tar file, and specify the temporary load directory:
3137
32-
var exampleForSaveCmd = `
33-
save docker.io/sealerio/kubernetes:v1-22-15-sealerio-2 image to kubernetes.tar file:
38+
sealer save docker.io/sealerio/kubernetes:v1.22.15 -o kubernetes.tar --tmp-dir /root/tmp`
39+
)
3440

35-
sealer save -o kubernetes.tar docker.io/sealerio/kubernetes:v1-22-15-sealerio-2`
41+
var (
42+
containerConfig = buildah.NewPodmanConfig()
43+
)
3644

3745
// NewSaveCmd saveCmd represents the save command
3846
func NewSaveCmd() *cobra.Command {
@@ -59,14 +67,26 @@ func NewSaveCmd() *cobra.Command {
5967
}
6068
saveOpts = &options.SaveOptions{}
6169
flags := saveCmd.Flags()
62-
flags.StringVar(&saveOpts.Format, "format", buildah.OCIArchive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
63-
flags.StringVarP(&saveOpts.Output, "output", "o", "", "Write image to a specified file")
70+
71+
formatFlagName := "format"
72+
flags.StringVar(&saveOpts.Format, formatFlagName, buildah.OCIArchive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
73+
74+
outputFlagName := "output"
75+
flags.StringVarP(&saveOpts.Output, outputFlagName, "o", "", "Write to a specified file (default: stdout, which must be redirected)")
76+
77+
// TODO: Waiting for implementation, not yet supported
78+
flags.StringVar(&loadOpts.TmpDir, "tmp-dir", "", "Set temporary directory when load image. use system temporary directory(/var/tmp/) if not present.")
79+
6480
flags.BoolVarP(&saveOpts.Quiet, "quiet", "q", false, "Suppress the output")
65-
flags.StringVar(&saveOpts.TmpDir, "tmp-dir", "", "set temporary directory when save image. if not set, use system`s temporary directory")
66-
flags.BoolVar(&saveOpts.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
81+
82+
compressFlagName := "compress"
83+
flags.BoolVar(&saveOpts.Compress, compressFlagName, false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
84+
85+
MultiImageArchiveFlagName := "multi-image-archive"
86+
flags.BoolVarP(&saveOpts.MultiImageArchive, MultiImageArchiveFlagName, "m", containerConfig.ContainersConfDefaultsRO.Engine.MultiImageArchive, "Interpret additional arguments as images not tags and create a multi-image-archive (only for docker-archive)")
87+
6788
if err := saveCmd.MarkFlagRequired("output"); err != nil {
68-
logrus.Errorf("failed to init flag: %v", err)
69-
os.Exit(1)
89+
logrus.WithError(err).Fatal("failed to mark flag as required")
7090
}
7191

7292
return saveCmd

pkg/define/options/options.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,16 @@ type ImagesOptions struct {
120120
JSON bool
121121
}
122122

123+
// SaveOptions provide options for saving images.
123124
type SaveOptions struct {
124-
Compress bool
125-
Format string
126-
// don't support currently
127-
MultiImageArchive bool
128-
Output string
129-
Quiet bool
130-
ImageNameOrID string
131-
TmpDir string
125+
Compress bool // Whether or not to compress the image layers when saving to a directory. Default is false.
126+
Format string // The format to save the image in. Possible values are oci-archive, oci-dir, docker-archive, and docker-dir. Default is oci-archive.
127+
MultiImageArchive bool // Whether or not to save multiple images into a single archive file. Default is false.
128+
Output string // The file or directory to save the image to. If not set, output will go to stdout.
129+
Quiet bool // Whether or not to suppress output when saving the image. Default is false.
130+
ImageNameOrID string // The name or ID of the image to save.
131+
// // TODO: unrealized, Set temporary directory when save image. if not set, use system temporary directory(`/var/tmp/`)
132+
TmpDir string
132133
}
133134

134135
type LoadOptions struct {

pkg/image/save/save.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,22 @@ func (is *DefaultImageSaver) SaveImages(images []string, dir string, platform v1
7171
}
7272
}()
7373

74-
//handle image name
74+
// handle image name
7575
for _, image := range images {
7676
named, err := ParseNormalizedNamed(image, "")
7777
if err != nil {
7878
return fmt.Errorf("failed to parse image name:: %v", err)
7979
}
8080

81-
//check if image exist
81+
// check if image exist
8282
if err := is.isImageExist(named, dir, platform); err == nil {
8383
continue
8484
}
8585
is.domainToImages[named.domain+named.repo] = append(is.domainToImages[named.domain+named.repo], named)
8686
progress.Message(is.progressOut, "", fmt.Sprintf("Pulling image: %s", named.FullName()))
8787
}
8888

89-
//perform image save ability
89+
// perform image save ability
9090
eg, _ := errgroup.WithContext(context.Background())
9191
numCh := make(chan struct{}, maxPullGoroutineNum)
9292
for _, nameds := range is.domainToImages {

pkg/imageengine/buildah/from.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ type fromFlagsWrapper struct {
3838
*buildahcli.NameSpaceResults
3939
}
4040

41+
type BuildahConfig struct {
42+
ContainersConfDefaultsRO config.Config
43+
}
44+
45+
func NewPodmanConfig() *BuildahConfig {
46+
var buildOptions BuildahConfig
47+
return &buildOptions
48+
}
49+
4150
// createContainerFromImage create a working container. This function is copied from
4251
// "buildah from". This function takes args([]string{"$image"}), and create a working container
4352
// based on $image, this will generate an empty dictionary, not a real rootfs. And this container is a fake container.

pkg/imageengine/buildah/load.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,8 @@ func (engine *Engine) Load(opts *options.LoadOptions) error {
6262
}
6363

6464
defer func() {
65-
err = os.RemoveAll(tempDir)
66-
if err != nil {
67-
logrus.Errorf("failed to delete %s: %v", tempDir, err)
65+
if err = os.RemoveAll(tempDir); err != nil {
66+
logrus.Warnf("failed to delete %s: %v", tempDir, err)
6867
}
6968
}()
7069

pkg/imageengine/buildah/save.go

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"path/filepath"
2323

2424
"github.com/containers/common/libimage"
25+
"github.com/containers/common/pkg/auth"
2526
"github.com/pkg/errors"
2627
"github.com/sirupsen/logrus"
2728

@@ -34,42 +35,54 @@ import (
3435

3536
// Save image as tar file, if image is multi-arch image, will save all its instances and manifest name as tar file.
3637
func (engine *Engine) Save(opts *options.SaveOptions) error {
37-
imageNameOrID := opts.ImageNameOrID
38-
imageTar := opts.Output
38+
var (
39+
imageNameOrID = opts.ImageNameOrID
40+
output = opts.Output
41+
format = opts.Format
42+
tmpDir = opts.TmpDir
43+
compress = opts.Compress
44+
)
45+
46+
systemCxt := engine.SystemContext()
47+
if err := auth.CheckAuthFile(systemCxt.AuthFilePath); err != nil {
48+
return err
49+
}
50+
51+
systemCxt.BigFilesTemporaryDir = tmpDir
3952

4053
if len(imageNameOrID) == 0 {
41-
return errors.New("image name or id must be specified")
54+
return errors.New("failed to save image, image name or id is empty")
4255
}
43-
if opts.Compress && (opts.Format != OCIManifestDir && opts.Format != V2s2ManifestDir) {
56+
57+
if compress && (format != OCIManifestDir && format != V2s2ManifestDir) {
4458
return errors.New("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
4559
}
4660

47-
img, _, err := engine.ImageRuntime().LookupImage(imageNameOrID, &libimage.LookupImageOptions{
48-
ManifestList: true,
49-
})
61+
img, _, err := engine.ImageRuntime().LookupImage(imageNameOrID,
62+
&libimage.LookupImageOptions{
63+
ManifestList: true,
64+
})
5065
if err != nil {
5166
return err
5267
}
5368

54-
isManifest, err := img.IsManifestList(getContext())
55-
if err != nil {
69+
// checks if the image is a manifest list or an image index, and saves the image if it is not
70+
if isManifest, err := img.IsManifestList(getContext()); err != nil {
5671
return err
57-
}
58-
59-
if !isManifest {
60-
return engine.saveOneImage(imageNameOrID, opts.Format, imageTar, opts.Compress)
72+
} else if !isManifest {
73+
return engine.saveOneImage(imageNameOrID, format, output, compress)
6174
}
6275

6376
// save multi-arch images :including each platform images and manifest.
64-
var pathsToCompress []string
77+
pathsToCompress := []string{}
6578

66-
if err := fs.FS.MkdirAll(filepath.Dir(imageTar)); err != nil {
67-
return fmt.Errorf("failed to create %s, err: %v", imageTar, err)
79+
if err := fs.FS.MkdirAll(filepath.Dir(output)); err != nil {
80+
return fmt.Errorf("failed to create %s, err: %v", output, err)
6881
}
6982

70-
file, err := os.Create(filepath.Clean(imageTar))
83+
file, err := os.Create(filepath.Clean(output))
7184
if err != nil {
72-
return fmt.Errorf("failed to create %s, err: %v", imageTar, err)
85+
return fmt.Errorf("failed to create %s, err: %v", output, err)
7386
}
7487

7588
defer func() {
@@ -78,14 +91,13 @@ func (engine *Engine) Save(opts *options.SaveOptions) error {
7891
}
7992
}()
8093

81-
tempDir, err := os.MkdirTemp(opts.TmpDir, "sealer-save-tmp")
94+
tempDir, err := os.MkdirTemp(tmpDir, "sealer-save-tmp")
8295
if err != nil {
8396
return fmt.Errorf("failed to create %s, err: %v", tempDir, err)
8497
}
8598

8699
defer func() {
87-
err = os.RemoveAll(tempDir)
88-
if err != nil {
100+
if err = os.RemoveAll(tempDir); err != nil {
89101
logrus.Warnf("failed to delete %s: %v", tempDir, err)
90102
}
91103
}()
@@ -110,7 +122,7 @@ func (engine *Engine) Save(opts *options.SaveOptions) error {
110122
}
111123

112124
instanceTar := filepath.Join(tempDir, instance.ID()+".tar")
113-
err = engine.saveOneImage(instance.ID(), opts.Format, instanceTar, opts.Compress)
125+
err = engine.saveOneImage(instance.ID(), format, instanceTar, compress)
114126
if err != nil {
115127
return err
116128
}

0 commit comments

Comments
 (0)