diff --git a/pkg/skaffold/deploy/component/kubernetes/component.go b/pkg/skaffold/deploy/component/kubernetes/component.go index 70cf00cb5da..f001548e9a1 100644 --- a/pkg/skaffold/deploy/component/kubernetes/component.go +++ b/pkg/skaffold/deploy/component/kubernetes/component.go @@ -84,7 +84,7 @@ func newDebugger(mode config.RunMode, podSelector kubernetes.PodSelector, namesp func newImageLoader(cfg k8sloader.Config, cli *kubectl.CLI) loader.ImageLoader { if cfg.LoadImages() { - return k8sloader.NewImageLoader(cfg.GetKubeContext(), cli) + return k8sloader.NewImageLoader(cfg, cli) } return &loader.NoopImageLoader{} } diff --git a/pkg/skaffold/docker/image.go b/pkg/skaffold/docker/image.go index 3bd7f51eb60..cbb58f8c6c6 100644 --- a/pkg/skaffold/docker/image.go +++ b/pkg/skaffold/docker/image.go @@ -92,6 +92,7 @@ type LocalDaemon interface { Push(ctx context.Context, out io.Writer, ref string) (string, error) Pull(ctx context.Context, out io.Writer, ref string, platform v1.Platform) error Load(ctx context.Context, out io.Writer, input io.Reader, ref string) (string, error) + Save(ctx context.Context, out io.Writer, ref string) (io.ReadCloser, error) Run(ctx context.Context, out io.Writer, opts ContainerCreateOpts) (<-chan container.WaitResponse, <-chan error, string, error) Delete(ctx context.Context, out io.Writer, id string) error Tag(ctx context.Context, image, ref string) error @@ -543,6 +544,11 @@ func (l *localDaemon) Load(ctx context.Context, out io.Writer, input io.Reader, return l.ImageID(ctx, ref) } +// Save saves an image to a tar file. Returns a ReadCloser for the tar file. +func (l *localDaemon) Save(ctx context.Context, out io.Writer, ref string) (io.ReadCloser, error) { + return l.apiClient.ImageSave(ctx, []string{ref}) +} + // Tag adds a tag to an image. func (l *localDaemon) Tag(ctx context.Context, image, ref string) error { return l.apiClient.ImageTag(ctx, image, ref) diff --git a/pkg/skaffold/kubernetes/loader/load.go b/pkg/skaffold/kubernetes/loader/load.go index 2c1527d4a17..6647f4f743b 100644 --- a/pkg/skaffold/kubernetes/loader/load.go +++ b/pkg/skaffold/kubernetes/loader/load.go @@ -39,21 +39,22 @@ import ( ) type ImageLoader struct { - kubeContext string - cli *kubectl.CLI + cfg Config + cli *kubectl.CLI } type Config interface { kubectl.Config + docker.Config GetKubeContext() string LoadImages() bool } -func NewImageLoader(kubeContext string, cli *kubectl.CLI) *ImageLoader { +func NewImageLoader(cfg Config, cli *kubectl.CLI) *ImageLoader { return &ImageLoader{ - kubeContext: kubeContext, - cli: cli, + cfg: cfg, + cli: cli, } } @@ -93,7 +94,7 @@ func (i *ImageLoader) LoadImages(ctx context.Context, out io.Writer, localImages artifacts := imagesToLoad(localImages, deployerImages, images) - if config.IsKindCluster(i.kubeContext) { + if config.IsKindCluster(i.cfg.GetKubeContext()) { kindCluster := config.KindClusterName(currentContext.Cluster) // With `kind`, docker images have to be loaded with the `kind` CLI. @@ -102,7 +103,7 @@ func (i *ImageLoader) LoadImages(ctx context.Context, out io.Writer, localImages } } - if config.IsK3dCluster(i.kubeContext) { + if config.IsK3dCluster(i.cfg.GetKubeConfig()) { k3dCluster := config.K3dClusterName(currentContext.Cluster) // With `k3d`, docker images have to be loaded with the `k3d` CLI. @@ -118,7 +119,41 @@ func (i *ImageLoader) LoadImages(ctx context.Context, out io.Writer, localImages func (i *ImageLoader) loadImagesInKindNodes(ctx context.Context, out io.Writer, kindCluster string, artifacts []graph.Artifact) error { output.Default.Fprintln(out, "Loading images into kind cluster nodes...") return i.loadImages(ctx, out, artifacts, func(tag string) *exec.Cmd { - return exec.CommandContext(ctx, "kind", "load", "docker-image", "--name", kindCluster, tag) + localDaemon, err := docker.NewAPIClient(ctx, i.cfg) + if err != nil { + output.Red.Fprintf(out, "Failed to create localDaemon: %v\n", err) + return nil + } + pr, pw := io.Pipe() + imageTar, err := localDaemon.Save(ctx, out, tag) + if err != nil { + output.Red.Fprintf(out, "Failed to save image %s: %v\n", tag, err) + return nil + } + + go func() { + defer pw.Close() + defer imageTar.Close() + _, err := io.Copy(pw, imageTar) + if err != nil { + // handle error + } + }() + + kindCmd := exec.CommandContext(ctx, "kind", "load", "image-archive", "-n", kindCluster, "/dev/stdin") + kindCmd.Stdin = pr + kindCmd.Stderr = out + /*/ Ensure the imageTar is closed after the command finishes + go func() { + defer func(imageTar io.ReadCloser) { + err := imageTar.Close() + if err != nil { + output.Red.Fprintf(out, "Failed to close image archive: %v\n", err) + } + }(imageTar) + }() // */ + + return kindCmd }) } @@ -183,7 +218,7 @@ func (i *ImageLoader) getCurrentContext() (*api.Context, error) { return nil, fmt.Errorf("unable to get kubernetes config: %w", err) } - currentContext, present := currentCfg.Contexts[i.kubeContext] + currentContext, present := currentCfg.Contexts[i.cfg.GetKubeContext()] if !present { return nil, fmt.Errorf("unable to get current kubernetes context: %w", err) }