Skip to content

Commit 527a3d9

Browse files
authored
Merge pull request #12265 from prezha/fix-image-save-cmd
fix image save cmd
2 parents 2bb50e4 + f1ba75f commit 527a3d9

File tree

19 files changed

+684
-20
lines changed

19 files changed

+684
-20
lines changed

cmd/minikube/cmd/image.go

+74
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,77 @@ var loadImageCmd = &cobra.Command{
144144
},
145145
}
146146

147+
func readFile(w io.Writer, tmp string) error {
148+
r, err := os.Open(tmp)
149+
if err != nil {
150+
return err
151+
}
152+
_, err = io.Copy(w, r)
153+
if err != nil {
154+
return err
155+
}
156+
err = r.Close()
157+
if err != nil {
158+
return err
159+
}
160+
return nil
161+
}
162+
163+
// saveImageCmd represents the image load command
164+
var saveImageCmd = &cobra.Command{
165+
Use: "save IMAGE [ARCHIVE | -]",
166+
Short: "Save a image from minikube",
167+
Long: "Save a image from minikube",
168+
Example: "minikube image save image\nminikube image save image image.tar",
169+
Run: func(cmd *cobra.Command, args []string) {
170+
if len(args) == 0 {
171+
exit.Message(reason.Usage, "Please provide an image in the container runtime to save from minikube via <minikube image save IMAGE_NAME>")
172+
}
173+
// Save images from container runtime
174+
profile, err := config.LoadProfile(viper.GetString(config.ProfileName))
175+
if err != nil {
176+
exit.Error(reason.Usage, "loading profile", err)
177+
}
178+
179+
if len(args) > 1 {
180+
output = args[1]
181+
182+
if args[1] == "-" {
183+
tmp, err := ioutil.TempFile("", "image.*.tar")
184+
if err != nil {
185+
exit.Error(reason.GuestImageSave, "Failed to get temp", err)
186+
}
187+
tmp.Close()
188+
output = tmp.Name()
189+
}
190+
191+
if err := machine.DoSaveImages([]string{args[0]}, output, []*config.Profile{profile}, ""); err != nil {
192+
exit.Error(reason.GuestImageSave, "Failed to save image", err)
193+
}
194+
195+
if args[1] == "-" {
196+
err := readFile(os.Stdout, output)
197+
if err != nil {
198+
exit.Error(reason.GuestImageSave, "Failed to read temp", err)
199+
}
200+
os.Remove(output)
201+
}
202+
} else {
203+
if err := machine.SaveAndCacheImages([]string{args[0]}, []*config.Profile{profile}); err != nil {
204+
exit.Error(reason.GuestImageSave, "Failed to save image", err)
205+
}
206+
if imgDaemon || imgRemote {
207+
image.UseDaemon(imgDaemon)
208+
image.UseRemote(imgRemote)
209+
err := image.UploadCachedImage(args[0])
210+
if err != nil {
211+
exit.Error(reason.GuestImageSave, "Failed to save image", err)
212+
}
213+
}
214+
}
215+
},
216+
}
217+
147218
var removeImageCmd = &cobra.Command{
148219
Use: "rm IMAGE [IMAGE...]",
149220
Short: "Remove one or more images",
@@ -317,6 +388,9 @@ func init() {
317388
buildImageCmd.Flags().StringArrayVar(&buildEnv, "build-env", nil, "Environment variables to pass to the build. (format: key=value)")
318389
buildImageCmd.Flags().StringArrayVar(&buildOpt, "build-opt", nil, "Specify arbitrary flags to pass to the build. (format: key=value)")
319390
imageCmd.AddCommand(buildImageCmd)
391+
saveImageCmd.Flags().BoolVar(&imgDaemon, "daemon", false, "Cache image to docker daemon")
392+
saveImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image to remote registry")
393+
imageCmd.AddCommand(saveImageCmd)
320394
imageCmd.AddCommand(listImageCmd)
321395
imageCmd.AddCommand(tagImageCmd)
322396
imageCmd.AddCommand(pushImageCmd)

pkg/minikube/assets/vm_assets.go

+59
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"io"
2525
"os"
2626
"path"
27+
"strconv"
2728
"time"
2829

2930
"github.com/pkg/errors"
@@ -37,8 +38,11 @@ const MemorySource = "memory"
3738
// CopyableFile is something that can be copied
3839
type CopyableFile interface {
3940
io.Reader
41+
io.Writer
4042
GetLength() int
43+
SetLength(int)
4144
GetSourcePath() string
45+
GetTargetPath() string
4246

4347
GetTargetDir() string
4448
GetTargetName() string
@@ -62,6 +66,11 @@ func (b *BaseAsset) GetSourcePath() string {
6266
return b.SourcePath
6367
}
6468

69+
// GetTargetPath returns target path
70+
func (b *BaseAsset) GetTargetPath() string {
71+
return path.Join(b.GetTargetDir(), b.GetTargetName())
72+
}
73+
6574
// GetTargetDir returns target dir
6675
func (b *BaseAsset) GetTargetDir() string {
6776
return b.TargetDir
@@ -86,6 +95,7 @@ func (b *BaseAsset) GetModTime() (time.Time, error) {
8695
type FileAsset struct {
8796
BaseAsset
8897
reader io.ReadSeeker
98+
writer io.Writer
8999
file *os.File // Optional pointer to close file through FileAsset.Close()
90100
}
91101

@@ -134,6 +144,14 @@ func (f *FileAsset) GetLength() (flen int) {
134144
return int(fi.Size())
135145
}
136146

147+
// SetLength sets the file length
148+
func (f *FileAsset) SetLength(flen int) {
149+
err := os.Truncate(f.SourcePath, int64(flen))
150+
if err != nil {
151+
klog.Errorf("truncate(%q) failed: %v", f.SourcePath, err)
152+
}
153+
}
154+
137155
// GetModTime returns modification time of the file
138156
func (f *FileAsset) GetModTime() (time.Time, error) {
139157
fi, err := os.Stat(f.SourcePath)
@@ -152,6 +170,23 @@ func (f *FileAsset) Read(p []byte) (int, error) {
152170
return f.reader.Read(p)
153171
}
154172

173+
// Write writes the asset
174+
func (f *FileAsset) Write(p []byte) (int, error) {
175+
if f.writer == nil {
176+
f.file.Close()
177+
perms, err := strconv.ParseUint(f.Permissions, 8, 32)
178+
if err != nil || perms > 07777 {
179+
return 0, err
180+
}
181+
f.file, err = os.OpenFile(f.SourcePath, os.O_RDWR|os.O_CREATE, os.FileMode(perms))
182+
if err != nil {
183+
return 0, err
184+
}
185+
f.writer = io.Writer(f.file)
186+
}
187+
return f.writer.Write(p)
188+
}
189+
155190
// Seek resets the reader to offset
156191
func (f *FileAsset) Seek(offset int64, whence int) (int64, error) {
157192
return f.reader.Seek(offset, whence)
@@ -177,11 +212,23 @@ func (m *MemoryAsset) GetLength() int {
177212
return m.length
178213
}
179214

215+
// SetLength returns length
216+
func (m *MemoryAsset) SetLength(len int) {
217+
m.length = len
218+
}
219+
180220
// Read reads the asset
181221
func (m *MemoryAsset) Read(p []byte) (int, error) {
182222
return m.reader.Read(p)
183223
}
184224

225+
// Writer writes the asset
226+
func (m *MemoryAsset) Write(p []byte) (int, error) {
227+
m.length = len(p)
228+
m.reader = bytes.NewReader(p)
229+
return len(p), nil
230+
}
231+
185232
// Seek resets the reader to offset
186233
func (m *MemoryAsset) Seek(offset int64, whence int) (int64, error) {
187234
return m.reader.Seek(offset, whence)
@@ -298,6 +345,11 @@ func (m *BinAsset) GetLength() int {
298345
return m.length
299346
}
300347

348+
// SetLength sets length
349+
func (m *BinAsset) SetLength(len int) {
350+
m.length = len
351+
}
352+
301353
// Read reads the asset
302354
func (m *BinAsset) Read(p []byte) (int, error) {
303355
if m.GetLength() == 0 {
@@ -306,6 +358,13 @@ func (m *BinAsset) Read(p []byte) (int, error) {
306358
return m.reader.Read(p)
307359
}
308360

361+
// Write writes the asset
362+
func (m *BinAsset) Write(p []byte) (int, error) {
363+
m.length = len(p)
364+
m.reader = bytes.NewReader(p)
365+
return len(p), nil
366+
}
367+
309368
// Seek resets the reader to offset
310369
func (m *BinAsset) Seek(offset int64, whence int) (int64, error) {
311370
return m.reader.Seek(offset, whence)

pkg/minikube/command/command_runner.go

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ type Runner interface {
7575
// Copy is a convenience method that runs a command to copy a file
7676
Copy(assets.CopyableFile) error
7777

78+
// CopyFrom is a convenience method that runs a command to copy a file back
79+
CopyFrom(assets.CopyableFile) error
80+
7881
// Remove is a convenience method that runs a command to remove a file
7982
Remove(assets.CopyableFile) error
8083
}

pkg/minikube/command/exec_runner.go

+18
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,24 @@ func (e *execRunner) Copy(f assets.CopyableFile) error {
184184
return writeFile(dst, f, os.FileMode(perms))
185185
}
186186

187+
// CopyFrom copies a file
188+
func (e *execRunner) CopyFrom(f assets.CopyableFile) error {
189+
src := path.Join(f.GetTargetDir(), f.GetTargetName())
190+
191+
dst := f.GetSourcePath()
192+
klog.Infof("cp: %s --> %s (%d bytes)", src, dst, f.GetLength())
193+
if f.GetLength() == 0 {
194+
klog.Warningf("0 byte asset: %+v", f)
195+
}
196+
197+
perms, err := strconv.ParseInt(f.GetPermissions(), 8, 0)
198+
if err != nil || perms > 07777 {
199+
return errors.Wrapf(err, "error converting permissions %s to integer", f.GetPermissions())
200+
}
201+
202+
return writeFile(dst, f, os.FileMode(perms))
203+
}
204+
187205
// Remove removes a file
188206
func (e *execRunner) Remove(f assets.CopyableFile) error {
189207
dst := filepath.Join(f.GetTargetDir(), f.GetTargetName())

pkg/minikube/command/fake_runner.go

+13
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,19 @@ func (f *FakeCommandRunner) Copy(file assets.CopyableFile) error {
142142
return nil
143143
}
144144

145+
func (f *FakeCommandRunner) CopyFrom(file assets.CopyableFile) error {
146+
v, ok := f.fileMap.Load(file.GetSourcePath())
147+
if !ok {
148+
return fmt.Errorf("not found in map")
149+
}
150+
b := v.(bytes.Buffer)
151+
_, err := io.Copy(file, &b)
152+
if err != nil {
153+
return errors.Wrapf(err, "error writing file: %+v", file)
154+
}
155+
return nil
156+
}
157+
145158
// Remove removes the filename, file contents key value pair from the stored map
146159
func (f *FakeCommandRunner) Remove(file assets.CopyableFile) error {
147160
f.fileMap.Delete(file.GetSourcePath())

pkg/minikube/command/kic_runner.go

+17
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ func (k *kicRunner) Copy(f assets.CopyableFile) error {
204204
return k.copy(tf.Name(), dst)
205205
}
206206

207+
// CopyFrom copies a file
208+
func (k *kicRunner) CopyFrom(f assets.CopyableFile) error {
209+
src := f.GetTargetPath()
210+
dst := f.GetSourcePath()
211+
212+
klog.Infof("%s (direct): %s --> %s", k.ociBin, src, dst)
213+
return k.copyFrom(src, dst)
214+
}
215+
207216
// tempDirectory returns the directory to use as the temp directory
208217
// or an empty string if it should use the os default temp directory.
209218
func tempDirectory(isMinikubeSnap bool, isDockerSnap bool) (string, error) {
@@ -229,6 +238,14 @@ func (k *kicRunner) copy(src string, dst string) error {
229238
return copyToDocker(src, fullDest)
230239
}
231240

241+
func (k *kicRunner) copyFrom(src string, dst string) error {
242+
fullSource := fmt.Sprintf("%s:%s", k.nameOrID, src)
243+
if k.ociBin == oci.Podman {
244+
return copyToPodman(fullSource, dst)
245+
}
246+
return copyToDocker(fullSource, dst)
247+
}
248+
232249
func (k *kicRunner) chmod(dst string, perm string) error {
233250
_, err := k.RunCmd(exec.Command("sudo", "chmod", perm, dst))
234251
return err

0 commit comments

Comments
 (0)