Repository: code-ready/crc
Pull request #1730 (2020-11-30 10:17:02)

Add unit tests to bundle package
Before doing a larger refactoring, add unit tests.

openshift-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1730#" title="Author self-approved">guillaumerose</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=code-ready%2Fcrc).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/code-ready/crc/blob/master/OWNERS)~~ [guillaumerose]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/docs/source/topics/proc_connecting-to-remote-instance.adoc b/docs/source/topics/proc_connecting-to-remote-instance.adoc
index be31d4c82..f00122ede 100644
--- a/docs/source/topics/proc_connecting-to-remote-instance.adoc
+++ b/docs/source/topics/proc_connecting-to-remote-instance.adoc
@@ -63,4 +63,4 @@ $ sudo systemctl reload NetworkManager
 $ oc login -u developer -p developer https://api.crc.testing:6443
 ----
 +
-The remote OpenShift Web Console is available at https://console-openshift-console.apps-crc.testing.
+The remote OpenShift Web Console is available at \https://console-openshift-console.apps-crc.testing.
diff --git a/pkg/crc/machine/bundle/metadata.go b/pkg/crc/machine/bundle/metadata.go
index 4d25908b7..d09bfe46c 100644
--- a/pkg/crc/machine/bundle/metadata.go
+++ b/pkg/crc/machine/bundle/metadata.go
@@ -1,124 +1,72 @@
 package bundle
 
 import (
-	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strconv"
 	"strings"
 	"time"
-
-	"github.com/code-ready/crc/pkg/crc/constants"
-	"github.com/code-ready/crc/pkg/extract"
-	crcos "github.com/code-ready/crc/pkg/os"
 )
 
 // Metadata structure to unmarshal the crc-bundle-info.json file
 
 type CrcBundleInfo struct {
-	Version   string `json:"version"`
-	Type      string `json:"type"`
-	BuildInfo struct {
-		BuildTime                 string `json:"buildTime"`
-		OpenshiftInstallerVersion string `json:"openshiftInstallerVersion"`
-		SncVersion                string `json:"sncVersion"`
-	} `json:"buildInfo"`
-	ClusterInfo struct {
-		OpenShiftVersion      string `json:"openshiftVersion"`
-		ClusterName           string `json:"clusterName"`
-		BaseDomain            string `json:"baseDomain"`
-		AppsDomain            string `json:"appsDomain"`
-		SSHPrivateKeyFile     string `json:"sshPrivateKeyFile"`
-		KubeConfig            string `json:"kubeConfig"`
-		KubeadminPasswordFile string `json:"kubeadminPasswordFile"`
-	} `json:"clusterInfo"`
-	Nodes []struct {
-		Kind          []string `json:"kind"`
-		Hostname      string   `json:"hostname"`
-		DiskImage     string   `json:"diskImage"`
-		KernelCmdLine string   `json:"kernelCmdLine"`
-		Initramfs     string   `json:"initramfs"`
-		Kernel        string   `json:"kernel"`
-		InternalIP    string   `json:"internalIP"`
-	} `json:"nodes"`
-	Storage struct {
-		DiskImages []struct {
-			Name   string `json:"name"`
-			Format string `json:"format"`
-			Size   string `json:"size"`
-		} `json:"diskImages"`
-	} `json:"storage"`
-	cachedPath string
-}
-
-func getCachedBundlePath(bundleName string) string {
-	path := strings.TrimSuffix(bundleName, ".crcbundle")
-	return filepath.Join(constants.MachineCacheDir, path)
-}
-
-func (bundle *CrcBundleInfo) isCached() bool {
-	_, err := os.Stat(bundle.cachedPath)
-	return err == nil
-}
-
-func (bundle *CrcBundleInfo) readBundleInfo() error {
-	bundleInfoPath := bundle.resolvePath("crc-bundle-info.json")
-	f, err := ioutil.ReadFile(filepath.Clean(bundleInfoPath))
-	if err != nil {
-		return fmt.Errorf("Error reading %s file : %+v", bundleInfoPath, err)
-	}
+	Version     string      `json:"version"`
+	Type        string      `json:"type"`
+	BuildInfo   BuildInfo   `json:"buildInfo"`
+	ClusterInfo ClusterInfo `json:"clusterInfo"`
+	Nodes       []Node      `json:"nodes"`
+	Storage     Storage     `json:"storage"`
+	DriverInfo  DriverInfo  `json:"driverInfo"`
+	cachedPath  string
+}
 
-	err = json.Unmarshal(f, bundle)
-	if err != nil {
-		return fmt.Errorf("Error Unmarshal the data: %+v", err)
-	}
+type BuildInfo struct {
+	BuildTime                 string `json:"buildTime"`
+	OpenshiftInstallerVersion string `json:"openshiftInstallerVersion"`
+	SncVersion                string `json:"sncVersion"`
+}
 
-	return nil
+type ClusterInfo struct {
+	OpenShiftVersion      string `json:"openshiftVersion"`
+	ClusterName           string `json:"clusterName"`
+	BaseDomain            string `json:"baseDomain"`
+	AppsDomain            string `json:"appsDomain"`
+	SSHPrivateKeyFile     string `json:"sshPrivateKeyFile"`
+	KubeConfig            string `json:"kubeConfig"`
+	KubeadminPasswordFile string `json:"kubeadminPasswordFile"`
+	OpenshiftPullSecret   string `json:"openshiftPullSecret,omitempty"`
 }
 
-func GetCachedBundleInfo(bundleName string) (*CrcBundleInfo, error) {
-	var bundleInfo CrcBundleInfo
-	bundleInfo.cachedPath = getCachedBundlePath(bundleName)
-	if !bundleInfo.isCached() {
-		return nil, fmt.Errorf("Could not find cached bundle info")
-	}
-	err := bundleInfo.readBundleInfo()
-	if err != nil {
-		return nil, err
-	}
-	if err := bundleInfo.installSymlinkOrCopy(); err != nil {
-		return nil, err
-	}
-	return &bundleInfo, nil
+type Node struct {
+	Kind          []string `json:"kind"`
+	Hostname      string   `json:"hostname"`
+	DiskImage     string   `json:"diskImage"`
+	KernelCmdLine string   `json:"kernelCmdLine,omitempty"`
+	Initramfs     string   `json:"initramfs,omitempty"`
+	Kernel        string   `json:"kernel,omitempty"`
+	InternalIP    string   `json:"internalIP"`
 }
 
-func (bundle *CrcBundleInfo) installSymlinkOrCopy() error {
-	ocInBundle := filepath.Join(bundle.cachedPath, constants.OcExecutableName)
-	ocInBinDir := filepath.Join(constants.CrcOcBinDir, constants.OcExecutableName)
-	if err := os.MkdirAll(constants.CrcOcBinDir, 0750); err != nil {
-		return err
-	}
-	_ = os.Remove(ocInBinDir)
-	if runtime.GOOS == "windows" {
-		return crcos.CopyFileContents(ocInBundle, ocInBinDir, 0750)
-	}
-	return os.Symlink(ocInBundle, ocInBinDir)
+type Storage struct {
+	DiskImages []DiskImage `json:"diskImages"`
 }
 
-func (bundle *CrcBundleInfo) resolvePath(filename string) string {
-	return filepath.Join(bundle.cachedPath, filename)
+type DiskImage struct {
+	Name     string `json:"name"`
+	Format   string `json:"format"`
+	Size     string `json:"size"`
+	Checksum string `json:"sha256sum"`
 }
 
-func Extract(sourcepath string) (*CrcBundleInfo, error) {
-	_, err := extract.Uncompress(sourcepath, constants.MachineCacheDir, true)
-	if err != nil {
-		return nil, err
-	}
+type DriverInfo struct {
+	Name string `json:"name"`
+}
 
-	return GetCachedBundleInfo(filepath.Base(sourcepath))
+func (bundle *CrcBundleInfo) resolvePath(filename string) string {
+	return filepath.Join(bundle.cachedPath, filename)
 }
 
 func (bundle *CrcBundleInfo) GetAPIHostname() string {
@@ -155,13 +103,6 @@ func (bundle *CrcBundleInfo) GetInitramfsPath() string {
 	return bundle.resolvePath(bundle.Nodes[0].Initramfs)
 }
 
-func (bundle *CrcBundleInfo) GetInternalIP() string {
-	if bundle.Nodes[0].InternalIP == "" {
-		return ""
-	}
-	return bundle.resolvePath(bundle.Nodes[0].InternalIP)
-}
-
 func (bundle *CrcBundleInfo) GetKubeadminPassword() (string, error) {
 	rawData, err := ioutil.ReadFile(bundle.resolvePath(bundle.ClusterInfo.KubeadminPasswordFile))
 	return string(rawData), err
@@ -175,17 +116,9 @@ func (bundle *CrcBundleInfo) GetOpenshiftVersion() string {
 	return bundle.ClusterInfo.OpenShiftVersion
 }
 
-func (bundle *CrcBundleInfo) GetDiskSize() (int64, error) {
-	size, err := strconv.ParseInt(bundle.Storage.DiskImages[0].Size, 10, 64)
-	if err != nil {
-		return 0, err
-	}
-	return size, nil
-}
-
 func (bundle *CrcBundleInfo) CheckDiskImageSize() error {
 	diskImagePath := bundle.GetDiskImagePath()
-	expectedSize, err := bundle.GetDiskSize()
+	expectedSize, err := strconv.ParseInt(bundle.Storage.DiskImages[0].Size, 10, 64)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/crc/machine/bundle/repository.go b/pkg/crc/machine/bundle/repository.go
new file mode 100644
index 000000000..599847e25
--- /dev/null
+++ b/pkg/crc/machine/bundle/repository.go
@@ -0,0 +1,103 @@
+package bundle
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+
+	"github.com/code-ready/crc/pkg/crc/constants"
+	"github.com/code-ready/crc/pkg/extract"
+	crcos "github.com/code-ready/crc/pkg/os"
+)
+
+func getCachedBundlePath(cacheDir, bundleName string) string {
+	path := strings.TrimSuffix(bundleName, ".crcbundle")
+	return filepath.Join(cacheDir, path)
+}
+
+func (bundle *CrcBundleInfo) isCached() bool {
+	_, err := os.Stat(bundle.cachedPath)
+	return err == nil
+}
+
+func (bundle *CrcBundleInfo) readBundleInfo() error {
+	bundleInfoPath := bundle.resolvePath("crc-bundle-info.json")
+	f, err := ioutil.ReadFile(filepath.Clean(bundleInfoPath))
+	if err != nil {
+		return fmt.Errorf("Error reading %s file : %+v", bundleInfoPath, err)
+	}
+
+	err = json.Unmarshal(f, bundle)
+	if err != nil {
+		return fmt.Errorf("Error Unmarshal the data: %+v", err)
+	}
+
+	return nil
+}
+
+type Repository struct {
+	CacheDir string
+	OcBinDir string
+}
+
+func (repo *Repository) Get(bundleName string) (*CrcBundleInfo, error) {
+	var bundleInfo CrcBundleInfo
+	bundleInfo.cachedPath = getCachedBundlePath(repo.CacheDir, bundleName)
+	if !bundleInfo.isCached() {
+		return nil, fmt.Errorf("could not find cached bundle info in %s", bundleInfo.cachedPath)
+	}
+	err := bundleInfo.readBundleInfo()
+	if err != nil {
+		return nil, err
+	}
+	return &bundleInfo, nil
+}
+
+func (repo *Repository) Use(bundleName string) (*CrcBundleInfo, error) {
+	bundleInfo, err := repo.Get(bundleName)
+	if err != nil {
+		return nil, err
+	}
+	if err := bundleInfo.createSymlinkOrCopyOpenShiftClient(repo.OcBinDir); err != nil {
+		return nil, err
+	}
+	return bundleInfo, nil
+}
+
+func (bundle *CrcBundleInfo) createSymlinkOrCopyOpenShiftClient(ocBinDir string) error {
+	ocInBundle := filepath.Join(bundle.cachedPath, constants.OcExecutableName)
+	ocInBinDir := filepath.Join(ocBinDir, constants.OcExecutableName)
+	if err := os.MkdirAll(ocBinDir, 0750); err != nil {
+		return err
+	}
+	_ = os.Remove(ocInBinDir)
+	if runtime.GOOS == "windows" {
+		return crcos.CopyFileContents(ocInBundle, ocInBinDir, 0750)
+	}
+	return os.Symlink(ocInBundle, ocInBinDir)
+}
+
+func (repo *Repository) Extract(path string) error {
+	_, err := extract.Uncompress(path, repo.CacheDir, true)
+	return err
+}
+
+var defaultRepo = &Repository{
+	CacheDir: constants.MachineCacheDir,
+	OcBinDir: constants.CrcOcBinDir,
+}
+
+func GetCachedBundleInfo(bundleName string) (*CrcBundleInfo, error) {
+	return defaultRepo.Use(bundleName)
+}
+
+func Extract(path string) (*CrcBundleInfo, error) {
+	if err := defaultRepo.Extract(path); err != nil {
+		return nil, err
+	}
+	return defaultRepo.Use(filepath.Base(path))
+}


Test patch:
diff --git a/pkg/crc/machine/bundle/metadata_test.go b/pkg/crc/machine/bundle/metadata_test.go
new file mode 100644
index 000000000..208341377
--- /dev/null
+++ b/pkg/crc/machine/bundle/metadata_test.go
@@ -0,0 +1,97 @@
+package bundle
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+const reference = `{
+  "version": "1.0",
+  "type": "snc",
+  "buildInfo": {
+    "buildTime": "2020-10-26T04:48:26+00:00",
+    "openshiftInstallerVersion": "./openshift-install v4.6.0\nbuilt from commit ebdbda57fc18d3b73e69f0f2cc499ddfca7e6593\nrelease image registry.svc.ci.openshift.org/origin/release:4.5",
+    "sncVersion": "git4.1.14-137-g14e7"
+  },
+  "clusterInfo": {
+    "openshiftVersion": "4.6.1",
+    "clusterName": "crc",
+    "baseDomain": "testing",
+    "appsDomain": "apps-crc.testing",
+    "sshPrivateKeyFile": "id_rsa_crc",
+    "kubeConfig": "kubeconfig",
+    "kubeadminPasswordFile": "kubeadmin-password"
+  },
+  "nodes": [
+    {
+      "kind": [
+        "master",
+        "worker"
+      ],
+      "hostname": "crc-h66l2-master-0",
+      "diskImage": "crc.qcow2",
+      "internalIP": "192.168.126.11"
+    }
+  ],
+  "storage": {
+    "diskImages": [
+      {
+        "name": "crc.qcow2",
+        "format": "qcow2",
+        "size": "12268273664",
+        "sha256sum": "245a0e5acd4f09000a9a5f37d731082ed1cf3fdcad1b5320cbe9b153c9fd82a4"
+      }
+    ]
+  },
+  "driverInfo": {
+    "name": "libvirt"
+  }
+}`
+
+func TestUnmarshalMarshal(t *testing.T) {
+	var bundle CrcBundleInfo
+	assert.NoError(t, json.Unmarshal([]byte(reference), &bundle))
+	assert.Equal(t, CrcBundleInfo{
+		Version: "1.0",
+		Type:    "snc",
+		BuildInfo: BuildInfo{
+			BuildTime:                 "2020-10-26T04:48:26+00:00",
+			OpenshiftInstallerVersion: "./openshift-install v4.6.0\nbuilt from commit ebdbda57fc18d3b73e69f0f2cc499ddfca7e6593\nrelease image registry.svc.ci.openshift.org/origin/release:4.5",
+			SncVersion:                "git4.1.14-137-g14e7",
+		},
+		ClusterInfo: ClusterInfo{
+			OpenShiftVersion:      "4.6.1",
+			ClusterName:           "crc",
+			BaseDomain:            "testing",
+			AppsDomain:            "apps-crc.testing",
+			SSHPrivateKeyFile:     "id_rsa_crc",
+			KubeConfig:            "kubeconfig",
+			KubeadminPasswordFile: "kubeadmin-password",
+		}, Nodes: []Node{
+			{
+				Kind:       []string{"master", "worker"},
+				Hostname:   "crc-h66l2-master-0",
+				DiskImage:  "crc.qcow2",
+				InternalIP: "192.168.126.11",
+			},
+		},
+		Storage: Storage{
+			DiskImages: []DiskImage{
+				{
+					Name:     "crc.qcow2",
+					Format:   "qcow2",
+					Size:     "12268273664",
+					Checksum: "245a0e5acd4f09000a9a5f37d731082ed1cf3fdcad1b5320cbe9b153c9fd82a4",
+				},
+			},
+		},
+		DriverInfo: DriverInfo{
+			Name: "libvirt",
+		},
+		cachedPath: ""}, bundle)
+	bin, err := json.Marshal(bundle)
+	assert.NoError(t, err)
+	assert.JSONEq(t, string(bin), reference)
+}
diff --git a/pkg/crc/machine/bundle/repository_test.go b/pkg/crc/machine/bundle/repository_test.go
new file mode 100644
index 000000000..f9c7430e3
--- /dev/null
+++ b/pkg/crc/machine/bundle/repository_test.go
@@ -0,0 +1,60 @@
+package bundle
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/code-ready/crc/pkg/crc/constants"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestUse(t *testing.T) {
+	dir, err := ioutil.TempDir("", "repo")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	ocBinDir, err := ioutil.TempDir("", "oc-bin-dir")
+	assert.NoError(t, err)
+	defer os.RemoveAll(ocBinDir)
+
+	assert.NoError(t, os.Mkdir(filepath.Join(dir, "crc_libvirt_4.6.1"), 0755))
+	assert.NoError(t, ioutil.WriteFile(filepath.Join(dir, "crc_libvirt_4.6.1", "crc-bundle-info.json"), []byte(reference), 0600))
+	assert.NoError(t, ioutil.WriteFile(filepath.Join(dir, "crc_libvirt_4.6.1", constants.OcExecutableName), []byte("openshift-client"), 0600))
+
+	repo := &Repository{
+		CacheDir: dir,
+		OcBinDir: ocBinDir,
+	}
+
+	bundle, err := repo.Use("crc_libvirt_4.6.1.crcbundle")
+	assert.NoError(t, err)
+	assert.Equal(t, "4.6.1", bundle.ClusterInfo.OpenShiftVersion)
+
+	bin, err := ioutil.ReadFile(filepath.Join(ocBinDir, constants.OcExecutableName))
+	assert.NoError(t, err)
+	assert.Equal(t, "openshift-client", string(bin))
+}
+
+func TestExtract(t *testing.T) {
+	dir, err := ioutil.TempDir("", "repo")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dir)
+
+	ocBinDir, err := ioutil.TempDir("", "oc-bin-dir")
+	assert.NoError(t, err)
+	defer os.RemoveAll(ocBinDir)
+
+	repo := &Repository{
+		CacheDir: dir,
+		OcBinDir: ocBinDir,
+	}
+
+	assert.NoError(t, repo.Extract(filepath.Join("testdata", "crc_libvirt_4.6.1.crcbundle")))
+
+	bundle, err := repo.Get("crc_libvirt_4.6.1.crcbundle")
+	assert.NoError(t, err)
+	assert.Equal(t, "4.6.1", bundle.ClusterInfo.OpenShiftVersion)
+}