From 8ad5bd189d27f85b69aea0a0052dc3e6568c2d9f Mon Sep 17 00:00:00 2001 From: Zeeshan Ali Date: Mon, 13 Jan 2020 10:25:43 +0530 Subject: [PATCH] WIP: Embed podman (remote) binary in crc binary Fixes #961. --- cmd/crc-embedder/cmd/embed.go | 1 + pkg/crc/constants/constants.go | 15 ++- pkg/crc/constants/constants_darwin.go | 5 +- pkg/crc/constants/constants_linux.go | 3 +- pkg/crc/constants/constants_windows.go | 3 +- pkg/crc/podman/podman_cache.go | 118 +++++++++++++++++++ pkg/crc/preflight/preflight_checks_common.go | 26 ++++ 7 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 pkg/crc/podman/podman_cache.go diff --git a/cmd/crc-embedder/cmd/embed.go b/cmd/crc-embedder/cmd/embed.go index 752b44510b..c98df6161f 100644 --- a/cmd/crc-embedder/cmd/embed.go +++ b/cmd/crc-embedder/cmd/embed.go @@ -91,6 +91,7 @@ var ( hyperkit.HyperkitDownloadUrl, constants.GetOcUrlForOs("darwin"), constants.GetCrcTrayDownloadURL(), + constants.GetPodmanUrl(), }, "linux": []string{ libvirt.MachineDriverDownloadUrl, diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go index a091bb08bf..c26fa1bf63 100644 --- a/pkg/crc/constants/constants.go +++ b/pkg/crc/constants/constants.go @@ -30,8 +30,9 @@ const ( CrcLandingPageURL = "https://cloud.redhat.com/openshift/install/crc/installer-provisioned" // #nosec G101 PullSecretFile = "pullsecret.json" - DefaultOcUrlBase = "https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest" - CrcTrayDownloadURL = "https://github.com/code-ready/tray-macos/releases/download/v%s/crc-tray-macos.tar.gz" + DefaultOcUrlBase = "https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest" + DefaultPodmanUrlBase = "https://storage.googleapis.com/libpod-master-releases" + CrcTrayDownloadURL = "https://github.com/code-ready/tray-macos/releases/download/v%s/crc-tray-macos.tar.gz" ) var ocUrlForOs = map[string]string{ @@ -48,6 +49,16 @@ func GetOcUrl() string { return GetOcUrlForOs(runtime.GOOS) } +var PodmanUrlForOs = map[string]string{ + "darwin": fmt.Sprintf("%s/%s", DefaultPodmanUrlBase, "podman-remote-latest-master-darwin-amd64.zip"), + "linux": fmt.Sprintf("%s/%s", DefaultPodmanUrlBase, "podman-remote-latest-master-linux---amd64.zip"), + "windows": fmt.Sprintf("%s/%s", DefaultPodmanUrlBase, "podman-remote-latest-master-windows-amd64.msi"), +} + +func GetPodmanUrl() string { + return PodmanUrlForOs[runtime.GOOS] +} + var defaultBundleForOs = map[string]string{ "darwin": fmt.Sprintf("crc_hyperkit_%s.crcbundle", version.GetBundleVersion()), "linux": fmt.Sprintf("crc_libvirt_%s.crcbundle", version.GetBundleVersion()), diff --git a/pkg/crc/constants/constants_darwin.go b/pkg/crc/constants/constants_darwin.go index ba4640d17f..647727dd52 100644 --- a/pkg/crc/constants/constants_darwin.go +++ b/pkg/crc/constants/constants_darwin.go @@ -3,8 +3,9 @@ package constants import "path/filepath" const ( - OcBinaryName = "oc" - TrayBinaryName = "CodeReady Containers.app" + OcBinaryName = "oc" + PodmanBinaryName = "podman" + TrayBinaryName = "CodeReady Containers.app" ) var ( diff --git a/pkg/crc/constants/constants_linux.go b/pkg/crc/constants/constants_linux.go index 807f214da6..2598e3478b 100644 --- a/pkg/crc/constants/constants_linux.go +++ b/pkg/crc/constants/constants_linux.go @@ -1,5 +1,6 @@ package constants const ( - OcBinaryName = "oc" + OcBinaryName = "oc" + PodmanBinaryName = "podman" ) diff --git a/pkg/crc/constants/constants_windows.go b/pkg/crc/constants/constants_windows.go index 18815cfe70..8b26073d5d 100644 --- a/pkg/crc/constants/constants_windows.go +++ b/pkg/crc/constants/constants_windows.go @@ -1,5 +1,6 @@ package constants const ( - OcBinaryName = "oc.exe" + OcBinaryName = "oc.exe" + PodmanBinaryName = "podman.exe" ) diff --git a/pkg/crc/podman/podman_cache.go b/pkg/crc/podman/podman_cache.go new file mode 100644 index 0000000000..026454c362 --- /dev/null +++ b/pkg/crc/podman/podman_cache.go @@ -0,0 +1,118 @@ +package podman + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/code-ready/crc/pkg/crc/constants" + "github.com/code-ready/crc/pkg/crc/logging" + "github.com/code-ready/crc/pkg/download" + "github.com/code-ready/crc/pkg/embed" + "github.com/code-ready/crc/pkg/extract" + crcos "github.com/code-ready/crc/pkg/os" + "github.com/pkg/errors" +) + +// Podman is a struct with methods designed for dealing with the podman binary +type PodmanCached struct{} + +func (podman *PodmanCached) EnsureIsCached() error { + if !podman.IsCached() { + err := podman.cachePodman() + if err != nil { + return err + } + + } + return nil +} + +func (podman *PodmanCached) IsCached() bool { + if _, err := os.Stat(filepath.Join(constants.CrcBinDir, constants.PodmanBinaryName)); os.IsNotExist(err) { + return false + } + return true +} + +func (podman *PodmanCached) getPodman(destDir string) (string, error) { + logging.Debug("Trying to extract podman from crc binary") + archiveName := filepath.Base(constants.GetPodmanUrl()) + destPath := filepath.Join(destDir, archiveName) + err := embed.Extract(archiveName, destPath) + if err != nil { + logging.Debug("Downloading podman") + return download.Download(constants.GetPodmanUrl(), destDir, 0600) + } + + return destPath, err +} + +// cachePodman downloads and caches the podman binary into the minishift directory +func (podman *PodmanCached) cachePodman() error { + if podman.IsCached() { + return nil + } + + // Create tmp dir to download the podman tarball + tmpDir, err := ioutil.TempDir("", "crc") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + assetTmpFile, err := podman.getPodman(tmpDir) + if err != nil { + return err + } + + // Extract the tarball and put it the cache directory. + err = extract.Uncompress(assetTmpFile, tmpDir) + if err != nil { + return errors.Wrapf(err, "Cannot uncompress '%s'", assetTmpFile) + } + + binaryName := constants.PodmanBinaryName + binaryPath, err := findBinary(tmpDir, binaryName) + if err != nil { + return err + } + + // Copy the requested asset into its final destination + outputPath := constants.CrcBinDir + err = os.MkdirAll(outputPath, 0750) + if err != nil && !os.IsExist(err) { + return errors.Wrap(err, "Cannot create the target directory.") + } + + finalBinaryPath := filepath.Join(outputPath, binaryName) + err = crcos.CopyFileContents(binaryPath, finalBinaryPath, 0500) + if err != nil { + return err + } + + return nil +} + +func findBinary(dirPath string, binaryName string) (string, error) { + var binaryPath string = "" + + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Base(path) == binaryName { + binaryPath = path + + return filepath.SkipDir + } + + return nil + }) + if binaryPath == "" && err == nil { + err = fmt.Errorf("Failed to find `{}` binary in `{}`.", binaryName, dirPath) + } + + return binaryPath, err +} diff --git a/pkg/crc/preflight/preflight_checks_common.go b/pkg/crc/preflight/preflight_checks_common.go index 7ac1665820..8bd205f413 100644 --- a/pkg/crc/preflight/preflight_checks_common.go +++ b/pkg/crc/preflight/preflight_checks_common.go @@ -9,6 +9,7 @@ import ( "github.com/code-ready/crc/pkg/crc/constants" "github.com/code-ready/crc/pkg/crc/logging" "github.com/code-ready/crc/pkg/crc/oc" + "github.com/code-ready/crc/pkg/crc/podman" "github.com/code-ready/crc/pkg/embed" ) @@ -19,6 +20,12 @@ var genericPreflightChecks = [...]PreflightCheck{ fixDescription: "Caching oc binary", fix: fixOcBinaryCached, }, + { + checkDescription: "Checking if podman binary is cached", + check: checkPodmanBinaryCached, + fixDescription: "Caching podman binary", + fix: fixPodmanBinaryCached, + }, { configKeySuffix: "check-bundle-cached", checkDescription: "Checking if CRC bundle is cached in '$HOME/.crc'", @@ -70,3 +77,22 @@ func fixOcBinaryCached() error { logging.Debug("oc binary cached") return nil } + +// Check if podman binary is cached or not +func checkPodmanBinaryCached() error { + podman := podman.PodmanCached{} + if !podman.IsCached() { + return errors.New("podman binary is not cached") + } + logging.Debug("podman binary already cached") + return nil +} + +func fixPodmanBinaryCached() error { + podman := podman.PodmanCached{} + if err := podman.EnsureIsCached(); err != nil { + return fmt.Errorf("Unable to download podman %v", err) + } + logging.Debug("podman binary cached") + return nil +}