Revert "new `image save` command"

pull/12260/head
Medya Ghazizadeh 2021-08-13 14:06:42 -07:00 committed by GitHub
parent 559048f83d
commit 0bd906077f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 0 additions and 554 deletions

View File

@ -144,77 +144,6 @@ var loadImageCmd = &cobra.Command{
},
}
func readFile(w io.Writer, tmp string) error {
r, err := os.Open(tmp)
if err != nil {
return err
}
_, err = io.Copy(w, r)
if err != nil {
return err
}
err = r.Close()
if err != nil {
return err
}
return nil
}
// saveImageCmd represents the image load command
var saveImageCmd = &cobra.Command{
Use: "save IMAGE [ARCHIVE | -]",
Short: "Save a image from minikube",
Long: "Save a image from minikube",
Example: "minikube image save image\nminikube image save image image.tar",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
exit.Message(reason.Usage, "Please provide an image in the container runtime to save from minikube via <minikube image save IMAGE_NAME>")
}
// Save images from container runtime
profile, err := config.LoadProfile(viper.GetString(config.ProfileName))
if err != nil {
exit.Error(reason.Usage, "loading profile", err)
}
if len(args) > 1 {
output = args[1]
if args[1] == "-" {
tmp, err := ioutil.TempFile("", "image.*.tar")
if err != nil {
exit.Error(reason.GuestImageSave, "Failed to get temp", err)
}
tmp.Close()
output = tmp.Name()
}
if err := machine.DoSaveImages([]string{args[0]}, output, []*config.Profile{profile}, ""); err != nil {
exit.Error(reason.GuestImageSave, "Failed to save image", err)
}
if args[1] == "-" {
err := readFile(os.Stdout, output)
if err != nil {
exit.Error(reason.GuestImageSave, "Failed to read temp", err)
}
os.Remove(output)
}
} else {
if err := machine.SaveAndCacheImages([]string{args[0]}, []*config.Profile{profile}); err != nil {
exit.Error(reason.GuestImageSave, "Failed to save image", err)
}
if imgDaemon || imgRemote {
image.UseDaemon(imgDaemon)
image.UseRemote(imgRemote)
err := image.UploadCachedImage(args[0])
if err != nil {
exit.Error(reason.GuestImageSave, "Failed to save image", err)
}
}
}
},
}
var removeImageCmd = &cobra.Command{
Use: "rm IMAGE [IMAGE...]",
Short: "Remove one or more images",
@ -329,8 +258,5 @@ func init() {
buildImageCmd.Flags().StringArrayVar(&buildEnv, "build-env", nil, "Environment variables to pass to the build. (format: key=value)")
buildImageCmd.Flags().StringArrayVar(&buildOpt, "build-opt", nil, "Specify arbitrary flags to pass to the build. (format: key=value)")
imageCmd.AddCommand(buildImageCmd)
saveImageCmd.Flags().BoolVar(&imgDaemon, "daemon", false, "Cache image to docker daemon")
saveImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image to remote registry")
imageCmd.AddCommand(saveImageCmd)
imageCmd.AddCommand(listImageCmd)
}

View File

@ -24,7 +24,6 @@ import (
"io"
"os"
"path"
"strconv"
"time"
"github.com/pkg/errors"
@ -38,11 +37,8 @@ const MemorySource = "memory"
// CopyableFile is something that can be copied
type CopyableFile interface {
io.Reader
io.Writer
GetLength() int
SetLength(int)
GetSourcePath() string
GetTargetPath() string
GetTargetDir() string
GetTargetName() string
@ -66,11 +62,6 @@ func (b *BaseAsset) GetSourcePath() string {
return b.SourcePath
}
// GetTargetPath returns target path
func (b *BaseAsset) GetTargetPath() string {
return path.Join(b.GetTargetDir(), b.GetTargetName())
}
// GetTargetDir returns target dir
func (b *BaseAsset) GetTargetDir() string {
return b.TargetDir
@ -95,7 +86,6 @@ func (b *BaseAsset) GetModTime() (time.Time, error) {
type FileAsset struct {
BaseAsset
reader io.ReadSeeker
writer io.Writer
file *os.File // Optional pointer to close file through FileAsset.Close()
}
@ -144,14 +134,6 @@ func (f *FileAsset) GetLength() (flen int) {
return int(fi.Size())
}
// SetLength sets the file length
func (f *FileAsset) SetLength(flen int) {
err := os.Truncate(f.SourcePath, int64(flen))
if err != nil {
klog.Errorf("truncate(%q) failed: %v", f.SourcePath, err)
}
}
// GetModTime returns modification time of the file
func (f *FileAsset) GetModTime() (time.Time, error) {
fi, err := os.Stat(f.SourcePath)
@ -170,23 +152,6 @@ func (f *FileAsset) Read(p []byte) (int, error) {
return f.reader.Read(p)
}
// Write writes the asset
func (f *FileAsset) Write(p []byte) (int, error) {
if f.writer == nil {
f.file.Close()
perms, err := strconv.ParseUint(f.Permissions, 8, 32)
if err != nil || perms > 07777 {
return 0, err
}
f.file, err = os.OpenFile(f.SourcePath, os.O_RDWR|os.O_CREATE, os.FileMode(perms))
if err != nil {
return 0, err
}
f.writer = io.Writer(f.file)
}
return f.writer.Write(p)
}
// Seek resets the reader to offset
func (f *FileAsset) Seek(offset int64, whence int) (int64, error) {
return f.reader.Seek(offset, whence)
@ -212,23 +177,11 @@ func (m *MemoryAsset) GetLength() int {
return m.length
}
// SetLength returns length
func (m *MemoryAsset) SetLength(len int) {
m.length = len
}
// Read reads the asset
func (m *MemoryAsset) Read(p []byte) (int, error) {
return m.reader.Read(p)
}
// Writer writes the asset
func (m *MemoryAsset) Write(p []byte) (int, error) {
m.length = len(p)
m.reader = bytes.NewReader(p)
return len(p), nil
}
// Seek resets the reader to offset
func (m *MemoryAsset) Seek(offset int64, whence int) (int64, error) {
return m.reader.Seek(offset, whence)
@ -345,11 +298,6 @@ func (m *BinAsset) GetLength() int {
return m.length
}
// SetLength sets length
func (m *BinAsset) SetLength(len int) {
m.length = len
}
// Read reads the asset
func (m *BinAsset) Read(p []byte) (int, error) {
if m.GetLength() == 0 {
@ -358,13 +306,6 @@ func (m *BinAsset) Read(p []byte) (int, error) {
return m.reader.Read(p)
}
// Write writes the asset
func (m *BinAsset) Write(p []byte) (int, error) {
m.length = len(p)
m.reader = bytes.NewReader(p)
return len(p), nil
}
// Seek resets the reader to offset
func (m *BinAsset) Seek(offset int64, whence int) (int64, error) {
return m.reader.Seek(offset, whence)

View File

@ -75,9 +75,6 @@ type Runner interface {
// Copy is a convenience method that runs a command to copy a file
Copy(assets.CopyableFile) error
// CopyFrom is a convenience method that runs a command to copy a file back
CopyFrom(assets.CopyableFile) error
// Remove is a convenience method that runs a command to remove a file
Remove(assets.CopyableFile) error
}

View File

@ -184,24 +184,6 @@ func (e *execRunner) Copy(f assets.CopyableFile) error {
return writeFile(dst, f, os.FileMode(perms))
}
// CopyFrom copies a file
func (e *execRunner) CopyFrom(f assets.CopyableFile) error {
src := path.Join(f.GetTargetDir(), f.GetTargetName())
dst := f.GetSourcePath()
klog.Infof("cp: %s --> %s (%d bytes)", src, dst, f.GetLength())
if f.GetLength() == 0 {
klog.Warningf("0 byte asset: %+v", f)
}
perms, err := strconv.ParseInt(f.GetPermissions(), 8, 0)
if err != nil || perms > 07777 {
return errors.Wrapf(err, "error converting permissions %s to integer", f.GetPermissions())
}
return writeFile(dst, f, os.FileMode(perms))
}
// Remove removes a file
func (e *execRunner) Remove(f assets.CopyableFile) error {
dst := filepath.Join(f.GetTargetDir(), f.GetTargetName())

View File

@ -142,19 +142,6 @@ func (f *FakeCommandRunner) Copy(file assets.CopyableFile) error {
return nil
}
func (f *FakeCommandRunner) CopyFrom(file assets.CopyableFile) error {
v, ok := f.fileMap.Load(file.GetSourcePath())
if !ok {
return fmt.Errorf("not found in map")
}
b := v.(bytes.Buffer)
_, err := io.Copy(file, &b)
if err != nil {
return errors.Wrapf(err, "error writing file: %+v", file)
}
return nil
}
// Remove removes the filename, file contents key value pair from the stored map
func (f *FakeCommandRunner) Remove(file assets.CopyableFile) error {
f.fileMap.Delete(file.GetSourcePath())

View File

@ -204,15 +204,6 @@ func (k *kicRunner) Copy(f assets.CopyableFile) error {
return k.copy(tf.Name(), dst)
}
// CopyFrom copies a file
func (k *kicRunner) CopyFrom(f assets.CopyableFile) error {
src := f.GetTargetPath()
dst := f.GetSourcePath()
klog.Infof("%s (direct): %s --> %s", k.ociBin, src, dst)
return k.copyFrom(src, dst)
}
// tempDirectory returns the directory to use as the temp directory
// or an empty string if it should use the os default temp directory.
func tempDirectory(isMinikubeSnap bool, isDockerSnap bool) (string, error) {
@ -238,14 +229,6 @@ func (k *kicRunner) copy(src string, dst string) error {
return copyToDocker(src, fullDest)
}
func (k *kicRunner) copyFrom(src string, dst string) error {
fullSource := fmt.Sprintf("%s:%s", k.nameOrID, src)
if k.ociBin == oci.Podman {
return copyToPodman(fullSource, dst)
}
return copyToDocker(fullSource, dst)
}
func (k *kicRunner) chmod(dst string, perm string) error {
_, err := k.RunCmd(exec.Command("sudo", "chmod", perm, dst))
return err

View File

@ -17,14 +17,11 @@ limitations under the License.
package command
import (
"bufio"
"bytes"
"fmt"
"io"
"os/exec"
"path"
"strconv"
"strings"
"sync"
"time"
@ -376,82 +373,3 @@ func (s *SSHRunner) Copy(f assets.CopyableFile) error {
}
return g.Wait()
}
// CopyFrom copies a file from the remote over SSH.
func (s *SSHRunner) CopyFrom(f assets.CopyableFile) error {
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))
sess, err := s.session()
if err != nil {
return errors.Wrap(err, "NewSession")
}
defer func() {
if err := sess.Close(); err != nil {
if err != io.EOF {
klog.Errorf("session close: %v", err)
}
}
}()
cmd := exec.Command("stat", "-c", "%s", dst)
rr, err := s.RunCmd(cmd)
if err != nil {
return fmt.Errorf("%s: %v", cmd, err)
}
length, err := strconv.Atoi(strings.TrimSuffix(rr.Stdout.String(), "\n"))
if err != nil {
return err
}
src := f.GetSourcePath()
klog.Infof("scp %s --> %s (%d bytes)", dst, src, length)
f.SetLength(length)
r, err := sess.StdoutPipe()
if err != nil {
return errors.Wrap(err, "StdoutPipe")
}
w, err := sess.StdinPipe()
if err != nil {
return errors.Wrap(err, "StdinPipe")
}
// The scpcmd below *should not* return until all data is copied and the
// StdinPipe is closed. But let's use errgroup to make it explicit.
var g errgroup.Group
var copied int64
g.Go(func() error {
defer w.Close()
br := bufio.NewReader(r)
fmt.Fprint(w, "\x00")
b, err := br.ReadBytes('\n')
if err != nil {
return errors.Wrap(err, "ReadBytes")
}
if b[0] != 'C' {
return fmt.Errorf("unexpected: %v", b)
}
fmt.Fprint(w, "\x00")
copied = 0
for copied < int64(length) {
n, err := io.CopyN(f, br, int64(length))
if err != nil {
return errors.Wrap(err, "io.CopyN")
}
copied += n
}
fmt.Fprint(w, "\x00")
err = sess.Wait()
if err != nil {
return err
}
return nil
})
scp := fmt.Sprintf("sudo scp -f %s", f.GetTargetPath())
err = sess.Start(scp)
if err != nil {
return fmt.Errorf("%s: %s", scp, err)
}
return g.Wait()
}

View File

@ -65,8 +65,6 @@ type CommandRunner interface {
WaitCmd(sc *command.StartedCmd) (*command.RunResult, error)
// Copy is a convenience method that runs a command to copy a file
Copy(assets.CopyableFile) error
// CopyFrom is a convenience method that runs a command to copy a file back
CopyFrom(assets.CopyableFile) error
// Remove is a convenience method that runs a command to remove a file
Remove(assets.CopyableFile) error
}

View File

@ -236,10 +236,6 @@ func (f *FakeRunner) Copy(assets.CopyableFile) error {
return nil
}
func (f *FakeRunner) CopyFrom(assets.CopyableFile) error {
return nil
}
func (f *FakeRunner) Remove(assets.CopyableFile) error {
return nil
}

View File

@ -33,12 +33,10 @@ import (
"github.com/google/go-containerregistry/pkg/v1/daemon"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/pkg/errors"
"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/localpath"
)
const (
@ -193,62 +191,6 @@ func retrieveRemote(ref name.Reference, p v1.Platform) (v1.Image, error) {
return img, err
}
// imagePathInCache returns path in local cache directory
func imagePathInCache(img string) string {
f := filepath.Join(constants.ImageCacheDir, img)
f = localpath.SanitizeCacheDir(f)
return f
}
func UploadCachedImage(imgName string) error {
tag, err := name.NewTag(imgName, name.WeakValidation)
if err != nil {
klog.Infof("error parsing image name %s tag %v ", imgName, err)
return err
}
return uploadImage(tag, imagePathInCache(imgName))
}
func uploadImage(tag name.Tag, p string) error {
var err error
var img v1.Image
if !useDaemon && !useRemote {
return fmt.Errorf("neither daemon nor remote")
}
img, err = tarball.ImageFromPath(p, &tag)
if err != nil {
return errors.Wrap(err, "tarball")
}
ref := name.Reference(tag)
klog.Infof("uploading image: %+v from: %s", ref, p)
if useDaemon {
return uploadDaemon(ref, img)
}
if useRemote {
return uploadRemote(ref, img, defaultPlatform)
}
return nil
}
func uploadDaemon(ref name.Reference, img v1.Image) error {
resp, err := daemon.Write(ref, img)
if err != nil {
klog.Warningf("daemon load for %s: %v\n%s", ref, err, resp)
}
return err
}
func uploadRemote(ref name.Reference, img v1.Image, p v1.Platform) error {
err := remote.Write(ref, img, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithPlatform(p))
if err != nil {
klog.Warningf("remote push for %s: %v", ref, err)
}
return err
}
// See https://github.com/kubernetes/minikube/issues/10402
// check if downloaded image Architecture field matches the requested and fix it otherwise
func fixPlatform(ref name.Reference, img v1.Image, p v1.Platform) (v1.Image, error) {

View File

@ -19,7 +19,6 @@ package machine
import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"sort"
@ -49,9 +48,6 @@ var loadRoot = path.Join(vmpath.GuestPersistentDir, "images")
// loadImageLock is used to serialize image loads to avoid overloading the guest VM
var loadImageLock sync.Mutex
// saveRoot is where images should be saved from within the guest VM
var saveRoot = path.Join(vmpath.GuestPersistentDir, "images")
// CacheImagesForBootstrapper will cache images for a bootstrapper
func CacheImagesForBootstrapper(imageRepository string, version string, clusterBootstrapper string) error {
images, err := bootstrapper.GetCachedImageList(imageRepository, version, clusterBootstrapper)
@ -330,173 +326,6 @@ func removeExistingImage(r cruntime.Manager, src string, imgName string) error {
return nil
}
// SaveCachedImages saves from the container runtime to the cache
func SaveCachedImages(cc *config.ClusterConfig, runner command.Runner, images []string, cacheDir string) error {
klog.Infof("SaveImages start: %s", images)
start := time.Now()
defer func() {
klog.Infof("SaveImages completed in %s", time.Since(start))
}()
var g errgroup.Group
for _, image := range images {
image := image
g.Go(func() error {
return transferAndSaveCachedImage(runner, cc.KubernetesConfig, image, cacheDir)
})
}
if err := g.Wait(); err != nil {
return errors.Wrap(err, "saving cached images")
}
klog.Infoln("Successfully saved all cached images")
return nil
}
// SaveLocalImages saves images from the container runtime
func SaveLocalImages(cc *config.ClusterConfig, runner command.Runner, images []string, output string) error {
var g errgroup.Group
for _, image := range images {
image := image
g.Go(func() error {
return transferAndSaveImage(runner, cc.KubernetesConfig, output, image)
})
}
if err := g.Wait(); err != nil {
return errors.Wrap(err, "saving images")
}
klog.Infoln("Successfully saved all images")
return nil
}
// SaveAndCacheImages saves images from all profiles into the cache
func SaveAndCacheImages(images []string, profiles []*config.Profile) error {
if len(images) == 0 {
return nil
}
return DoSaveImages(images, "", profiles, constants.ImageCacheDir)
}
// DoSaveImages saves images from all profiles
func DoSaveImages(images []string, output string, profiles []*config.Profile, cacheDir string) error {
api, err := NewAPIClient()
if err != nil {
return errors.Wrap(err, "api")
}
defer api.Close()
klog.Infof("Save images: %q", images)
succeeded := []string{}
failed := []string{}
for _, p := range profiles { // loading images to all running profiles
pName := p.Name // capture the loop variable
c, err := config.Load(pName)
if err != nil {
// Non-fatal because it may race with profile deletion
klog.Errorf("Failed to load profile %q: %v", pName, err)
failed = append(failed, pName)
continue
}
for _, n := range c.Nodes {
m := config.MachineName(*c, n)
status, err := Status(api, m)
if err != nil {
klog.Warningf("error getting status for %s: %v", m, err)
failed = append(failed, m)
continue
}
if status == state.Running.String() { // the not running hosts will load on next start
h, err := api.Load(m)
if err != nil {
klog.Warningf("Failed to load machine %q: %v", m, err)
failed = append(failed, m)
continue
}
cr, err := CommandRunner(h)
if err != nil {
return err
}
if cacheDir != "" {
// saving image names, to cache
err = SaveCachedImages(c, cr, images, cacheDir)
} else {
// saving mage files
err = SaveLocalImages(c, cr, images, output)
}
if err != nil {
failed = append(failed, m)
klog.Warningf("Failed to load cached images for profile %s. make sure the profile is running. %v", pName, err)
continue
}
succeeded = append(succeeded, m)
}
}
}
klog.Infof("succeeded pulling from : %s", strings.Join(succeeded, " "))
klog.Infof("failed pulling from : %s", strings.Join(failed, " "))
// Live pushes are not considered a failure
return nil
}
// transferAndSaveCachedImage transfers and loads a single image from the cache
func transferAndSaveCachedImage(cr command.Runner, k8s config.KubernetesConfig, imgName string, cacheDir string) error {
dst := filepath.Join(cacheDir, imgName)
dst = localpath.SanitizeCacheDir(dst)
return transferAndSaveImage(cr, k8s, dst, imgName)
}
// transferAndSaveImage transfers and loads a single image
func transferAndSaveImage(cr command.Runner, k8s config.KubernetesConfig, dst string, imgName string) error {
r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr})
if err != nil {
return errors.Wrap(err, "runtime")
}
klog.Infof("Saving image to: %s", dst)
filename := filepath.Base(dst)
_, err = os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0777)
if err != nil {
return err
}
f, err := assets.NewFileAsset(dst, saveRoot, filename, "0644")
if err != nil {
return errors.Wrapf(err, "creating copyable file asset: %s", filename)
}
defer func() {
if err := f.Close(); err != nil {
klog.Warningf("error closing the file %s: %v", f.GetSourcePath(), err)
}
}()
src := path.Join(saveRoot, filename)
args := append([]string{"rm", "-f"}, src)
if _, err := cr.RunCmd(exec.Command("sudo", args...)); err != nil {
return err
}
err = r.SaveImage(imgName, src)
if err != nil {
return errors.Wrapf(err, "%s save %s", r.Name(), src)
}
if err := cr.CopyFrom(f); err != nil {
return errors.Wrap(err, "transferring cached image")
}
klog.Infof("Transferred and saved %s to cache", dst)
return nil
}
// pullImages pulls images to the container run time
func pullImages(cruntime cruntime.Manager, images []string) error {
klog.Infof("PullImages start: %s", images)

View File

@ -317,8 +317,6 @@ var (
GuestImageRemove = Kind{ID: "GUEST_IMAGE_REMOVE", ExitCode: ExGuestError}
// minikube failed to build an image
GuestImageBuild = Kind{ID: "GUEST_IMAGE_BUILD", ExitCode: ExGuestError}
// minikube failed to push or save an image
GuestImageSave = Kind{ID: "GUEST_IMAGE_SAVE", ExitCode: ExGuestError}
// minikube failed to load host
GuestLoadHost = Kind{ID: "GUEST_LOAD_HOST", ExitCode: ExGuestError}
// minkube failed to create a mount

View File

@ -264,51 +264,3 @@ $ minikube image unload image busybox
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
## minikube image save
Save a image from minikube
### Synopsis
Save a image from minikube
```shell
minikube image save IMAGE [ARCHIVE | -] [flags]
```
### Examples
```
minikube image save image
minikube image save image image.tar
```
### Options
```
--daemon Cache image to docker daemon
--remote Cache image to remote registry
```
### Options inherited from parent commands
```
--add_dir_header If true, adds the file directory to the header of the log messages
--alsologtostderr log to standard error as well as files
-b, --bootstrapper string The name of the cluster bootstrapper that will set up the Kubernetes cluster. (default "kubeadm")
-h, --help
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--log_file string If non-empty, use this log file
--log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level)
-p, --profile string The name of the minikube VM being used. This can be set to allow having multiple instances of minikube independently. (default "minikube")
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
--user string Specifies the user executing the operation. Useful for auditing operations executed by 3rd party tools. Defaults to the operating system username.
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```

View File

@ -381,9 +381,6 @@ minikube failed to remove an image
"GUEST_IMAGE_BUILD" (Exit code ExGuestError)
minikube failed to build an image
"GUEST_IMAGE_SAVE" (Exit code ExGuestError)
minikube failed to push or save an image
"GUEST_LOAD_HOST" (Exit code ExGuestError)
minikube failed to load host