From af5828dff9a1d171c3d37b34abeae7892ab35a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sun, 7 Mar 2021 20:59:38 +0100 Subject: [PATCH 01/28] Add initial re-implementation of the build command Currently only handles tarballs, not directories --- cmd/minikube/cmd/build.go | 52 +++++++++ cmd/minikube/cmd/root.go | 1 + pkg/minikube/cruntime/containerd.go | 14 +++ pkg/minikube/cruntime/crio.go | 10 ++ pkg/minikube/cruntime/cruntime.go | 2 + pkg/minikube/cruntime/docker.go | 10 ++ pkg/minikube/machine/build_images.go | 154 +++++++++++++++++++++++++++ pkg/minikube/reason/reason.go | 1 + 8 files changed, 244 insertions(+) create mode 100644 cmd/minikube/cmd/build.go create mode 100644 pkg/minikube/machine/build_images.go diff --git a/cmd/minikube/cmd/build.go b/cmd/minikube/cmd/build.go new file mode 100644 index 000000000000..687046d554b3 --- /dev/null +++ b/cmd/minikube/cmd/build.go @@ -0,0 +1,52 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/machine" + "k8s.io/minikube/pkg/minikube/reason" +) + +// buildCmd represents the build command +var buildCmd = &cobra.Command{ + Use: "build", + Short: "Build a container image", + Long: `Build a container image, using the container runtime. +Examples: +minikube build .`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) < 1 { + exit.Message(reason.Usage, "minikube build -- [OPTIONS] PATH | URL | -") + } + // Cache and load images into docker daemon + profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) + if err != nil { + exit.Error(reason.Usage, "loading profile", err) + } + img := args[0] + if err := machine.BuildImage(img, "test:latest", []*config.Profile{profile}); err != nil { + exit.Error(reason.GuestImageBuild, "Failed to build image", err) + } + }, +} + +func init() { +} diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index f2eb187d76ff..7bcf6b0348ec 100644 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -250,6 +250,7 @@ func init() { mountCmd, sshCmd, kubectlCmd, + buildCmd, nodeCmd, cpCmd, }, diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 11bcaf9fef1e..0a4e155d75ac 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -277,6 +277,20 @@ func (r *Containerd) RemoveImage(name string) error { return removeCRIImage(r.Runner, name) } +// BuildImage builds an image into this runtime +func (r *Containerd) BuildImage(path string, tag string) error { + klog.Infof("Building image: %s", path) + c := exec.Command("sudo", "buildctl", "build", + "--frontend", "dockerfile.v0", + "--local", fmt.Sprintf("context=%s", path), + "--local", fmt.Sprintf("dockerfile=%s", path), + "--output", fmt.Sprintf("type=image,name=%s", tag)) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "buildctl build.") + } + return nil +} + // CGroupDriver returns cgroup driver ("cgroupfs" or "systemd") func (r *Containerd) CGroupDriver() (string, error) { info, err := getCRIInfo(r.Runner) diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 210a2927b7b2..14ab02c16933 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -197,6 +197,16 @@ func (r *CRIO) RemoveImage(name string) error { return removeCRIImage(r.Runner, name) } +// BuildImage builds an image into this runtime +func (r *CRIO) BuildImage(path string, tag string) error { + klog.Infof("Building image: %s", path) + c := exec.Command("sudo", "podman", "build", "-t", tag, path) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "crio build image") + } + return nil +} + // CGroupDriver returns cgroup driver ("cgroupfs" or "systemd") func (r *CRIO) CGroupDriver() (string, error) { c := exec.Command("crio", "config") diff --git a/pkg/minikube/cruntime/cruntime.go b/pkg/minikube/cruntime/cruntime.go index 1a5d0a8ae71d..49621b5449a8 100644 --- a/pkg/minikube/cruntime/cruntime.go +++ b/pkg/minikube/cruntime/cruntime.go @@ -97,6 +97,8 @@ type Manager interface { LoadImage(string) error // Pull an image to the runtime from the container registry PullImage(string) error + // Build an image idempotently into the runtime on a host + BuildImage(string, string) error // ImageExists takes image name and image sha checks if an it exists ImageExists(string, string) bool diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 4298e75c1b71..37de92e45c2d 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -217,6 +217,16 @@ func (r *Docker) RemoveImage(name string) error { return nil } +// BuildImage builds an image into this runtime +func (r *Docker) BuildImage(path string, tag string) error { + klog.Infof("Building image: %s", path) + c := exec.Command("docker", "build", "-t", tag, path) + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "buildimage docker.") + } + return nil +} + // CGroupDriver returns cgroup driver ("cgroupfs" or "systemd") func (r *Docker) CGroupDriver() (string, error) { // Note: the server daemon has to be running, for this call to return successfully diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go new file mode 100644 index 000000000000..9bd4ab65f8dd --- /dev/null +++ b/pkg/minikube/machine/build_images.go @@ -0,0 +1,154 @@ +/* +Copyright 2021 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package machine + +import ( + "os" + "os/exec" + "path" + "path/filepath" + "strings" + + "github.com/docker/machine/libmachine/state" + "github.com/pkg/errors" + "k8s.io/klog/v2" + "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/command" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/cruntime" + "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/vmpath" +) + +// buildRoot is where images should be built from within the guest VM +var buildRoot = path.Join(vmpath.GuestPersistentDir, "build") + +// BuildImage builds image to all profiles +func BuildImage(path string, tag string, profiles []*config.Profile) error { + api, err := NewAPIClient() + if err != nil { + return errors.Wrap(err, "api") + } + defer api.Close() + + succeeded := []string{} + failed := []string{} + + for _, p := range profiles { // building 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() { + 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 + } + err = transferAndBuildImage(cr, c.KubernetesConfig, path, tag) + if err != nil { + failed = append(failed, m) + klog.Warningf("Failed to build image for profile %s. make sure the profile is running. %v", pName, err) + continue + } + succeeded = append(succeeded, m) + } + } + } + + klog.Infof("succeeded building to: %s", strings.Join(succeeded, " ")) + klog.Infof("failed building to: %s", strings.Join(failed, " ")) + return nil +} + +// transferAndBuildImage transfers and builds a single image +func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, tag string) error { + r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) + if err != nil { + return errors.Wrap(err, "runtime") + } + klog.Infof("Building image from path: %s", src) + + filename := filepath.Base(src) + filename = localpath.SanitizeCacheDir(filename) + + if _, err := os.Stat(src); err != nil { + return err + } + + args := append([]string{"mkdir", "-p"}, buildRoot) + if _, err := cr.RunCmd(exec.Command("sudo", args...)); err != nil { + return err + } + + dst := path.Join(buildRoot, filename) + f, err := assets.NewFileAsset(src, buildRoot, filename, "0644") + if err != nil { + return errors.Wrapf(err, "creating copyable file asset: %s", filename) + } + if err := cr.Copy(f); err != nil { + return errors.Wrap(err, "transferring cached image") + } + + context := path.Join(buildRoot, ".", strings.TrimSuffix(filename, filepath.Ext(filename))) + args = append([]string{"mkdir", "-p"}, context) + if _, err := cr.RunCmd(exec.Command("sudo", args...)); err != nil { + return err + } + args = append([]string{"tar", "-C", context, "-xf"}, dst) + if _, err := cr.RunCmd(exec.Command("sudo", args...)); err != nil { + return err + } + + err = r.BuildImage(context, tag) + if err != nil { + return errors.Wrapf(err, "%s build %s", r.Name(), dst) + } + + args = append([]string{"rm", "-rf"}, context) + if _, err := cr.RunCmd(exec.Command("sudo", args...)); err != nil { + return err + } + args = append([]string{"rm", "-f"}, dst) + if _, err := cr.RunCmd(exec.Command("sudo", args...)); err != nil { + return err + } + + klog.Infof("Built %s from %s", tag, src) + return nil +} diff --git a/pkg/minikube/reason/reason.go b/pkg/minikube/reason/reason.go index 0aa22685b5d8..fd5edb095ff1 100644 --- a/pkg/minikube/reason/reason.go +++ b/pkg/minikube/reason/reason.go @@ -250,6 +250,7 @@ var ( GuestImageList = Kind{ID: "GUEST_IMAGE_LIST", ExitCode: ExGuestError} GuestImageLoad = Kind{ID: "GUEST_IMAGE_LOAD", ExitCode: ExGuestError} GuestImageRemove = Kind{ID: "GUEST_IMAGE_REMOVE", ExitCode: ExGuestError} + GuestImageBuild = Kind{ID: "GUEST_IMAGE_BUILD", ExitCode: ExGuestError} GuestLoadHost = Kind{ID: "GUEST_LOAD_HOST", ExitCode: ExGuestError} GuestMount = Kind{ID: "GUEST_MOUNT", ExitCode: ExGuestError} GuestMountConflict = Kind{ID: "GUEST_MOUNT_CONFLICT", ExitCode: ExGuestConflict} From a2744827c33135092bb8a847c640fa631f1d952a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Mon, 8 Mar 2021 20:10:29 +0100 Subject: [PATCH 02/28] Add some basic support for setting the image tag --- cmd/minikube/cmd/build.go | 7 ++++++- pkg/minikube/cruntime/containerd.go | 10 +++++++++- pkg/minikube/cruntime/crio.go | 6 +++++- pkg/minikube/cruntime/docker.go | 6 +++++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/cmd/minikube/cmd/build.go b/cmd/minikube/cmd/build.go index 687046d554b3..9720c886dd17 100644 --- a/cmd/minikube/cmd/build.go +++ b/cmd/minikube/cmd/build.go @@ -25,6 +25,10 @@ import ( "k8s.io/minikube/pkg/minikube/reason" ) +var ( + tag string +) + // buildCmd represents the build command var buildCmd = &cobra.Command{ Use: "build", @@ -42,11 +46,12 @@ minikube build .`, exit.Error(reason.Usage, "loading profile", err) } img := args[0] - if err := machine.BuildImage(img, "test:latest", []*config.Profile{profile}); err != nil { + if err := machine.BuildImage(img, tag, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) } }, } func init() { + buildCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") } diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 0a4e155d75ac..2d501d50c1ed 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -280,11 +280,19 @@ func (r *Containerd) RemoveImage(name string) error { // BuildImage builds an image into this runtime func (r *Containerd) BuildImage(path string, tag string) error { klog.Infof("Building image: %s", path) + opt := "" + if tag != "" { + // add default tag if missing + if !strings.Contains(tag, ":") { + tag += ":latest" + } + opt = fmt.Sprintf(",name=%s", tag) + } c := exec.Command("sudo", "buildctl", "build", "--frontend", "dockerfile.v0", "--local", fmt.Sprintf("context=%s", path), "--local", fmt.Sprintf("dockerfile=%s", path), - "--output", fmt.Sprintf("type=image,name=%s", tag)) + "--output", fmt.Sprintf("type=image%s", opt)) if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "buildctl build.") } diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 14ab02c16933..0700b63c674f 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -200,7 +200,11 @@ func (r *CRIO) RemoveImage(name string) error { // BuildImage builds an image into this runtime func (r *CRIO) BuildImage(path string, tag string) error { klog.Infof("Building image: %s", path) - c := exec.Command("sudo", "podman", "build", "-t", tag, path) + args := []string{"podman", "build"} + if tag != "" { + args = append(args, "-t", tag) + } + c := exec.Command("sudo", args...) if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "crio build image") } diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 37de92e45c2d..2b863eb7bd6c 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -220,7 +220,11 @@ func (r *Docker) RemoveImage(name string) error { // BuildImage builds an image into this runtime func (r *Docker) BuildImage(path string, tag string) error { klog.Infof("Building image: %s", path) - c := exec.Command("docker", "build", "-t", tag, path) + args := []string{"build"} + if tag != "" { + args = append(args, "-t", tag) + } + c := exec.Command("docker", args...) if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "buildimage docker.") } From 9ef5d255d2b6ef20401b60f3614a9f55d5e7e402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Mon, 8 Mar 2021 21:43:53 +0100 Subject: [PATCH 03/28] Add support for build directory and Dockerfile Will create a temporary tarball, if given a dir Some code from github.com/fsouza/go-dockerclient (but not exported as a library, for some reason) Upgrade docker client and cherry-pick windows From v18.09.0 to v19.03.15, plus c3a0a37446 Update go-dockerclient and remove internal deps --- cmd/minikube/cmd/build.go | 42 +++++++++- go.mod | 3 +- go.sum | 8 +- third_party/go-dockerclient/LICENSE | 23 ++++++ third_party/go-dockerclient/tar.go | 122 ++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 third_party/go-dockerclient/LICENSE create mode 100644 third_party/go-dockerclient/tar.go diff --git a/cmd/minikube/cmd/build.go b/cmd/minikube/cmd/build.go index 9720c886dd17..ff1f07d4290e 100644 --- a/cmd/minikube/cmd/build.go +++ b/cmd/minikube/cmd/build.go @@ -17,18 +17,45 @@ limitations under the License. package cmd import ( + "io" + "io/ioutil" + "os" + "github.com/spf13/cobra" "github.com/spf13/viper" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/exit" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/reason" + docker "k8s.io/minikube/third_party/go-dockerclient" ) var ( - tag string + tag string + dockerFile string ) +func createTar(dir string) (string, error) { + tmp, err := ioutil.TempFile("", "build.*.tar") + if err != nil { + return "", err + } + tar, err := docker.CreateTarStream(dir, dockerFile) + if err != nil { + return "", err + } + _, err = io.Copy(tmp, tar) + if err != nil { + return "", err + } + err = tmp.Close() + if err != nil { + return "", err + } + + return tmp.Name(), nil +} + // buildCmd represents the build command var buildCmd = &cobra.Command{ Use: "build", @@ -46,12 +73,25 @@ minikube build .`, exit.Error(reason.Usage, "loading profile", err) } img := args[0] + var tmp string + info, err := os.Stat(img) + if err == nil && info.IsDir() { + tmp, err := createTar(img) + if err != nil { + exit.Error(reason.GuestImageBuild, "Failed to build image", err) + } + img = tmp + } if err := machine.BuildImage(img, tag, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) } + if tmp != "" { + os.Remove(tmp) + } }, } func init() { buildCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") + buildCmd.Flags().StringVarP(&dockerFile, "file", "f", "Dockerfile", "Path to the Dockerfile to use") } diff --git a/go.mod b/go.mod index a740ba6e4328..ad6b5f45d725 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect github.com/docker/cli v0.0.0-20200303162255-7d407207c304 // indirect - github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible + github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible github.com/docker/go-units v0.4.0 github.com/docker/machine v0.16.2 github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f @@ -102,6 +102,7 @@ require ( replace ( git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999 github.com/briandowns/spinner => github.com/alonyb/spinner v1.12.7 + github.com/docker/docker => github.com/afbjorklund/moby v0.0.0-20210308214533-2fa72faf0e8b github.com/docker/machine => github.com/machine-drivers/machine v0.7.1-0.20210306082426-fcb2ad5bcb17 github.com/google/go-containerregistry => github.com/afbjorklund/go-containerregistry v0.4.1-0.20210321165649-761f6f9626b1 github.com/samalba/dockerclient => github.com/sayboras/dockerclient v1.0.0 diff --git a/go.sum b/go.sum index df9b043fc436..41e1d3b96eba 100644 --- a/go.sum +++ b/go.sum @@ -109,6 +109,8 @@ github.com/VividCortex/godaemon v0.0.0-20201030160542-15e3f4925a21 h1:Pgxfz/g+Xy github.com/VividCortex/godaemon v0.0.0-20201030160542-15e3f4925a21/go.mod h1:Y8CJ3IwPIAkMhv/rRUWIlczaeqd9ty9yrl+nc2AbaL4= github.com/afbjorklund/go-containerregistry v0.4.1-0.20210321165649-761f6f9626b1 h1:AI8EIk8occ3pruhaTpkaQxQGlC1dHx3J9hAtg7t+FLI= github.com/afbjorklund/go-containerregistry v0.4.1-0.20210321165649-761f6f9626b1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= +github.com/afbjorklund/moby v0.0.0-20210308214533-2fa72faf0e8b h1:wmyy8gOOzYzMD6SfMs44yCPoOWAAHcjxCio/zQjOlDU= +github.com/afbjorklund/moby v0.0.0-20210308214533-2fa72faf0e8b/go.mod h1:qXUBi22bjTfxOV8XyOI/W1PklPSinepyWoJ6eYSLwwo= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -292,10 +294,8 @@ github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TT github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v17.12.0-ce-rc1.0.20181225093023-5ddb1d410a8b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible h1:SiUATuP//KecDjpOK2tvZJgeScYAklvyjfK8JZlU6fo= -github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible h1:nhVo1udYfMj0Jsw0lnqrTjjf33aLpdgW9Wve9fHVzhQ= +github.com/docker/docker v17.12.0-ce-rc1.0.20210128214336-420b1d36250f+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= diff --git a/third_party/go-dockerclient/LICENSE b/third_party/go-dockerclient/LICENSE new file mode 100644 index 000000000000..707a0ed49b28 --- /dev/null +++ b/third_party/go-dockerclient/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013-2021, go-dockerclient authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/go-dockerclient/tar.go b/third_party/go-dockerclient/tar.go new file mode 100644 index 000000000000..bb6eee087f8e --- /dev/null +++ b/third_party/go-dockerclient/tar.go @@ -0,0 +1,122 @@ +// Copyright 2014 go-dockerclient authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package docker + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/fileutils" +) + +func CreateTarStream(srcPath, dockerfilePath string) (io.ReadCloser, error) { + srcPath, err := filepath.Abs(srcPath) + if err != nil { + return nil, err + } + + excludes, err := parseDockerignore(srcPath) + if err != nil { + return nil, err + } + + includes := []string{"."} + + // If .dockerignore mentions .dockerignore or the Dockerfile + // then make sure we send both files over to the daemon + // because Dockerfile is, obviously, needed no matter what, and + // .dockerignore is needed to know if either one needs to be + // removed. The deamon will remove them for us, if needed, after it + // parses the Dockerfile. + // + // https://github.com/docker/docker/issues/8330 + // + forceIncludeFiles := []string{".dockerignore", dockerfilePath} + + for _, includeFile := range forceIncludeFiles { + if includeFile == "" { + continue + } + keepThem, err := fileutils.Matches(includeFile, excludes) + if err != nil { + return nil, fmt.Errorf("cannot match .dockerfileignore: '%s', error: %w", includeFile, err) + } + if keepThem { + includes = append(includes, includeFile) + } + } + + if err := validateContextDirectory(srcPath, excludes); err != nil { + return nil, err + } + tarOpts := &archive.TarOptions{ + ExcludePatterns: excludes, + IncludeFiles: includes, + Compression: archive.Uncompressed, + NoLchown: true, + } + return archive.TarWithOptions(srcPath, tarOpts) +} + +// validateContextDirectory checks if all the contents of the directory +// can be read and returns an error if some files can't be read. +// Symlinks which point to non-existing files don't trigger an error +func validateContextDirectory(srcPath string, excludes []string) error { + return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error { + // skip this directory/file if it's not in the path, it won't get added to the context + if relFilePath, relErr := filepath.Rel(srcPath, filePath); relErr != nil { + return relErr + } else if skip, matchErr := fileutils.Matches(relFilePath, excludes); matchErr != nil { + return matchErr + } else if skip { + if f.IsDir() { + return filepath.SkipDir + } + return nil + } + + if err != nil { + if os.IsPermission(err) { + return fmt.Errorf("cannot stat %q: %w", filePath, err) + } + if os.IsNotExist(err) { + return nil + } + return err + } + + // skip checking if symlinks point to non-existing files, such symlinks can be useful + // also skip named pipes, because they hanging on open + if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 { + return nil + } + + if !f.IsDir() { + currentFile, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("cannot open %q for reading: %w", filePath, err) + } + currentFile.Close() + } + return nil + }) +} + +func parseDockerignore(root string) ([]string, error) { + var excludes []string + ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore")) + if err != nil && !os.IsNotExist(err) { + return excludes, fmt.Errorf("error reading .dockerignore: %w", err) + } + excludes = strings.Split(string(ignore), "\n") + + return excludes, nil +} From 6f26e4b8ec9ed519088c3fb524212fd81c65d112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Mon, 8 Mar 2021 21:56:02 +0100 Subject: [PATCH 04/28] Generate documentation for the build command --- site/content/en/docs/commands/build.md | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 site/content/en/docs/commands/build.md diff --git a/site/content/en/docs/commands/build.md b/site/content/en/docs/commands/build.md new file mode 100644 index 000000000000..9b50e897d6ba --- /dev/null +++ b/site/content/en/docs/commands/build.md @@ -0,0 +1,50 @@ +--- +title: "build" +description: > + Build a container image +--- + + +## minikube build + +Build a container image + +### Synopsis + +Build a container image, using the container runtime. +Examples: +minikube build . + +```shell +minikube build [flags] +``` + +### Options + +``` + -f, --file string Path to the Dockerfile to use (default "Dockerfile") + -t, --tag string Tag to apply to the new image (optional) +``` + +### 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 +``` + From 6cfbf2cc3dac3f77294fc049b2814229d201a6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 08:27:27 +0100 Subject: [PATCH 05/28] Move minikube build cmd to minikube image build Use the long name also for examples and usage --- cmd/minikube/cmd/build.go | 97 -------------------------- cmd/minikube/cmd/image.go | 64 +++++++++++++++++ cmd/minikube/cmd/root.go | 1 - pkg/minikube/cruntime/crio.go | 1 + pkg/minikube/cruntime/docker.go | 1 + site/content/en/docs/commands/build.md | 50 ------------- site/content/en/docs/commands/image.md | 47 +++++++++++++ 7 files changed, 113 insertions(+), 148 deletions(-) delete mode 100644 cmd/minikube/cmd/build.go delete mode 100644 site/content/en/docs/commands/build.md diff --git a/cmd/minikube/cmd/build.go b/cmd/minikube/cmd/build.go deleted file mode 100644 index ff1f07d4290e..000000000000 --- a/cmd/minikube/cmd/build.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cmd - -import ( - "io" - "io/ioutil" - "os" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - "k8s.io/minikube/pkg/minikube/config" - "k8s.io/minikube/pkg/minikube/exit" - "k8s.io/minikube/pkg/minikube/machine" - "k8s.io/minikube/pkg/minikube/reason" - docker "k8s.io/minikube/third_party/go-dockerclient" -) - -var ( - tag string - dockerFile string -) - -func createTar(dir string) (string, error) { - tmp, err := ioutil.TempFile("", "build.*.tar") - if err != nil { - return "", err - } - tar, err := docker.CreateTarStream(dir, dockerFile) - if err != nil { - return "", err - } - _, err = io.Copy(tmp, tar) - if err != nil { - return "", err - } - err = tmp.Close() - if err != nil { - return "", err - } - - return tmp.Name(), nil -} - -// buildCmd represents the build command -var buildCmd = &cobra.Command{ - Use: "build", - Short: "Build a container image", - Long: `Build a container image, using the container runtime. -Examples: -minikube build .`, - Run: func(cmd *cobra.Command, args []string) { - if len(args) < 1 { - exit.Message(reason.Usage, "minikube build -- [OPTIONS] PATH | URL | -") - } - // Cache and load images into docker daemon - profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) - if err != nil { - exit.Error(reason.Usage, "loading profile", err) - } - img := args[0] - var tmp string - info, err := os.Stat(img) - if err == nil && info.IsDir() { - tmp, err := createTar(img) - if err != nil { - exit.Error(reason.GuestImageBuild, "Failed to build image", err) - } - img = tmp - } - if err := machine.BuildImage(img, tag, []*config.Profile{profile}); err != nil { - exit.Error(reason.GuestImageBuild, "Failed to build image", err) - } - if tmp != "" { - os.Remove(tmp) - } - }, -} - -func init() { - buildCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") - buildCmd.Flags().StringVarP(&dockerFile, "file", "f", "Dockerfile", "Path to the Dockerfile to use") -} diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 62c50b44f543..50cb74affecc 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -29,6 +29,7 @@ import ( "k8s.io/minikube/pkg/minikube/image" "k8s.io/minikube/pkg/minikube/machine" "k8s.io/minikube/pkg/minikube/reason" + docker "k8s.io/minikube/third_party/go-dockerclient" ) // imageCmd represents the image command @@ -59,6 +60,11 @@ func saveFile(r io.Reader) (string, error) { return tmp.Name(), nil } +var ( + tag string + dockerFile string +) + // loadImageCmd represents the image load command var loadImageCmd = &cobra.Command{ Use: "load IMAGE | ARCHIVE | -", @@ -173,6 +179,61 @@ $ minikube image list }, } +func createTar(dir string) (string, error) { + tmp, err := ioutil.TempFile("", "build.*.tar") + if err != nil { + return "", err + } + tar, err := docker.CreateTarStream(dir, dockerFile) + if err != nil { + return "", err + } + _, err = io.Copy(tmp, tar) + if err != nil { + return "", err + } + err = tmp.Close() + if err != nil { + return "", err + } + + return tmp.Name(), nil +} + +// buildImageCmd represents the image build command +var buildImageCmd = &cobra.Command{ + Use: "build", + Short: "Build a container image in minikube", + Long: "Build a container image, using the container runtime.", + Example: `minikube image build .`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) < 1 { + exit.Message(reason.Usage, "Please provide a path to build") + } + // Cache and load images into docker daemon + profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) + if err != nil { + exit.Error(reason.Usage, "loading profile", err) + } + img := args[0] + var tmp string + info, err := os.Stat(img) + if err == nil && info.IsDir() { + tmp, err := createTar(img) + if err != nil { + exit.Error(reason.GuestImageBuild, "Failed to build image", err) + } + img = tmp + } + if err := machine.BuildImage(img, tag, []*config.Profile{profile}); err != nil { + exit.Error(reason.GuestImageBuild, "Failed to build image", err) + } + if tmp != "" { + os.Remove(tmp) + } + }, +} + func init() { imageCmd.AddCommand(loadImageCmd) imageCmd.AddCommand(removeImageCmd) @@ -180,4 +241,7 @@ func init() { loadImageCmd.Flags().BoolVar(&imgDaemon, "daemon", false, "Cache image from docker daemon") loadImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image from remote registry") imageCmd.AddCommand(listImageCmd) + buildImageCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") + buildImageCmd.Flags().StringVarP(&dockerFile, "file", "f", "Dockerfile", "Path to the Dockerfile to use") + imageCmd.AddCommand(buildImageCmd) } diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index 7bcf6b0348ec..f2eb187d76ff 100644 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -250,7 +250,6 @@ func init() { mountCmd, sshCmd, kubectlCmd, - buildCmd, nodeCmd, cpCmd, }, diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 0700b63c674f..95641bd5ea2e 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -204,6 +204,7 @@ func (r *CRIO) BuildImage(path string, tag string) error { if tag != "" { args = append(args, "-t", tag) } + args = append(args, path) c := exec.Command("sudo", args...) if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "crio build image") diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 2b863eb7bd6c..27e72a36d543 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -224,6 +224,7 @@ func (r *Docker) BuildImage(path string, tag string) error { if tag != "" { args = append(args, "-t", tag) } + args = append(args, path) c := exec.Command("docker", args...) if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "buildimage docker.") diff --git a/site/content/en/docs/commands/build.md b/site/content/en/docs/commands/build.md deleted file mode 100644 index 9b50e897d6ba..000000000000 --- a/site/content/en/docs/commands/build.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: "build" -description: > - Build a container image ---- - - -## minikube build - -Build a container image - -### Synopsis - -Build a container image, using the container runtime. -Examples: -minikube build . - -```shell -minikube build [flags] -``` - -### Options - -``` - -f, --file string Path to the Dockerfile to use (default "Dockerfile") - -t, --tag string Tag to apply to the new image (optional) -``` - -### 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 -``` - diff --git a/site/content/en/docs/commands/image.md b/site/content/en/docs/commands/image.md index a1457499fa95..47f139309e3b 100644 --- a/site/content/en/docs/commands/image.md +++ b/site/content/en/docs/commands/image.md @@ -35,6 +35,53 @@ Manage images --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging ``` +## minikube image build + +Build a container image in minikube + +### Synopsis + +Build a container image, using the container runtime. + +```shell +minikube image build [flags] +``` + +### Examples + +``` +minikube image build . +``` + +### Options + +``` + -f, --file string Path to the Dockerfile to use (default "Dockerfile") + -t, --tag string Tag to apply to the new image (optional) +``` + +### 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 +``` + ## minikube image help Help about any command From 844c6965cce96f61b99d2cafbd0b5f2018534923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 12:57:10 +0100 Subject: [PATCH 06/28] Make sure to show output from build cmd --- pkg/minikube/cruntime/containerd.go | 3 +++ pkg/minikube/cruntime/crio.go | 3 +++ pkg/minikube/cruntime/docker.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 2d501d50c1ed..dab1f8d750b7 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -21,6 +21,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "os" "os/exec" "path" "strings" @@ -293,6 +294,8 @@ func (r *Containerd) BuildImage(path string, tag string) error { "--local", fmt.Sprintf("context=%s", path), "--local", fmt.Sprintf("dockerfile=%s", path), "--output", fmt.Sprintf("type=image%s", opt)) + c.Stdout = os.Stdout + c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "buildctl build.") } diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 95641bd5ea2e..b71039403dcf 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "net" + "os" "os/exec" "path" "strings" @@ -206,6 +207,8 @@ func (r *CRIO) BuildImage(path string, tag string) error { } args = append(args, path) c := exec.Command("sudo", args...) + c.Stdout = os.Stdout + c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "crio build image") } diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 27e72a36d543..a31b1ce6e0fc 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -18,6 +18,7 @@ package cruntime import ( "fmt" + "os" "os/exec" "path" "strings" @@ -226,6 +227,8 @@ func (r *Docker) BuildImage(path string, tag string) error { } args = append(args, path) c := exec.Command("docker", args...) + c.Stdout = os.Stdout + c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "buildimage docker.") } From c94a6d7968b2ed53b70e7a6b2b4e005c0b926ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 15:16:10 +0100 Subject: [PATCH 07/28] Make sure to pass any file param to build --- cmd/minikube/cmd/image.go | 4 ++-- pkg/minikube/cruntime/containerd.go | 19 +++++++++++++++---- pkg/minikube/cruntime/crio.go | 12 +++++++++--- pkg/minikube/cruntime/cruntime.go | 2 +- pkg/minikube/cruntime/docker.go | 12 +++++++++--- pkg/minikube/machine/build_images.go | 8 ++++---- site/content/en/docs/commands/image.md | 2 +- 7 files changed, 41 insertions(+), 18 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 50cb74affecc..2167c8f8291a 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -225,7 +225,7 @@ var buildImageCmd = &cobra.Command{ } img = tmp } - if err := machine.BuildImage(img, tag, []*config.Profile{profile}); err != nil { + if err := machine.BuildImage(img, dockerFile, tag, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) } if tmp != "" { @@ -242,6 +242,6 @@ func init() { loadImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image from remote registry") imageCmd.AddCommand(listImageCmd) buildImageCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") - buildImageCmd.Flags().StringVarP(&dockerFile, "file", "f", "Dockerfile", "Path to the Dockerfile to use") + buildImageCmd.Flags().StringVarP(&dockerFile, "file", "f", "", "Path to the Dockerfile to use (optional)") imageCmd.AddCommand(buildImageCmd) } diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index dab1f8d750b7..536a444738fa 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -279,8 +279,19 @@ func (r *Containerd) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *Containerd) BuildImage(path string, tag string) error { - klog.Infof("Building image: %s", path) +func (r *Containerd) BuildImage(dir string, file string, tag string) error { + if file != "" { + if !path.IsAbs(file) { + file = path.Join(dir, file) + } + // copy to standard path for Dockerfile + df := path.Join(dir, "Dockerfile") + cmd := exec.Command("sudo", "cp", "-f", file, df) + if _, err := r.Runner.RunCmd(cmd); err != nil { + return err + } + } + klog.Infof("Building image: %s", dir) opt := "" if tag != "" { // add default tag if missing @@ -291,8 +302,8 @@ func (r *Containerd) BuildImage(path string, tag string) error { } c := exec.Command("sudo", "buildctl", "build", "--frontend", "dockerfile.v0", - "--local", fmt.Sprintf("context=%s", path), - "--local", fmt.Sprintf("dockerfile=%s", path), + "--local", fmt.Sprintf("context=%s", dir), + "--local", fmt.Sprintf("dockerfile=%s", dir), "--output", fmt.Sprintf("type=image%s", opt)) c.Stdout = os.Stdout c.Stderr = os.Stderr diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index b71039403dcf..c080ff6bb916 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -199,13 +199,19 @@ func (r *CRIO) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *CRIO) BuildImage(path string, tag string) error { - klog.Infof("Building image: %s", path) +func (r *CRIO) BuildImage(dir string, file string, tag string) error { + klog.Infof("Building image: %s", dir) args := []string{"podman", "build"} + if file != "" { + if !path.IsAbs(file) { + file = path.Join(dir, file) + } + args = append(args, "-f", file) + } if tag != "" { args = append(args, "-t", tag) } - args = append(args, path) + args = append(args, dir) c := exec.Command("sudo", args...) c.Stdout = os.Stdout c.Stderr = os.Stderr diff --git a/pkg/minikube/cruntime/cruntime.go b/pkg/minikube/cruntime/cruntime.go index 49621b5449a8..158f28ead44a 100644 --- a/pkg/minikube/cruntime/cruntime.go +++ b/pkg/minikube/cruntime/cruntime.go @@ -98,7 +98,7 @@ type Manager interface { // Pull an image to the runtime from the container registry PullImage(string) error // Build an image idempotently into the runtime on a host - BuildImage(string, string) error + BuildImage(string, string, string) error // ImageExists takes image name and image sha checks if an it exists ImageExists(string, string) bool diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index a31b1ce6e0fc..fe161076679f 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -219,13 +219,19 @@ func (r *Docker) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *Docker) BuildImage(path string, tag string) error { - klog.Infof("Building image: %s", path) +func (r *Docker) BuildImage(dir string, file string, tag string) error { + klog.Infof("Building image: %s", dir) args := []string{"build"} + if file != "" { + if !path.IsAbs(file) { + file = path.Join(dir, file) + } + args = append(args, "-f", file) + } if tag != "" { args = append(args, "-t", tag) } - args = append(args, path) + args = append(args, dir) c := exec.Command("docker", args...) c.Stdout = os.Stdout c.Stderr = os.Stderr diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index 9bd4ab65f8dd..bef9c492b271 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -38,7 +38,7 @@ import ( var buildRoot = path.Join(vmpath.GuestPersistentDir, "build") // BuildImage builds image to all profiles -func BuildImage(path string, tag string, profiles []*config.Profile) error { +func BuildImage(path string, file string, tag string, profiles []*config.Profile) error { api, err := NewAPIClient() if err != nil { return errors.Wrap(err, "api") @@ -80,7 +80,7 @@ func BuildImage(path string, tag string, profiles []*config.Profile) error { if err != nil { return err } - err = transferAndBuildImage(cr, c.KubernetesConfig, path, tag) + err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag) if err != nil { failed = append(failed, m) klog.Warningf("Failed to build image for profile %s. make sure the profile is running. %v", pName, err) @@ -97,7 +97,7 @@ func BuildImage(path string, tag string, profiles []*config.Profile) error { } // transferAndBuildImage transfers and builds a single image -func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, tag string) error { +func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string) error { r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) if err != nil { return errors.Wrap(err, "runtime") @@ -135,7 +135,7 @@ func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src s return err } - err = r.BuildImage(context, tag) + err = r.BuildImage(context, file, tag) if err != nil { return errors.Wrapf(err, "%s build %s", r.Name(), dst) } diff --git a/site/content/en/docs/commands/image.md b/site/content/en/docs/commands/image.md index 47f139309e3b..cdf9da2eeafa 100644 --- a/site/content/en/docs/commands/image.md +++ b/site/content/en/docs/commands/image.md @@ -56,7 +56,7 @@ minikube image build . ### Options ``` - -f, --file string Path to the Dockerfile to use (default "Dockerfile") + -f, --file string Path to the Dockerfile to use (optional) -t, --tag string Tag to apply to the new image (optional) ``` From f670368023884a49002a1f9c934ca23b2f128f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 15:36:50 +0100 Subject: [PATCH 08/28] Move absolute file path handling to machine --- pkg/minikube/cruntime/containerd.go | 3 --- pkg/minikube/cruntime/crio.go | 3 --- pkg/minikube/cruntime/docker.go | 3 --- pkg/minikube/machine/build_images.go | 3 +++ 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 536a444738fa..0e0f1553fd94 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -281,9 +281,6 @@ func (r *Containerd) RemoveImage(name string) error { // BuildImage builds an image into this runtime func (r *Containerd) BuildImage(dir string, file string, tag string) error { if file != "" { - if !path.IsAbs(file) { - file = path.Join(dir, file) - } // copy to standard path for Dockerfile df := path.Join(dir, "Dockerfile") cmd := exec.Command("sudo", "cp", "-f", file, df) diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index c080ff6bb916..c32cef243bfe 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -203,9 +203,6 @@ func (r *CRIO) BuildImage(dir string, file string, tag string) error { klog.Infof("Building image: %s", dir) args := []string{"podman", "build"} if file != "" { - if !path.IsAbs(file) { - file = path.Join(dir, file) - } args = append(args, "-f", file) } if tag != "" { diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index fe161076679f..1013b302b596 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -223,9 +223,6 @@ func (r *Docker) BuildImage(dir string, file string, tag string) error { klog.Infof("Building image: %s", dir) args := []string{"build"} if file != "" { - if !path.IsAbs(file) { - file = path.Join(dir, file) - } args = append(args, "-f", file) } if tag != "" { diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index bef9c492b271..a073c4f15e88 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -135,6 +135,9 @@ func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src s return err } + if !path.IsAbs(file) { + file = path.Join(context, file) + } err = r.BuildImage(context, file, tag) if err != nil { return errors.Wrapf(err, "%s build %s", r.Name(), dst) From 22150fbf980915fad86165e368d8d65d3d53f8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 15:48:15 +0100 Subject: [PATCH 09/28] Don't add build context to the default file --- pkg/minikube/machine/build_images.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index a073c4f15e88..100c1da1948b 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -135,7 +135,7 @@ func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src s return err } - if !path.IsAbs(file) { + if file != "" && !path.IsAbs(file) { file = path.Join(context, file) } err = r.BuildImage(context, file, tag) From a8a8788c11a3bef117a5e5d7604c154598c7fc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 15:52:44 +0100 Subject: [PATCH 10/28] Don't fail when the default file is passed --- pkg/minikube/cruntime/containerd.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 0e0f1553fd94..78dcc46c0e16 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -283,9 +283,11 @@ func (r *Containerd) BuildImage(dir string, file string, tag string) error { if file != "" { // copy to standard path for Dockerfile df := path.Join(dir, "Dockerfile") - cmd := exec.Command("sudo", "cp", "-f", file, df) - if _, err := r.Runner.RunCmd(cmd); err != nil { - return err + if file != df { + cmd := exec.Command("sudo", "cp", "-f", file, df) + if _, err := r.Runner.RunCmd(cmd); err != nil { + return err + } } } klog.Infof("Building image: %s", dir) From 02b4267fd0b35a1d106037c94c6b30b0ecc71c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 10 Mar 2021 21:45:16 +0100 Subject: [PATCH 11/28] Add option for pushing image to registry --- cmd/minikube/cmd/image.go | 4 +++- pkg/minikube/cruntime/containerd.go | 5 ++++- pkg/minikube/cruntime/crio.go | 10 +++++++++- pkg/minikube/cruntime/cruntime.go | 2 +- pkg/minikube/cruntime/docker.go | 10 +++++++++- pkg/minikube/machine/build_images.go | 8 ++++---- site/content/en/docs/commands/image.md | 1 + 7 files changed, 31 insertions(+), 9 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 2167c8f8291a..1735b66a8e80 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -62,6 +62,7 @@ func saveFile(r io.Reader) (string, error) { var ( tag string + push bool dockerFile string ) @@ -225,7 +226,7 @@ var buildImageCmd = &cobra.Command{ } img = tmp } - if err := machine.BuildImage(img, dockerFile, tag, []*config.Profile{profile}); err != nil { + if err := machine.BuildImage(img, dockerFile, tag, push, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) } if tmp != "" { @@ -242,6 +243,7 @@ func init() { loadImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image from remote registry") imageCmd.AddCommand(listImageCmd) buildImageCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") + buildImageCmd.Flags().BoolVarP(&push, "push", "", false, "Push the new image (requires tag)") buildImageCmd.Flags().StringVarP(&dockerFile, "file", "f", "", "Path to the Dockerfile to use (optional)") imageCmd.AddCommand(buildImageCmd) } diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 78dcc46c0e16..d640515044b2 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -279,7 +279,7 @@ func (r *Containerd) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *Containerd) BuildImage(dir string, file string, tag string) error { +func (r *Containerd) BuildImage(dir string, file string, tag string, push bool) error { if file != "" { // copy to standard path for Dockerfile df := path.Join(dir, "Dockerfile") @@ -298,6 +298,9 @@ func (r *Containerd) BuildImage(dir string, file string, tag string) error { tag += ":latest" } opt = fmt.Sprintf(",name=%s", tag) + if push { + opt += ",push=true" + } } c := exec.Command("sudo", "buildctl", "build", "--frontend", "dockerfile.v0", diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index c32cef243bfe..51a7c4526986 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -199,7 +199,7 @@ func (r *CRIO) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *CRIO) BuildImage(dir string, file string, tag string) error { +func (r *CRIO) BuildImage(dir string, file string, tag string, push bool) error { klog.Infof("Building image: %s", dir) args := []string{"podman", "build"} if file != "" { @@ -215,6 +215,14 @@ func (r *CRIO) BuildImage(dir string, file string, tag string) error { if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "crio build image") } + if tag != "" && push { + c := exec.Command("sudo", "podman", "push", tag) + c.Stdout = os.Stdout + c.Stderr = os.Stderr + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "crio push image") + } + } return nil } diff --git a/pkg/minikube/cruntime/cruntime.go b/pkg/minikube/cruntime/cruntime.go index 158f28ead44a..cba168f153e9 100644 --- a/pkg/minikube/cruntime/cruntime.go +++ b/pkg/minikube/cruntime/cruntime.go @@ -98,7 +98,7 @@ type Manager interface { // Pull an image to the runtime from the container registry PullImage(string) error // Build an image idempotently into the runtime on a host - BuildImage(string, string, string) error + BuildImage(string, string, string, bool) error // ImageExists takes image name and image sha checks if an it exists ImageExists(string, string) bool diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 1013b302b596..f9ba86807127 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -219,7 +219,7 @@ func (r *Docker) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *Docker) BuildImage(dir string, file string, tag string) error { +func (r *Docker) BuildImage(dir string, file string, tag string, push bool) error { klog.Infof("Building image: %s", dir) args := []string{"build"} if file != "" { @@ -235,6 +235,14 @@ func (r *Docker) BuildImage(dir string, file string, tag string) error { if _, err := r.Runner.RunCmd(c); err != nil { return errors.Wrap(err, "buildimage docker.") } + if tag != "" && push { + c := exec.Command("docker", "push", tag) + c.Stdout = os.Stdout + c.Stderr = os.Stderr + if _, err := r.Runner.RunCmd(c); err != nil { + return errors.Wrap(err, "pushimage docker.") + } + } return nil } diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index 100c1da1948b..b0ab2393a5a3 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -38,7 +38,7 @@ import ( var buildRoot = path.Join(vmpath.GuestPersistentDir, "build") // BuildImage builds image to all profiles -func BuildImage(path string, file string, tag string, profiles []*config.Profile) error { +func BuildImage(path string, file string, tag string, push bool, profiles []*config.Profile) error { api, err := NewAPIClient() if err != nil { return errors.Wrap(err, "api") @@ -80,7 +80,7 @@ func BuildImage(path string, file string, tag string, profiles []*config.Profile if err != nil { return err } - err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag) + err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag, push) if err != nil { failed = append(failed, m) klog.Warningf("Failed to build image for profile %s. make sure the profile is running. %v", pName, err) @@ -97,7 +97,7 @@ func BuildImage(path string, file string, tag string, profiles []*config.Profile } // transferAndBuildImage transfers and builds a single image -func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string) error { +func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool) error { r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) if err != nil { return errors.Wrap(err, "runtime") @@ -138,7 +138,7 @@ func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src s if file != "" && !path.IsAbs(file) { file = path.Join(context, file) } - err = r.BuildImage(context, file, tag) + err = r.BuildImage(context, file, tag, push) if err != nil { return errors.Wrapf(err, "%s build %s", r.Name(), dst) } diff --git a/site/content/en/docs/commands/image.md b/site/content/en/docs/commands/image.md index cdf9da2eeafa..7b0e052afb2a 100644 --- a/site/content/en/docs/commands/image.md +++ b/site/content/en/docs/commands/image.md @@ -57,6 +57,7 @@ minikube image build . ``` -f, --file string Path to the Dockerfile to use (optional) + --push Push the new image (requires tag) -t, --tag string Tag to apply to the new image (optional) ``` From 40cbe652eaed7920f92818491a3af89b36b83afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 12 Mar 2021 17:44:38 +0100 Subject: [PATCH 12/28] Improve comments and remove docker daemon --- cmd/minikube/cmd/image.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 1735b66a8e80..cbe2924a8e83 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -76,7 +76,7 @@ var loadImageCmd = &cobra.Command{ if len(args) == 0 { exit.Message(reason.Usage, "Please provide an image in your local daemon to load into minikube via ") } - // Cache and load images into docker daemon + // Cache and load images into container runtime profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) if err != nil { exit.Error(reason.Usage, "loading profile", err) @@ -211,7 +211,7 @@ var buildImageCmd = &cobra.Command{ if len(args) < 1 { exit.Message(reason.Usage, "Please provide a path to build") } - // Cache and load images into docker daemon + // Build images into container runtime profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) if err != nil { exit.Error(reason.Usage, "loading profile", err) From 9f9958f7eac3503728ad9bbaed9aa266604dabfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 12 Mar 2021 20:44:52 +0100 Subject: [PATCH 13/28] Allow building image from an url as well --- cmd/minikube/cmd/image.go | 21 ++++++++---- pkg/minikube/cruntime/containerd.go | 48 +++++++++++++++++++++++++++- pkg/minikube/cruntime/crio.go | 6 ++-- pkg/minikube/cruntime/docker.go | 6 ++-- pkg/minikube/machine/build_images.go | 27 +++++++++++++++- 5 files changed, 93 insertions(+), 15 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index cbe2924a8e83..32f5bf911a82 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -19,6 +19,7 @@ package cmd import ( "io" "io/ioutil" + "net/url" "os" "strings" @@ -209,7 +210,7 @@ var buildImageCmd = &cobra.Command{ Example: `minikube image build .`, Run: func(cmd *cobra.Command, args []string) { if len(args) < 1 { - exit.Message(reason.Usage, "Please provide a path to build") + exit.Message(reason.Usage, "Please provide a path or url to build") } // Build images into container runtime profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) @@ -218,13 +219,19 @@ var buildImageCmd = &cobra.Command{ } img := args[0] var tmp string - info, err := os.Stat(img) - if err == nil && info.IsDir() { - tmp, err := createTar(img) - if err != nil { - exit.Error(reason.GuestImageBuild, "Failed to build image", err) + // If it is an URL, pass it as-is + u, err := url.Parse(img) + if err == nil && u.Scheme == "" && u.Host == "" { + // If it's a directory, tar it + info, err := os.Stat(img) + if err == nil && info.IsDir() { + tmp, err := createTar(img) + if err != nil { + exit.Error(reason.GuestImageBuild, "Failed to build image", err) + } + img = tmp } - img = tmp + // Otherwise, assume it's a tar } if err := machine.BuildImage(img, dockerFile, tag, push, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index d640515044b2..6d279c2b2d0e 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -21,6 +21,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net/url" "os" "os/exec" "path" @@ -278,9 +279,54 @@ func (r *Containerd) RemoveImage(name string) error { return removeCRIImage(r.Runner, name) } +func downloadRemote(cr CommandRunner, src string) (string, error) { + u, err := url.Parse(src) + if err != nil { + return "", err + } + if u.Scheme == "" && u.Host == "" { // regular file, return + return src, nil + } + if u.Scheme == "git" { + return "", fmt.Errorf("%s url not supported yet", u) + } + + // download to a temporary file + rr, err := cr.RunCmd(exec.Command("mktemp")) + if err != nil { + return "", err + } + dst := strings.TrimSpace(rr.Stdout.String()) + cmd := exec.Command("curl", "-L", "-o", dst, src) + if _, err := cr.RunCmd(cmd); err != nil { + return "", err + } + + // extract to a temporary directory + rr, err = cr.RunCmd(exec.Command("mktemp", "-d")) + if err != nil { + return "", err + } + tmp := strings.TrimSpace(rr.Stdout.String()) + cmd = exec.Command("tar", "-C", tmp, "-xf", dst) + if _, err := cr.RunCmd(cmd); err != nil { + return "", err + } + + return tmp, nil +} + // BuildImage builds an image into this runtime -func (r *Containerd) BuildImage(dir string, file string, tag string, push bool) error { +func (r *Containerd) BuildImage(src string, file string, tag string, push bool) error { + // download url if not already present + dir, err := downloadRemote(r.Runner, src) + if err != nil { + return err + } if file != "" { + if dir != src { + file = path.Join(dir, file) + } // copy to standard path for Dockerfile df := path.Join(dir, "Dockerfile") if file != df { diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 51a7c4526986..701e0988f956 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -199,8 +199,8 @@ func (r *CRIO) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *CRIO) BuildImage(dir string, file string, tag string, push bool) error { - klog.Infof("Building image: %s", dir) +func (r *CRIO) BuildImage(src string, file string, tag string, push bool) error { + klog.Infof("Building image: %s", src) args := []string{"podman", "build"} if file != "" { args = append(args, "-f", file) @@ -208,7 +208,7 @@ func (r *CRIO) BuildImage(dir string, file string, tag string, push bool) error if tag != "" { args = append(args, "-t", tag) } - args = append(args, dir) + args = append(args, src) c := exec.Command("sudo", args...) c.Stdout = os.Stdout c.Stderr = os.Stderr diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index f9ba86807127..95384e30f1ef 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -219,8 +219,8 @@ func (r *Docker) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *Docker) BuildImage(dir string, file string, tag string, push bool) error { - klog.Infof("Building image: %s", dir) +func (r *Docker) BuildImage(src string, file string, tag string, push bool) error { + klog.Infof("Building image: %s", src) args := []string{"build"} if file != "" { args = append(args, "-f", file) @@ -228,7 +228,7 @@ func (r *Docker) BuildImage(dir string, file string, tag string, push bool) erro if tag != "" { args = append(args, "-t", tag) } - args = append(args, dir) + args = append(args, src) c := exec.Command("docker", args...) c.Stdout = os.Stdout c.Stderr = os.Stderr diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index b0ab2393a5a3..2212c1f61738 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -17,6 +17,7 @@ limitations under the License. package machine import ( + "net/url" "os" "os/exec" "path" @@ -48,6 +49,9 @@ func BuildImage(path string, file string, tag string, push bool, profiles []*con succeeded := []string{} failed := []string{} + u, err := url.Parse(path) + remote := err == nil && u.Scheme != "" + for _, p := range profiles { // building images to all running profiles pName := p.Name // capture the loop variable @@ -80,7 +84,11 @@ func BuildImage(path string, file string, tag string, push bool, profiles []*con if err != nil { return err } - err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag, push) + if remote { + err = buildImage(cr, c.KubernetesConfig, path, file, tag, push) + } else { + err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag, push) + } if err != nil { failed = append(failed, m) klog.Warningf("Failed to build image for profile %s. make sure the profile is running. %v", pName, err) @@ -96,6 +104,23 @@ func BuildImage(path string, file string, tag string, push bool, profiles []*con return nil } +// buildImage builds a single image +func buildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool) error { + r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) + if err != nil { + return errors.Wrap(err, "runtime") + } + klog.Infof("Building image from url: %s", src) + + err = r.BuildImage(src, file, tag, push) + if err != nil { + return errors.Wrapf(err, "%s build %s", r.Name(), src) + } + + klog.Infof("Built %s from %s", tag, src) + return nil +} + // transferAndBuildImage transfers and builds a single image func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool) error { r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) From db203cd0e2b099e57f2e3b580002e4195683ca5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 12 Mar 2021 20:55:43 +0100 Subject: [PATCH 14/28] Allow building tarball from stdin stream --- cmd/minikube/cmd/image.go | 45 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 32f5bf911a82..9d2219d8715c 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -181,25 +181,28 @@ $ minikube image list }, } -func createTar(dir string) (string, error) { +func saveFile(r io.Reader) (string, error) { tmp, err := ioutil.TempFile("", "build.*.tar") if err != nil { return "", err } - tar, err := docker.CreateTarStream(dir, dockerFile) + _, err = io.Copy(tmp, r) if err != nil { return "", err } - _, err = io.Copy(tmp, tar) + err = tmp.Close() if err != nil { return "", err } - err = tmp.Close() + return tmp.Name(), nil +} + +func createTar(dir string) (string, error) { + tar, err := docker.CreateTarStream(dir, dockerFile) if err != nil { return "", err } - - return tmp.Name(), nil + return saveFile(tar) } // buildImageCmd represents the image build command @@ -219,19 +222,27 @@ var buildImageCmd = &cobra.Command{ } img := args[0] var tmp string - // If it is an URL, pass it as-is - u, err := url.Parse(img) - if err == nil && u.Scheme == "" && u.Host == "" { - // If it's a directory, tar it - info, err := os.Stat(img) - if err == nil && info.IsDir() { - tmp, err := createTar(img) - if err != nil { - exit.Error(reason.GuestImageBuild, "Failed to build image", err) + if img == "-" { + tmp, err = saveFile(os.Stdin) + if err != nil { + exit.Error(reason.GuestImageBuild, "Failed to save stdin", err) + } + img = tmp + } else { + // If it is an URL, pass it as-is + u, err := url.Parse(img) + if err == nil && u.Scheme == "" && u.Host == "" { + // If it's a directory, tar it + info, err := os.Stat(img) + if err == nil && info.IsDir() { + tmp, err := createTar(img) + if err != nil { + exit.Error(reason.GuestImageBuild, "Failed to save dir", err) + } + img = tmp } - img = tmp + // Otherwise, assume it's a tar } - // Otherwise, assume it's a tar } if err := machine.BuildImage(img, dockerFile, tag, push, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) From c961190ed861a8550a360fe280bf5dfc71b81d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 12 Mar 2021 23:19:16 +0100 Subject: [PATCH 15/28] Allow passing environ and options to build --- cmd/minikube/cmd/image.go | 6 +++++- pkg/minikube/cruntime/containerd.go | 18 ++++++++++++------ pkg/minikube/cruntime/crio.go | 7 ++++++- pkg/minikube/cruntime/cruntime.go | 2 +- pkg/minikube/cruntime/docker.go | 7 ++++++- pkg/minikube/machine/build_images.go | 14 +++++++------- site/content/en/docs/commands/image.md | 8 +++++--- 7 files changed, 42 insertions(+), 20 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 9d2219d8715c..dabedf94c55f 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -65,6 +65,8 @@ var ( tag string push bool dockerFile string + buildEnv []string + buildOpt []string ) // loadImageCmd represents the image load command @@ -244,7 +246,7 @@ var buildImageCmd = &cobra.Command{ // Otherwise, assume it's a tar } } - if err := machine.BuildImage(img, dockerFile, tag, push, []*config.Profile{profile}); err != nil { + if err := machine.BuildImage(img, dockerFile, tag, push, buildEnv, buildOpt, []*config.Profile{profile}); err != nil { exit.Error(reason.GuestImageBuild, "Failed to build image", err) } if tmp != "" { @@ -263,5 +265,7 @@ func init() { buildImageCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") buildImageCmd.Flags().BoolVarP(&push, "push", "", false, "Push the new image (requires tag)") buildImageCmd.Flags().StringVarP(&dockerFile, "file", "f", "", "Path to the Dockerfile to use (optional)") + 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) } diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 6d279c2b2d0e..acf2c5d4c485 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -317,7 +317,7 @@ func downloadRemote(cr CommandRunner, src string) (string, error) { } // BuildImage builds an image into this runtime -func (r *Containerd) BuildImage(src string, file string, tag string, push bool) error { +func (r *Containerd) BuildImage(src string, file string, tag string, push bool, env []string, opts []string) error { // download url if not already present dir, err := downloadRemote(r.Runner, src) if err != nil { @@ -337,22 +337,28 @@ func (r *Containerd) BuildImage(src string, file string, tag string, push bool) } } klog.Infof("Building image: %s", dir) - opt := "" + extra := "" if tag != "" { // add default tag if missing if !strings.Contains(tag, ":") { tag += ":latest" } - opt = fmt.Sprintf(",name=%s", tag) + extra = fmt.Sprintf(",name=%s", tag) if push { - opt += ",push=true" + extra += ",push=true" } } - c := exec.Command("sudo", "buildctl", "build", + args := []string{"buildctl", "build", "--frontend", "dockerfile.v0", "--local", fmt.Sprintf("context=%s", dir), "--local", fmt.Sprintf("dockerfile=%s", dir), - "--output", fmt.Sprintf("type=image%s", opt)) + "--output", fmt.Sprintf("type=image%s", extra)} + for _, opt := range opts { + args = append(args, "--"+opt) + } + c := exec.Command("sudo", args...) + e := os.Environ() + c.Env = append(e, env...) c.Stdout = os.Stdout c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 701e0988f956..ff4fab9bdf1c 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -199,7 +199,7 @@ func (r *CRIO) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *CRIO) BuildImage(src string, file string, tag string, push bool) error { +func (r *CRIO) BuildImage(src string, file string, tag string, push bool, env []string, opts []string) error { klog.Infof("Building image: %s", src) args := []string{"podman", "build"} if file != "" { @@ -209,7 +209,12 @@ func (r *CRIO) BuildImage(src string, file string, tag string, push bool) error args = append(args, "-t", tag) } args = append(args, src) + for _, opt := range opts { + args = append(args, "--"+opt) + } c := exec.Command("sudo", args...) + e := os.Environ() + c.Env = append(e, env...) c.Stdout = os.Stdout c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { diff --git a/pkg/minikube/cruntime/cruntime.go b/pkg/minikube/cruntime/cruntime.go index cba168f153e9..04a49783f68c 100644 --- a/pkg/minikube/cruntime/cruntime.go +++ b/pkg/minikube/cruntime/cruntime.go @@ -98,7 +98,7 @@ type Manager interface { // Pull an image to the runtime from the container registry PullImage(string) error // Build an image idempotently into the runtime on a host - BuildImage(string, string, string, bool) error + BuildImage(string, string, string, bool, []string, []string) error // ImageExists takes image name and image sha checks if an it exists ImageExists(string, string) bool diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 95384e30f1ef..01f4e3d606f8 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -219,7 +219,7 @@ func (r *Docker) RemoveImage(name string) error { } // BuildImage builds an image into this runtime -func (r *Docker) BuildImage(src string, file string, tag string, push bool) error { +func (r *Docker) BuildImage(src string, file string, tag string, push bool, env []string, opts []string) error { klog.Infof("Building image: %s", src) args := []string{"build"} if file != "" { @@ -229,7 +229,12 @@ func (r *Docker) BuildImage(src string, file string, tag string, push bool) erro args = append(args, "-t", tag) } args = append(args, src) + for _, opt := range opts { + args = append(args, "--"+opt) + } c := exec.Command("docker", args...) + e := os.Environ() + c.Env = append(e, env...) c.Stdout = os.Stdout c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index 2212c1f61738..d0f4e52f7417 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -39,7 +39,7 @@ import ( var buildRoot = path.Join(vmpath.GuestPersistentDir, "build") // BuildImage builds image to all profiles -func BuildImage(path string, file string, tag string, push bool, profiles []*config.Profile) error { +func BuildImage(path string, file string, tag string, push bool, env []string, opt []string, profiles []*config.Profile) error { api, err := NewAPIClient() if err != nil { return errors.Wrap(err, "api") @@ -85,9 +85,9 @@ func BuildImage(path string, file string, tag string, push bool, profiles []*con return err } if remote { - err = buildImage(cr, c.KubernetesConfig, path, file, tag, push) + err = buildImage(cr, c.KubernetesConfig, path, file, tag, push, env, opt) } else { - err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag, push) + err = transferAndBuildImage(cr, c.KubernetesConfig, path, file, tag, push, env, opt) } if err != nil { failed = append(failed, m) @@ -105,14 +105,14 @@ func BuildImage(path string, file string, tag string, push bool, profiles []*con } // buildImage builds a single image -func buildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool) error { +func buildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool, env []string, opt []string) error { r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) if err != nil { return errors.Wrap(err, "runtime") } klog.Infof("Building image from url: %s", src) - err = r.BuildImage(src, file, tag, push) + err = r.BuildImage(src, file, tag, push, env, opt) if err != nil { return errors.Wrapf(err, "%s build %s", r.Name(), src) } @@ -122,7 +122,7 @@ func buildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file } // transferAndBuildImage transfers and builds a single image -func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool) error { +func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src string, file string, tag string, push bool, env []string, opt []string) error { r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime, Runner: cr}) if err != nil { return errors.Wrap(err, "runtime") @@ -163,7 +163,7 @@ func transferAndBuildImage(cr command.Runner, k8s config.KubernetesConfig, src s if file != "" && !path.IsAbs(file) { file = path.Join(context, file) } - err = r.BuildImage(context, file, tag, push) + err = r.BuildImage(context, file, tag, push, env, opt) if err != nil { return errors.Wrapf(err, "%s build %s", r.Name(), dst) } diff --git a/site/content/en/docs/commands/image.md b/site/content/en/docs/commands/image.md index 7b0e052afb2a..78436adee862 100644 --- a/site/content/en/docs/commands/image.md +++ b/site/content/en/docs/commands/image.md @@ -56,9 +56,11 @@ minikube image build . ### Options ``` - -f, --file string Path to the Dockerfile to use (optional) - --push Push the new image (requires tag) - -t, --tag string Tag to apply to the new image (optional) + --build-env stringArray Environment variables to pass to the build. (format: key=value) + --build-opt stringArray Specify arbitrary flags to pass to the build. (format: key=value) + -f, --file string Path to the Dockerfile to use (optional) + --push Push the new image (requires tag) + -t, --tag string Tag to apply to the new image (optional) ``` ### Options inherited from parent commands From 947e31aead855cfcc65c11f46f5cc86c10244146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 12 Mar 2021 23:24:24 +0100 Subject: [PATCH 16/28] Add git url support to containerd runtime --- pkg/minikube/cruntime/containerd.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index acf2c5d4c485..829008007a16 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -279,6 +279,20 @@ func (r *Containerd) RemoveImage(name string) error { return removeCRIImage(r.Runner, name) } +func gitClone(cr CommandRunner, src string) (string, error) { + // clone to a temporary directory + rr, err := cr.RunCmd(exec.Command("mktemp", "-d")) + if err != nil { + return "", err + } + tmp := strings.TrimSpace(rr.Stdout.String()) + cmd := exec.Command("git", "clone", src, tmp) + if _, err := cr.RunCmd(cmd); err != nil { + return "", err + } + return tmp, nil +} + func downloadRemote(cr CommandRunner, src string) (string, error) { u, err := url.Parse(src) if err != nil { @@ -288,7 +302,7 @@ func downloadRemote(cr CommandRunner, src string) (string, error) { return src, nil } if u.Scheme == "git" { - return "", fmt.Errorf("%s url not supported yet", u) + return gitClone(cr, src) } // download to a temporary file From e230f023a0e0f2a1acbea22fc411303fc8aacfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 12 Mar 2021 23:27:34 +0100 Subject: [PATCH 17/28] Address lint append suggestions from gocritic --- pkg/minikube/cruntime/containerd.go | 3 ++- pkg/minikube/cruntime/crio.go | 3 ++- pkg/minikube/cruntime/docker.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 829008007a16..78738be5c93f 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -372,7 +372,8 @@ func (r *Containerd) BuildImage(src string, file string, tag string, push bool, } c := exec.Command("sudo", args...) e := os.Environ() - c.Env = append(e, env...) + e = append(e, env...) + c.Env = e c.Stdout = os.Stdout c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index ff4fab9bdf1c..faea475faf1e 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -214,7 +214,8 @@ func (r *CRIO) BuildImage(src string, file string, tag string, push bool, env [] } c := exec.Command("sudo", args...) e := os.Environ() - c.Env = append(e, env...) + e = append(e, env...) + c.Env = e c.Stdout = os.Stdout c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index 01f4e3d606f8..d351fedd305a 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -234,7 +234,8 @@ func (r *Docker) BuildImage(src string, file string, tag string, push bool, env } c := exec.Command("docker", args...) e := os.Environ() - c.Env = append(e, env...) + e = append(e, env...) + c.Env = e c.Stdout = os.Stdout c.Stderr = os.Stderr if _, err := r.Runner.RunCmd(c); err != nil { From 9516122d70c9f45d3d09341ebefd2a34d039b28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Tue, 16 Mar 2021 19:34:23 +0100 Subject: [PATCH 18/28] Add example parameters to the usage help text --- cmd/minikube/cmd/image.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index dabedf94c55f..a64e60811979 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -209,7 +209,7 @@ func createTar(dir string) (string, error) { // buildImageCmd represents the image build command var buildImageCmd = &cobra.Command{ - Use: "build", + Use: "build PATH | URL | -", Short: "Build a container image in minikube", Long: "Build a container image, using the container runtime.", Example: `minikube image build .`, From c7153247f960d88bc86605a539c39b5ab6548ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 24 Mar 2021 20:25:22 +0100 Subject: [PATCH 19/28] Regenerate documentation for the unit test --- site/content/en/docs/commands/image.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/en/docs/commands/image.md b/site/content/en/docs/commands/image.md index 78436adee862..49a03176029f 100644 --- a/site/content/en/docs/commands/image.md +++ b/site/content/en/docs/commands/image.md @@ -44,7 +44,7 @@ Build a container image in minikube Build a container image, using the container runtime. ```shell -minikube image build [flags] +minikube image build PATH | URL | - [flags] ``` ### Examples From f7e07770c03247ed59140284b83c064226e7a647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Thu, 25 Mar 2021 20:26:39 +0100 Subject: [PATCH 20/28] Clean up duplicated function etc after merge --- cmd/minikube/cmd/image.go | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index a64e60811979..8a63e59afe28 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -40,9 +40,14 @@ var imageCmd = &cobra.Command{ } var ( - pull bool - imgDaemon bool - imgRemote bool + pull bool + imgDaemon bool + imgRemote bool + tag string + push bool + dockerFile string + buildEnv []string + buildOpt []string ) func saveFile(r io.Reader) (string, error) { @@ -61,14 +66,6 @@ func saveFile(r io.Reader) (string, error) { return tmp.Name(), nil } -var ( - tag string - push bool - dockerFile string - buildEnv []string - buildOpt []string -) - // loadImageCmd represents the image load command var loadImageCmd = &cobra.Command{ Use: "load IMAGE | ARCHIVE | -", @@ -183,22 +180,6 @@ $ minikube image list }, } -func saveFile(r io.Reader) (string, error) { - tmp, err := ioutil.TempFile("", "build.*.tar") - if err != nil { - return "", err - } - _, err = io.Copy(tmp, r) - if err != nil { - return "", err - } - err = tmp.Close() - if err != nil { - return "", err - } - return tmp.Name(), nil -} - func createTar(dir string) (string, error) { tar, err := docker.CreateTarStream(dir, dockerFile) if err != nil { From 943561b24d498a3f4eafad3958e989942591cd94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Thu, 25 Mar 2021 20:26:39 +0100 Subject: [PATCH 21/28] Clean up the order of the image commands --- cmd/minikube/cmd/image.go | 44 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 8a63e59afe28..12a02b8125ac 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -162,24 +162,6 @@ $ minikube image unload image busybox }, } -var listImageCmd = &cobra.Command{ - Use: "list", - Short: "List images", - Example: ` -$ minikube image list -`, - Aliases: []string{"ls"}, - Run: func(cmd *cobra.Command, args []string) { - profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) - if err != nil { - exit.Error(reason.Usage, "loading profile", err) - } - if err := machine.ListImages(profile); err != nil { - exit.Error(reason.GuestImageList, "Failed to list images", err) - } - }, -} - func createTar(dir string) (string, error) { tar, err := docker.CreateTarStream(dir, dockerFile) if err != nil { @@ -203,6 +185,7 @@ var buildImageCmd = &cobra.Command{ if err != nil { exit.Error(reason.Usage, "loading profile", err) } + img := args[0] var tmp string if img == "-" { @@ -236,17 +219,36 @@ var buildImageCmd = &cobra.Command{ }, } +var listImageCmd = &cobra.Command{ + Use: "list", + Short: "List images", + Example: ` +$ minikube image list +`, + Aliases: []string{"ls"}, + Run: func(cmd *cobra.Command, args []string) { + profile, err := config.LoadProfile(viper.GetString(config.ProfileName)) + if err != nil { + exit.Error(reason.Usage, "loading profile", err) + } + + if err := machine.ListImages(profile); err != nil { + exit.Error(reason.GuestImageList, "Failed to list images", err) + } + }, +} + func init() { - imageCmd.AddCommand(loadImageCmd) - imageCmd.AddCommand(removeImageCmd) loadImageCmd.Flags().BoolVarP(&pull, "pull", "", false, "Pull the remote image (no caching)") loadImageCmd.Flags().BoolVar(&imgDaemon, "daemon", false, "Cache image from docker daemon") loadImageCmd.Flags().BoolVar(&imgRemote, "remote", false, "Cache image from remote registry") - imageCmd.AddCommand(listImageCmd) + imageCmd.AddCommand(loadImageCmd) + imageCmd.AddCommand(removeImageCmd) buildImageCmd.Flags().StringVarP(&tag, "tag", "t", "", "Tag to apply to the new image (optional)") buildImageCmd.Flags().BoolVarP(&push, "push", "", false, "Push the new image (requires tag)") buildImageCmd.Flags().StringVarP(&dockerFile, "file", "f", "", "Path to the Dockerfile to use (optional)") 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) + imageCmd.AddCommand(listImageCmd) } From a5f835bb0250d5553adf31b309ea6fe11ced77bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Wed, 21 Apr 2021 22:37:00 +0200 Subject: [PATCH 22/28] Add skeleton for minikube image integration test --- test/integration/image_test.go | 76 ++++++++++++++++++++ test/integration/testdata/build/Dockerfile | 3 + test/integration/testdata/build/content.txt | 1 + test/integration/testdata/hello-world.tar | Bin 0 -> 24576 bytes 4 files changed, 80 insertions(+) create mode 100644 test/integration/image_test.go create mode 100644 test/integration/testdata/build/Dockerfile create mode 100644 test/integration/testdata/build/content.txt create mode 100644 test/integration/testdata/hello-world.tar diff --git a/test/integration/image_test.go b/test/integration/image_test.go new file mode 100644 index 000000000000..4bd6ceab1968 --- /dev/null +++ b/test/integration/image_test.go @@ -0,0 +1,76 @@ +// +build integration + +/* +Copyright 2021 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "context" + "os/exec" + "path/filepath" + "testing" +) + +// TestImage tests minikube image +func TestImage(t *testing.T) { + profile := UniqueProfileName("image") + ctx, cancel := context.WithTimeout(context.Background(), Minutes(5)) + defer CleanupWithLogs(t, profile, cancel) + + args := append([]string{"start", "-p", profile, "--memory=2048"}, StartArgs()...) + rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) + if err != nil { + t.Fatalf("starting minikube: %v\n%s", err, rr.Output()) + } + + tests := []struct { + command string + args []string + }{ + { + command: "image", + args: []string{"load", filepath.Join(*testdataDir, "hello-world.tar")}, + }, { + command: "image", + args: []string{"rm", "hello-world"}, + }, { + command: "image", + args: []string{"build", "-t", "my-image", filepath.Join(*testdataDir, "build")}, + }, { + command: "image", + args: []string{"list"}, + }, + } + + for _, test := range tests { + t.Run(test.command, func(t *testing.T) { + args := []string{test.command, "-p", profile} + args = append(args, test.args...) + + rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) + if err != nil { + t.Errorf("failed to clean up: args %q: %v", rr.Command(), err) + } + if rr.Stdout.Len() > 0 { + t.Logf("(dbg) Stdout: %s:\n%s", rr.Command(), rr.Stdout) + } + if rr.Stderr.Len() > 0 { + t.Logf("(dbg) Stderr: %s:\n%s", rr.Command(), rr.Stderr) + } + }) + } +} diff --git a/test/integration/testdata/build/Dockerfile b/test/integration/testdata/build/Dockerfile new file mode 100644 index 000000000000..dfe60cebfaa8 --- /dev/null +++ b/test/integration/testdata/build/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox +RUN true +ADD content.txt / diff --git a/test/integration/testdata/build/content.txt b/test/integration/testdata/build/content.txt new file mode 100644 index 000000000000..63817ba73c81 --- /dev/null +++ b/test/integration/testdata/build/content.txt @@ -0,0 +1 @@ +Content for image build diff --git a/test/integration/testdata/hello-world.tar b/test/integration/testdata/hello-world.tar new file mode 100644 index 0000000000000000000000000000000000000000..b7c1981484680f12f8c18bd0280a1afe946cc9dd GIT binary patch literal 24576 zcmeHPeQX=Ym0#ImL@SQPpm8f8ZL?t*oYF|-esPx+qK1m8WK__xVo9yficVQBXGLB} zF0oun*12lqqXpcS$Gh~O;($Z%4ruRyUjI21MbFI@v23PRoCAu)xC7kg+6Mh(PWf<~ zHg@7xyf^zH)AEOsO7I2E4y1ka@!p#^zc;hX;SQf1V|Ybo1eIa1l3^8=QPVBeTu>!*}QC^e; z;Nw7orReC!syEM3DI2m5CT&*J`oV2h-wl$NH|zgT5APov+4sox%w<@H5d~qr-`z4G z$FK80%SaMMZ`vo2tv_1MhLtxYE|If=>af2~!u>Lt#6xXePM&iBo zzX7US(|kCX%LE64o1F&1zM!IG*}y~@$8c;v!}l}MagHD0qVN~yc=8P382%t()ry9k zD`Fj()u=4Ruo@L4F`iDxggAUu5O5qbA}`0{Dty>bVi;pl7L};X$Py<;qcKjz@pwjM zSrNCZn+C;dZYDTzIEb{eQIrdKT@S#W)C$nI(+1^a>b+WhK^O2oC4CF~64ISHi{1^;o5SMFk9U_=k(L!GX!(=+HPCj8w{cB(EuQK2lES ziUV!swc@1)tI94yHaOK6+*1JDCxa0vM#@0TOgd-f*j$;X0`EY9b( z$AVKtW~3m`5O~U2nTv`8lB8lr<|P$?#-hN=@EI(|MpaG{Svk%KoXEzaQ7OYkd6j25 zCBtWAk(FW$<|R&vsnE-(wS1+3%fW$SC7d@)c-NN)^gqZ z+i%2+;5k`WvN;1QMnxy{OD<$Y0i2_OXM_1%v2rN5ur>ECPs`>J*zAozl9%VP9wvLJ zYo_Zz|9SDs{U3ZHfc@VM`DFS5|NL*=R<7x}t!W_sm!xa$|9SXO(7ykNeIg4ryyyR| zLI3(%K0lm#Xj@0e8q+rFlN2e92NG7DeWj?sYg>#7Dn@lu32GO0tHpbh#pzyL37a+@ z)h?w1HXo_>k*rguoc>&TN2kr_QkRd&S^d#=J4Gk#j6?I=?V?ND;+OIl`?E;M^SzEscwq;_Mcdd&yXGFadK(iV^th% zAoew6{u0%r|MYwqWnl#Lw9$8-M`AC97%O}WgA1yC)KwdFn=dC`4 z>Vsc|Ejp_EzCcL)g0UMdFIn=7OU91Y%iVWANlFce0>5nWM-LKm?) zsN)oRan-oJO@Y7lvf6!T@{;_MgK?0fDv=3P9sETYvdH|ou@lt>-+@B&hp!)RHM6Z& z>y+;{;Om%j`*rCnhoP}ChRk=^rEjN*_>osf0S#3DY>0mI>(KRqL;`dM*dd`Rr(3*7 zLp0G2SagE?LsnffGz$5DCHcj-Dj!C*Z-#m-6MXw_v+7WH@*G%#h$YJ>M=gUGWOAr= z>I)CGjxHHD-yf;`dgV3C=;m+15vcZ^5b;{`E*Ks$;z?peJQ+Gv`4}laWfhNx{t9U2 z7F7LC=q%(YLwlNh=>cPf;rpL~`2a(IF;!27{BM4JIT;FoS=2G|)lSM>hH?*d1suqDs8@UOfv*%+D_8Xqx#H-XIm9vel$lu(x3O&_*Un7z~LgpK& z2DPaAY|rFW>t&b=%Z&?{jF|00)fGRx)cVlMYwXhGD_;RnK-&RiUO=W1>P2-UM5oMT zC^2-5w$Mn0Vtb)S^H-!dW~AdeR38sLJ_G|vnW+#$wa<|m5(@0C1&9W)>rwch4J6IS zLjIxZ#mL<4!{+yg&9g(*6Mhg+f|P${SE>g1@2w+COCVW;%Dpuz)p2^L`gUa2hoB;m zs_g)<0fWCGbD;X{0Wz~MBJ+Xf_n@Nsd~0#(&_tzc+1CM!gW$isCtJ$q zeQe1h{a+)bezcP&Q}X;v=&lA@e9gEQnP<^`!@a0h0a$)KVh)4IL|~-)T|c^Sp||-{ zun*M=)XEu9_&HE9V%`)2nUwhzGG$Yy;y*Y!HT2lfRAXY8J;RlDP4}>8 zQnh`($(rx2PY}+tdlyd|kJp&$>dh5j%KZMy9o5xsMrU=k)A-~H+@?-}KC%Q2o;dG5 z6j=GhkyY|VU?qrZrQW3ZT@-(P_QUAND(PY98y%?o;WtOjb1QJeIyurhjkZ5bul)V+ z(KAM8()9f$^obVF7{jRcAdR+v3ZT06TcpnP{W~GG2YVs=O*_k=W#5NM+&AdV~Z3BslL-WcMB*UhXVOt6V-O_ zCH3_|)gng&=Si)(`%cn9^<|jQu&#VNNNF$&xX*rk=; zo@QNSCBbz260BWoi65ApY9OU`?3WhH)8t!OWWLbtTY1}>h(}t~xmIiT*5*EFIRBE_ zgR1`t3eHZ#a=8HZ`t{q-FO%DgvGc2_^}L;jyRG>mI`PW(H_<;=f6{6AzS?@eauFm} zk8gt;&9Q&5`f6lQ?WTJm3nN*&zp?(gNE*E4A>bk4A>bk4A>bk4A@Ik5Ko=rM0%=v( z3Utysa(7qPcs5t2;UHfb4hYiatTtDsjjU|Y^IAoxb4B>UL_RMYa4L_MOC>Dp(5%sE zOwZW|{%A$hbvXW&pAUC+jcfD_hLfIfG!&Fv(%a|s2BO*%(1K}I14HKEz#v^VaH%}d zMYCag92#u1XeFP+MFW(>*--@!np#3mTSmqOtr+g2xywZCYPtmH(j5Mvb>5{PEQMo- zbdDT>w8u^?aFn4+8Z<%bwT`ac+ePzN)9RderDeK^=WMgckyck7;AN|y%jT3UtyhW! zMQka?hpG0l% zwj@3z7fN}+m*~o6uncNvbLE`TCJmD1cJUc(^pl?k!At{HR3w9EBjkXiv*>SkXvbgd zr#cP=I_}un>3<5gJf!}k@c)BXTP?Enp@wz^9^XZFPhRp6@DT72@DT72@DT72@DT72 z@DT72xG@MMZcEr5xpaeWdlC*m1uy=>936Gnf(Le|;;Zhzlwp6-Y-oUtb(R>T= zkQYHFTsya}YOa_#c5yAxJh z>~5Pyu*uqWJ$?^D%P#nLyK#SK;r^~+J-=Ia=_4T9-6l`o=aX~}Ui_2f^4XdA8k}hl@Z!y7T8vfhmsYJqVBEUj&e}7?@uIxVx4ej#odg^iHce;WOb?tVm zi;y98uKfw8|BxepXw9*<1Z|VGYkS{om5_1e*Do=`?zt|{FjE%LoY+$4pLC1@rbF~OM?;#+Q{o3^E!FJ%K zy`kw4+i3Fe1~HzPC?~|DT%5CFJlWx(rqdwl1}38W71{|(v}fPwpVDeBj|Zf*%5t)t zmZAzD6D679cop;MSUMxZ!->xbyez1SB0wx6UQswnNh>O&swyv~MTiK+!HEPqLI)T6 zHZt$>5LDMUkG>*eQeO}kO2+)O74IZCVCWUR08y0MqT2KYbxku=62K^E%$G>)8;iLN z!EZ7-Rh`adpl#B!W!&KLM z8M?;{2 Date: Thu, 22 Apr 2021 07:57:52 +0200 Subject: [PATCH 23/28] Make sure to start buildkit socket for containerd --- test/integration/image_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/integration/image_test.go b/test/integration/image_test.go index 4bd6ceab1968..202726637254 100644 --- a/test/integration/image_test.go +++ b/test/integration/image_test.go @@ -37,6 +37,17 @@ func TestImage(t *testing.T) { t.Fatalf("starting minikube: %v\n%s", err, rr.Output()) } + if ContainerRuntime() == "containerd" { + // sudo systemctl start buildkit.socket + cmd := exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "nohup", + "sudo", "-b", "buildkitd", "--oci-worker=false", + "--containerd-worker=true", "--containerd-worker-namespace=k8s.io") + if rr, err = Run(t, cmd); err != nil { + t.Fatalf("%s failed: %v", rr.Command(), err) + } + // unix:///run/buildkit/buildkitd.sock + } + tests := []struct { command string args []string From c9a0a7a8aab7458a46999020d7c03c2c3e9e78db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 23 Apr 2021 17:40:19 +0200 Subject: [PATCH 24/28] Move the BuildImage test over to functional test --- test/integration/functional_test.go | 49 ++++++++++++ test/integration/image_test.go | 87 ---------------------- test/integration/testdata/hello-world.tar | Bin 24576 -> 0 bytes 3 files changed, 49 insertions(+), 87 deletions(-) delete mode 100644 test/integration/image_test.go delete mode 100644 test/integration/testdata/hello-world.tar diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index d032315570c5..3158cdf42319 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -134,6 +134,7 @@ func TestFunctional(t *testing.T) { {"NodeLabels", validateNodeLabels}, {"LoadImage", validateLoadImage}, {"RemoveImage", validateRemoveImage}, + {"BuildImage", validateBuildImage}, } for _, tc := range tests { tc := tc @@ -294,6 +295,54 @@ func inspectImage(ctx context.Context, t *testing.T, profile string, image strin return rr, nil } +// validateBuildImage makes sures that `minikube image build` works as expected +func validateBuildImage(ctx context.Context, t *testing.T, profile string) { + if NoneDriver() { + t.Skip("load image not available on none driver") + } + if GithubActionRunner() && runtime.GOOS == "darwin" { + t.Skip("skipping on github actions and darwin, as this test requires a running docker daemon") + } + defer PostMortemLogs(t, profile) + + newImage := "my-image" + if ContainerRuntime() == "containerd" { + startBuildkit(ctx, t, profile) + // unix:///run/buildkit/buildkitd.sock + } + + // try to build the new image with minikube + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "image", "build", "-t", newImage, filepath.Join(*testdataDir, "build"))) + if err != nil { + t.Fatalf("building image with minikube: %v\n%s", err, rr.Output()) + } + if rr.Stdout.Len() > 0 { + t.Logf("(dbg) Stdout: %s:\n%s", rr.Command(), rr.Stdout) + } + if rr.Stderr.Len() > 0 { + t.Logf("(dbg) Stderr: %s:\n%s", rr.Command(), rr.Stderr) + } + + // make sure the image was correctly built + rr, err = inspectImage(ctx, t, profile, newImage) + if err != nil { + t.Fatalf("listing images: %v\n%s", err, rr.Output()) + } + if !strings.Contains(rr.Output(), newImage) { + t.Fatalf("expected %s to be built with minikube but the image is not there", newImage) + } +} + +func startBuildkit(ctx context.Context, t *testing.T, profile string) { + // sudo systemctl start buildkit.socket + cmd := exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "nohup", + "sudo", "-b", "buildkitd", "--oci-worker=false", + "--containerd-worker=true", "--containerd-worker-namespace=k8s.io") + if rr, err := Run(t, cmd); err != nil { + t.Fatalf("%s failed: %v", rr.Command(), err) + } +} + // check functionality of minikube after evaling docker-env // TODO: Add validatePodmanEnv for crio runtime: #10231 func validateDockerEnv(ctx context.Context, t *testing.T, profile string) { diff --git a/test/integration/image_test.go b/test/integration/image_test.go deleted file mode 100644 index 202726637254..000000000000 --- a/test/integration/image_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// +build integration - -/* -Copyright 2021 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package integration - -import ( - "context" - "os/exec" - "path/filepath" - "testing" -) - -// TestImage tests minikube image -func TestImage(t *testing.T) { - profile := UniqueProfileName("image") - ctx, cancel := context.WithTimeout(context.Background(), Minutes(5)) - defer CleanupWithLogs(t, profile, cancel) - - args := append([]string{"start", "-p", profile, "--memory=2048"}, StartArgs()...) - rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) - if err != nil { - t.Fatalf("starting minikube: %v\n%s", err, rr.Output()) - } - - if ContainerRuntime() == "containerd" { - // sudo systemctl start buildkit.socket - cmd := exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "nohup", - "sudo", "-b", "buildkitd", "--oci-worker=false", - "--containerd-worker=true", "--containerd-worker-namespace=k8s.io") - if rr, err = Run(t, cmd); err != nil { - t.Fatalf("%s failed: %v", rr.Command(), err) - } - // unix:///run/buildkit/buildkitd.sock - } - - tests := []struct { - command string - args []string - }{ - { - command: "image", - args: []string{"load", filepath.Join(*testdataDir, "hello-world.tar")}, - }, { - command: "image", - args: []string{"rm", "hello-world"}, - }, { - command: "image", - args: []string{"build", "-t", "my-image", filepath.Join(*testdataDir, "build")}, - }, { - command: "image", - args: []string{"list"}, - }, - } - - for _, test := range tests { - t.Run(test.command, func(t *testing.T) { - args := []string{test.command, "-p", profile} - args = append(args, test.args...) - - rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) - if err != nil { - t.Errorf("failed to clean up: args %q: %v", rr.Command(), err) - } - if rr.Stdout.Len() > 0 { - t.Logf("(dbg) Stdout: %s:\n%s", rr.Command(), rr.Stdout) - } - if rr.Stderr.Len() > 0 { - t.Logf("(dbg) Stderr: %s:\n%s", rr.Command(), rr.Stderr) - } - }) - } -} diff --git a/test/integration/testdata/hello-world.tar b/test/integration/testdata/hello-world.tar deleted file mode 100644 index b7c1981484680f12f8c18bd0280a1afe946cc9dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 zcmeHPeQX=Ym0#ImL@SQPpm8f8ZL?t*oYF|-esPx+qK1m8WK__xVo9yficVQBXGLB} zF0oun*12lqqXpcS$Gh~O;($Z%4ruRyUjI21MbFI@v23PRoCAu)xC7kg+6Mh(PWf<~ zHg@7xyf^zH)AEOsO7I2E4y1ka@!p#^zc;hX;SQf1V|Ybo1eIa1l3^8=QPVBeTu>!*}QC^e; z;Nw7orReC!syEM3DI2m5CT&*J`oV2h-wl$NH|zgT5APov+4sox%w<@H5d~qr-`z4G z$FK80%SaMMZ`vo2tv_1MhLtxYE|If=>af2~!u>Lt#6xXePM&iBo zzX7US(|kCX%LE64o1F&1zM!IG*}y~@$8c;v!}l}MagHD0qVN~yc=8P382%t()ry9k zD`Fj()u=4Ruo@L4F`iDxggAUu5O5qbA}`0{Dty>bVi;pl7L};X$Py<;qcKjz@pwjM zSrNCZn+C;dZYDTzIEb{eQIrdKT@S#W)C$nI(+1^a>b+WhK^O2oC4CF~64ISHi{1^;o5SMFk9U_=k(L!GX!(=+HPCj8w{cB(EuQK2lES ziUV!swc@1)tI94yHaOK6+*1JDCxa0vM#@0TOgd-f*j$;X0`EY9b( z$AVKtW~3m`5O~U2nTv`8lB8lr<|P$?#-hN=@EI(|MpaG{Svk%KoXEzaQ7OYkd6j25 zCBtWAk(FW$<|R&vsnE-(wS1+3%fW$SC7d@)c-NN)^gqZ z+i%2+;5k`WvN;1QMnxy{OD<$Y0i2_OXM_1%v2rN5ur>ECPs`>J*zAozl9%VP9wvLJ zYo_Zz|9SDs{U3ZHfc@VM`DFS5|NL*=R<7x}t!W_sm!xa$|9SXO(7ykNeIg4ryyyR| zLI3(%K0lm#Xj@0e8q+rFlN2e92NG7DeWj?sYg>#7Dn@lu32GO0tHpbh#pzyL37a+@ z)h?w1HXo_>k*rguoc>&TN2kr_QkRd&S^d#=J4Gk#j6?I=?V?ND;+OIl`?E;M^SzEscwq;_Mcdd&yXGFadK(iV^th% zAoew6{u0%r|MYwqWnl#Lw9$8-M`AC97%O}WgA1yC)KwdFn=dC`4 z>Vsc|Ejp_EzCcL)g0UMdFIn=7OU91Y%iVWANlFce0>5nWM-LKm?) zsN)oRan-oJO@Y7lvf6!T@{;_MgK?0fDv=3P9sETYvdH|ou@lt>-+@B&hp!)RHM6Z& z>y+;{;Om%j`*rCnhoP}ChRk=^rEjN*_>osf0S#3DY>0mI>(KRqL;`dM*dd`Rr(3*7 zLp0G2SagE?LsnffGz$5DCHcj-Dj!C*Z-#m-6MXw_v+7WH@*G%#h$YJ>M=gUGWOAr= z>I)CGjxHHD-yf;`dgV3C=;m+15vcZ^5b;{`E*Ks$;z?peJQ+Gv`4}laWfhNx{t9U2 z7F7LC=q%(YLwlNh=>cPf;rpL~`2a(IF;!27{BM4JIT;FoS=2G|)lSM>hH?*d1suqDs8@UOfv*%+D_8Xqx#H-XIm9vel$lu(x3O&_*Un7z~LgpK& z2DPaAY|rFW>t&b=%Z&?{jF|00)fGRx)cVlMYwXhGD_;RnK-&RiUO=W1>P2-UM5oMT zC^2-5w$Mn0Vtb)S^H-!dW~AdeR38sLJ_G|vnW+#$wa<|m5(@0C1&9W)>rwch4J6IS zLjIxZ#mL<4!{+yg&9g(*6Mhg+f|P${SE>g1@2w+COCVW;%Dpuz)p2^L`gUa2hoB;m zs_g)<0fWCGbD;X{0Wz~MBJ+Xf_n@Nsd~0#(&_tzc+1CM!gW$isCtJ$q zeQe1h{a+)bezcP&Q}X;v=&lA@e9gEQnP<^`!@a0h0a$)KVh)4IL|~-)T|c^Sp||-{ zun*M=)XEu9_&HE9V%`)2nUwhzGG$Yy;y*Y!HT2lfRAXY8J;RlDP4}>8 zQnh`($(rx2PY}+tdlyd|kJp&$>dh5j%KZMy9o5xsMrU=k)A-~H+@?-}KC%Q2o;dG5 z6j=GhkyY|VU?qrZrQW3ZT@-(P_QUAND(PY98y%?o;WtOjb1QJeIyurhjkZ5bul)V+ z(KAM8()9f$^obVF7{jRcAdR+v3ZT06TcpnP{W~GG2YVs=O*_k=W#5NM+&AdV~Z3BslL-WcMB*UhXVOt6V-O_ zCH3_|)gng&=Si)(`%cn9^<|jQu&#VNNNF$&xX*rk=; zo@QNSCBbz260BWoi65ApY9OU`?3WhH)8t!OWWLbtTY1}>h(}t~xmIiT*5*EFIRBE_ zgR1`t3eHZ#a=8HZ`t{q-FO%DgvGc2_^}L;jyRG>mI`PW(H_<;=f6{6AzS?@eauFm} zk8gt;&9Q&5`f6lQ?WTJm3nN*&zp?(gNE*E4A>bk4A>bk4A>bk4A@Ik5Ko=rM0%=v( z3Utysa(7qPcs5t2;UHfb4hYiatTtDsjjU|Y^IAoxb4B>UL_RMYa4L_MOC>Dp(5%sE zOwZW|{%A$hbvXW&pAUC+jcfD_hLfIfG!&Fv(%a|s2BO*%(1K}I14HKEz#v^VaH%}d zMYCag92#u1XeFP+MFW(>*--@!np#3mTSmqOtr+g2xywZCYPtmH(j5Mvb>5{PEQMo- zbdDT>w8u^?aFn4+8Z<%bwT`ac+ePzN)9RderDeK^=WMgckyck7;AN|y%jT3UtyhW! zMQka?hpG0l% zwj@3z7fN}+m*~o6uncNvbLE`TCJmD1cJUc(^pl?k!At{HR3w9EBjkXiv*>SkXvbgd zr#cP=I_}un>3<5gJf!}k@c)BXTP?Enp@wz^9^XZFPhRp6@DT72@DT72@DT72@DT72 z@DT72xG@MMZcEr5xpaeWdlC*m1uy=>936Gnf(Le|;;Zhzlwp6-Y-oUtb(R>T= zkQYHFTsya}YOa_#c5yAxJh z>~5Pyu*uqWJ$?^D%P#nLyK#SK;r^~+J-=Ia=_4T9-6l`o=aX~}Ui_2f^4XdA8k}hl@Z!y7T8vfhmsYJqVBEUj&e}7?@uIxVx4ej#odg^iHce;WOb?tVm zi;y98uKfw8|BxepXw9*<1Z|VGYkS{om5_1e*Do=`?zt|{FjE%LoY+$4pLC1@rbF~OM?;#+Q{o3^E!FJ%K zy`kw4+i3Fe1~HzPC?~|DT%5CFJlWx(rqdwl1}38W71{|(v}fPwpVDeBj|Zf*%5t)t zmZAzD6D679cop;MSUMxZ!->xbyez1SB0wx6UQswnNh>O&swyv~MTiK+!HEPqLI)T6 zHZt$>5LDMUkG>*eQeO}kO2+)O74IZCVCWUR08y0MqT2KYbxku=62K^E%$G>)8;iLN z!EZ7-Rh`adpl#B!W!&KLM z8M?;{2 Date: Fri, 23 Apr 2021 22:00:17 +0200 Subject: [PATCH 25/28] Use canonical names for the functional test images And add some cleanup and improved logging as well --- test/integration/functional_test.go | 42 ++++++++++++++++++----------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 3158cdf42319..3eab64f84dc4 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -157,12 +157,19 @@ func cleanupUnwantedImages(ctx context.Context, t *testing.T, profile string) { t.Skipf("docker is not installed, cannot delete docker images") } else { t.Run("delete busybox image", func(t *testing.T) { - newImage := fmt.Sprintf("busybox:%s", profile) + newImage := fmt.Sprintf("docker.io/library/busybox:%s", profile) rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", "-f", newImage)) if err != nil { t.Logf("failed to remove image busybox from docker images. args %q: %v", rr.Command(), err) } }) + t.Run("delete my-image image", func(t *testing.T) { + newImage := fmt.Sprintf("localhost/my-image:%s", profile) + rr, err := Run(t, exec.CommandContext(ctx, "docker", "rmi", "-f", newImage)) + if err != nil { + t.Logf("failed to remove image my-image from docker images. args %q: %v", rr.Command(), err) + } + }) t.Run("delete minikube cached images", func(t *testing.T) { img := "minikube-local-cache-test:" + profile @@ -208,7 +215,7 @@ func validateLoadImage(ctx context.Context, t *testing.T, profile string) { } // tag busybox - newImage := fmt.Sprintf("busybox:%s", profile) + newImage := fmt.Sprintf("docker.io/library/busybox:%s", profile) rr, err = Run(t, exec.CommandContext(ctx, "docker", "tag", busybox, newImage)) if err != nil { t.Fatalf("failed to setup test (tag image) : %v\n%s", err, rr.Output()) @@ -260,13 +267,7 @@ func validateRemoveImage(ctx context.Context, t *testing.T, profile string) { t.Fatalf("removing image from minikube: %v\n%s", err, rr.Output()) } // make sure the image was removed - var cmd *exec.Cmd - if ContainerRuntime() == "docker" { - cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "docker", "images") - } else { - cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "sudo", "crictl", "images") - } - rr, err = Run(t, cmd) + rr, err = listImages(ctx, t, profile) if err != nil { t.Fatalf("listing images: %v\n%s", err, rr.Output()) } @@ -280,13 +281,22 @@ func inspectImage(ctx context.Context, t *testing.T, profile string, image strin var cmd *exec.Cmd if ContainerRuntime() == "docker" { cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "docker", "image", "inspect", image) - } else if ContainerRuntime() == "containerd" { - // crictl inspecti busybox:test-example + } else { cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "sudo", "crictl", "inspecti", image) + } + rr, err := Run(t, cmd) + if err != nil { + return rr, err + } + return rr, nil +} + +func listImages(ctx context.Context, t *testing.T, profile string) (*RunResult, error) { + var cmd *exec.Cmd + if ContainerRuntime() == "docker" { + cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "docker", "images") } else { - // crio adds localhost prefix - // crictl inspecti localhost/busybox:test-example - cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "sudo", "crictl", "inspecti", "localhost/"+image) + cmd = exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "--", "sudo", "crictl", "images") } rr, err := Run(t, cmd) if err != nil { @@ -305,7 +315,7 @@ func validateBuildImage(ctx context.Context, t *testing.T, profile string) { } defer PostMortemLogs(t, profile) - newImage := "my-image" + newImage := fmt.Sprintf("localhost/my-image:%s", profile) if ContainerRuntime() == "containerd" { startBuildkit(ctx, t, profile) // unix:///run/buildkit/buildkitd.sock @@ -326,6 +336,8 @@ func validateBuildImage(ctx context.Context, t *testing.T, profile string) { // make sure the image was correctly built rr, err = inspectImage(ctx, t, profile, newImage) if err != nil { + ll, _ := listImages(ctx, t, profile) + t.Logf("(dbg) images: %s", ll.Output()) t.Fatalf("listing images: %v\n%s", err, rr.Output()) } if !strings.Contains(rr.Output(), newImage) { From d50845903fd43fd19f5cc9f209d7d6274c65e7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 23 Apr 2021 22:55:06 +0200 Subject: [PATCH 26/28] url.Parse treats DOS volumes as URL schemes So need to be on the lookout for "C:\\path" --- cmd/minikube/cmd/image.go | 8 +++++++- pkg/minikube/machine/build_images.go | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/minikube/cmd/image.go b/cmd/minikube/cmd/image.go index 12a02b8125ac..fe9d364fe72c 100644 --- a/cmd/minikube/cmd/image.go +++ b/cmd/minikube/cmd/image.go @@ -21,6 +21,8 @@ import ( "io/ioutil" "net/url" "os" + "path/filepath" + "runtime" "strings" "github.com/spf13/cobra" @@ -197,7 +199,11 @@ var buildImageCmd = &cobra.Command{ } else { // If it is an URL, pass it as-is u, err := url.Parse(img) - if err == nil && u.Scheme == "" && u.Host == "" { + local := err == nil && u.Scheme == "" && u.Host == "" + if runtime.GOOS == "windows" && filepath.VolumeName(img) != "" { + local = true + } + if local { // If it's a directory, tar it info, err := os.Stat(img) if err == nil && info.IsDir() { diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index d0f4e52f7417..df113c459964 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -22,6 +22,7 @@ import ( "os/exec" "path" "path/filepath" + "runtime" "strings" "github.com/docker/machine/libmachine/state" @@ -51,6 +52,9 @@ func BuildImage(path string, file string, tag string, push bool, env []string, o u, err := url.Parse(path) remote := err == nil && u.Scheme != "" + if runtime.GOOS == "windows" && filepath.VolumeName(path) != "" { + remote = false + } for _, p := range profiles { // building images to all running profiles pName := p.Name // capture the loop variable From 96e199c442bcb2780b6ed631d2ee688f5be68e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 23 Apr 2021 23:15:32 +0200 Subject: [PATCH 27/28] Support building a path local to the machine Using file: scheme means build local context --- pkg/minikube/machine/build_images.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/minikube/machine/build_images.go b/pkg/minikube/machine/build_images.go index df113c459964..ed39b217792c 100644 --- a/pkg/minikube/machine/build_images.go +++ b/pkg/minikube/machine/build_images.go @@ -51,6 +51,9 @@ func BuildImage(path string, file string, tag string, push bool, env []string, o failed := []string{} u, err := url.Parse(path) + if err == nil && u.Scheme == "file" { + path = u.Path + } remote := err == nil && u.Scheme != "" if runtime.GOOS == "windows" && filepath.VolumeName(path) != "" { remote = false From 4d0c0c6b252fb6a73019162a686257f0866c9d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 23 Apr 2021 23:42:39 +0200 Subject: [PATCH 28/28] The docker container runtime doesn't show name It hides the docker.io and the "library" parts --- test/integration/functional_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 3eab64f84dc4..d63b545db150 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -232,7 +232,7 @@ func validateLoadImage(ctx context.Context, t *testing.T, profile string) { if err != nil { t.Fatalf("listing images: %v\n%s", err, rr.Output()) } - if !strings.Contains(rr.Output(), newImage) { + if !strings.Contains(rr.Output(), fmt.Sprintf("busybox:%s", profile)) { t.Fatalf("expected %s to be loaded into minikube but the image is not there", newImage) }