Repository: code-ready/crc
Pull request #1381 (2020-07-13 05:05:38)

Update bundle from 4.5.0-rc.7 to 4.5.1

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

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1381#" title="Author self-approved">praveenkumar</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)~~ [praveenkumar]

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/Makefile b/Makefile
index 6de6b9e02..8fc90ff48 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-BUNDLE_VERSION = 4.5.0-rc.7
+BUNDLE_VERSION = 4.5.1
 # OC_VERSION and BUNDLE_VERSION are going to same for release artifacts but
 # different for nightly and CI bits where bundle version would be any random
 # string or dd-mm-yyyy format.
diff --git a/centos_ci.sh b/centos_ci.sh
index 254670dbb..6b90fe0dd 100644
--- a/centos_ci.sh
+++ b/centos_ci.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # bundle location
-BUNDLE_VERSION=4.5.0-rc.7
+BUNDLE_VERSION=4.5.1
 BUNDLE=crc_libvirt_$BUNDLE_VERSION.crcbundle
 GO_VERSION=1.13.4
 
diff --git a/go.mod b/go.mod
index 1f5ad91c4..12cc2c754 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,6 @@ require (
 	github.com/docker/go-units v0.4.0
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/mattn/go-colorable v0.1.2
-	github.com/mattn/go-isatty v0.0.12
 	github.com/pborman/uuid v1.2.0
 	github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
 	github.com/pkg/errors v0.8.1
diff --git a/go.sum b/go.sum
index add303630..aa6da42cc 100644
--- a/go.sum
+++ b/go.sum
@@ -67,13 +67,10 @@ github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRU
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
 github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
diff --git a/pkg/crc/machine/cert-renewal.go b/pkg/crc/cluster/cert-renewal.go
similarity index 82%
rename from pkg/crc/machine/cert-renewal.go
rename to pkg/crc/cluster/cert-renewal.go
index 8aa5ef9f0..2d7ad2c44 100644
--- a/pkg/crc/machine/cert-renewal.go
+++ b/pkg/crc/cluster/cert-renewal.go
@@ -1,4 +1,4 @@
-package machine
+package cluster
 
 import (
 	"fmt"
@@ -12,9 +12,9 @@ import (
 	"github.com/code-ready/crc/pkg/crc/systemd"
 )
 
-func waitForPendingCsrs(oc oc.OcConfig) error {
+func waitForPendingCsrs(ocConfig oc.OcConfig) error {
 	waitForPendingCsr := func() error {
-		output, _, err := oc.RunOcCommand("get", "csr")
+		output, _, err := ocConfig.RunOcCommand("get", "csr")
 		if err != nil {
 			return &errors.RetriableError{Err: err}
 		}
@@ -31,7 +31,7 @@ func waitForPendingCsrs(oc oc.OcConfig) error {
 	return errors.RetryAfter(120, waitForPendingCsr, time.Second*5)
 }
 
-func RegenerateCertificates(sshRunner *ssh.SSHRunner, machineName string) error {
+func RegenerateCertificates(sshRunner *ssh.SSHRunner, ocConfig oc.OcConfig) error {
 	sd := systemd.NewInstanceSystemdCommander(sshRunner)
 	startedKubelet, err := sd.Start("kubelet")
 	if err != nil {
@@ -41,28 +41,27 @@ func RegenerateCertificates(sshRunner *ssh.SSHRunner, machineName string) error
 	if startedKubelet {
 		defer sd.Stop("kubelet") //nolint:errcheck
 	}
-	oc := oc.UseOCWithConfig(machineName)
 	/* 2 CSRs to approve, one right after kubelet restart, the other one a few dozen seconds after
 	approving the first one
 	- First one is requested by system:serviceaccount:openshift-machine-config-operator:node-bootstrapper
 	- Second one is requested by system:node:<node_name> */
-	err = waitForPendingCsrs(oc)
+	err = waitForPendingCsrs(ocConfig)
 	if err != nil {
 		logging.Debugf("Error waiting for first pending (node-bootstrapper) CSR: %v", err)
 		return err
 	}
-	err = oc.ApproveNodeCSR()
+	err = ApproveNodeCSR(ocConfig)
 	if err != nil {
 		logging.Debugf("Error approving first pending (node-bootstrapper) CSR: %v", err)
 		return err
 	}
 
-	err = waitForPendingCsrs(oc)
+	err = waitForPendingCsrs(ocConfig)
 	if err != nil {
 		logging.Debugf("Error waiting for second pending (system:node) CSR: %v", err)
 		return err
 	}
-	err = oc.ApproveNodeCSR()
+	err = ApproveNodeCSR(ocConfig)
 	if err != nil {
 		logging.Debugf("Error approving second pending (system:node) CSR: %v", err)
 		return err
diff --git a/pkg/crc/cluster/cluster.go b/pkg/crc/cluster/cluster.go
index 24c37f5e3..a2d036d1c 100644
--- a/pkg/crc/cluster/cluster.go
+++ b/pkg/crc/cluster/cluster.go
@@ -83,7 +83,7 @@ func GetRootPartitionUsage(sshRunner *ssh.SSHRunner) (int64, int64, error) {
 	return diskSize, diskUsage, nil
 }
 
-func AddPullSecret(sshRunner *ssh.SSHRunner, oc oc.OcConfig, pullSec string) error {
+func AddPullSecret(sshRunner *ssh.SSHRunner, ocConfig oc.OcConfig, pullSec string) error {
 	if err := addPullSecretToInstanceDisk(sshRunner, pullSec); err != nil {
 		return err
 	}
@@ -93,25 +93,25 @@ func AddPullSecret(sshRunner *ssh.SSHRunner, oc oc.OcConfig, pullSec string) err
 		fmt.Sprintf(`{"data":{".dockerconfigjson":"%s"}}`, base64OfPullSec),
 		"-n", "openshift-config", "--type", "merge"}
 
-	if err := oc.WaitForOpenshiftResource("secret"); err != nil {
+	if err := WaitForOpenshiftResource(ocConfig, "secret"); err != nil {
 		return err
 	}
-	_, stderr, err := oc.RunOcCommandPrivate(cmdArgs...)
+	_, stderr, err := ocConfig.RunOcCommandPrivate(cmdArgs...)
 	if err != nil {
 		return fmt.Errorf("Failed to add Pull secret %v: %s", err, stderr)
 	}
 	return nil
 }
 
-func UpdateClusterID(oc oc.OcConfig) error {
+func UpdateClusterID(ocConfig oc.OcConfig) error {
 	clusterID := uuid.New()
 	cmdArgs := []string{"patch", "clusterversion", "version", "-p",
 		fmt.Sprintf(`{"spec":{"clusterID":"%s"}}`, clusterID), "--type", "merge"}
 
-	if err := oc.WaitForOpenshiftResource("clusterversion"); err != nil {
+	if err := WaitForOpenshiftResource(ocConfig, "clusterversion"); err != nil {
 		return err
 	}
-	_, stderr, err := oc.RunOcCommand(cmdArgs...)
+	_, stderr, err := ocConfig.RunOcCommand(cmdArgs...)
 	if err != nil {
 		return fmt.Errorf("Failed to update cluster ID %v: %s", err, stderr)
 	}
@@ -119,15 +119,15 @@ func UpdateClusterID(oc oc.OcConfig) error {
 	return nil
 }
 
-func AddProxyConfigToCluster(oc oc.OcConfig, proxy *network.ProxyConfig) error {
+func AddProxyConfigToCluster(ocConfig oc.OcConfig, proxy *network.ProxyConfig) error {
 	cmdArgs := []string{"patch", "proxy", "cluster", "-p",
 		fmt.Sprintf(`{"spec":{"httpProxy":"%s", "httpsProxy":"%s", "noProxy":"%s"}}`, proxy.HttpProxy, proxy.HttpsProxy, proxy.GetNoProxyString()),
 		"-n", "openshift-config", "--type", "merge"}
 
-	if err := oc.WaitForOpenshiftResource("proxy"); err != nil {
+	if err := WaitForOpenshiftResource(ocConfig, "proxy"); err != nil {
 		return err
 	}
-	if _, stderr, err := oc.RunOcCommand(cmdArgs...); err != nil {
+	if _, stderr, err := ocConfig.RunOcCommand(cmdArgs...); err != nil {
 		return fmt.Errorf("Failed to add proxy details %v: %s", err, stderr)
 	}
 	return nil
@@ -165,8 +165,8 @@ func addPullSecretToInstanceDisk(sshRunner *ssh.SSHRunner, pullSec string) error
 	return nil
 }
 
-func WaitforRequestHeaderClientCaFile(oc oc.OcConfig) error {
-	if err := oc.WaitForOpenshiftResource("configmaps"); err != nil {
+func WaitforRequestHeaderClientCaFile(ocConfig oc.OcConfig) error {
+	if err := WaitForOpenshiftResource(ocConfig, "configmaps"); err != nil {
 		return err
 	}
 
@@ -174,7 +174,7 @@ func WaitforRequestHeaderClientCaFile(oc oc.OcConfig) error {
 		cmdArgs := []string{"get", "configmaps/extension-apiserver-authentication", `-ojsonpath={.data.requestheader-client-ca-file}`,
 			"-n", "kube-system"}
 
-		stdout, stderr, err := oc.RunOcCommand(cmdArgs...)
+		stdout, stderr, err := ocConfig.RunOcCommand(cmdArgs...)
 		if err != nil {
 			return fmt.Errorf("Failed to get request header client ca file %v: %s", err, stderr)
 		}
@@ -187,14 +187,14 @@ func WaitforRequestHeaderClientCaFile(oc oc.OcConfig) error {
 	return errors.RetryAfter(90, lookupRequestHeaderClientCa, 2*time.Second)
 }
 
-func DeleteOpenshiftApiServerPods(oc oc.OcConfig) error {
-	if err := oc.WaitForOpenshiftResource("pod"); err != nil {
+func DeleteOpenshiftApiServerPods(ocConfig oc.OcConfig) error {
+	if err := WaitForOpenshiftResource(ocConfig, "pod"); err != nil {
 		return err
 	}
 
 	deleteOpenshiftApiserverPods := func() error {
 		cmdArgs := []string{"delete", "pod", "--all", "-n", "openshift-apiserver"}
-		_, _, err := oc.RunOcCommand(cmdArgs...)
+		_, _, err := ocConfig.RunOcCommand(cmdArgs...)
 		if err != nil {
 			return &errors.RetriableError{Err: err}
 		}
@@ -204,13 +204,13 @@ func DeleteOpenshiftApiServerPods(oc oc.OcConfig) error {
 	return errors.RetryAfter(60, deleteOpenshiftApiserverPods, time.Second)
 }
 
-func CheckProxySettingsForOperator(oc oc.OcConfig, proxy *network.ProxyConfig, deployment, namespace string) (bool, error) {
+func CheckProxySettingsForOperator(ocConfig oc.OcConfig, proxy *network.ProxyConfig, deployment, namespace string) (bool, error) {
 	if !proxy.IsEnabled() {
 		logging.Debugf("No proxy in use")
 		return true, nil
 	}
 	cmdArgs := []string{"set", "env", "deployment", deployment, "--list", "-n", namespace}
-	out, _, err := oc.RunOcCommand(cmdArgs...)
+	out, _, err := ocConfig.RunOcCommand(cmdArgs...)
 	if err != nil {
 		return false, err
 	}
diff --git a/pkg/crc/oc/clusteroperator.go b/pkg/crc/cluster/clusteroperator.go
similarity index 91%
rename from pkg/crc/oc/clusteroperator.go
rename to pkg/crc/cluster/clusteroperator.go
index 4571ab086..e6ddbd456 100644
--- a/pkg/crc/oc/clusteroperator.go
+++ b/pkg/crc/cluster/clusteroperator.go
@@ -1,4 +1,4 @@
-package oc
+package cluster
 
 import (
 	"encoding/json"
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/code-ready/crc/pkg/crc/oc"
 )
 
 var ignoreClusterOperators = []string{"monitoring", "machine-config", "marketplace", "insights"}
@@ -34,9 +35,9 @@ type ClusterStatus struct {
 	Disabled    bool
 }
 
-func GetClusterOperatorStatus(oc OcConfig) (*ClusterStatus, error) {
+func GetClusterOperatorStatus(ocConfig oc.OcConfig) (*ClusterStatus, error) {
 	cs := &ClusterStatus{}
-	data, stderr, err := oc.RunOcCommand("get", "co", "-ojson")
+	data, stderr, err := ocConfig.RunOcCommand("get", "co", "-ojson")
 	if err != nil {
 		return cs, fmt.Errorf("%s", stderr)
 	}
diff --git a/pkg/crc/cluster/csr.go b/pkg/crc/cluster/csr.go
new file mode 100644
index 000000000..bc2778551
--- /dev/null
+++ b/pkg/crc/cluster/csr.go
@@ -0,0 +1,56 @@
+package cluster
+
+import (
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"github.com/code-ready/crc/pkg/crc/errors"
+	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/code-ready/crc/pkg/crc/oc"
+)
+
+func WaitForOpenshiftResource(ocConfig oc.OcConfig, resource string) error {
+	logging.Debugf("Waiting for availability of resource type '%s'", resource)
+	waitForApiServer := func() error {
+		stdout, stderr, err := ocConfig.RunOcCommand("get", resource)
+		if err != nil {
+			logging.Debug(stderr)
+			return &errors.RetriableError{Err: err}
+		}
+		logging.Debug(stdout)
+		return nil
+	}
+	return errors.RetryAfter(80, waitForApiServer, time.Second)
+}
+
+// ApproveNodeCSR approves the certificate for the node.
+func ApproveNodeCSR(ocConfig oc.OcConfig) error {
+	err := WaitForOpenshiftResource(ocConfig, "csr")
+	if err != nil {
+		return err
+	}
+
+	logging.Debug("Approving pending CSRs")
+	// Execute 'oc get csr -oname' and store the output
+	csrsJson, stderr, err := ocConfig.RunOcCommand("get", "csr", "-ojson")
+	if err != nil {
+		return fmt.Errorf("Not able to get csr names (%v : %s)", err, stderr)
+	}
+	var csrs K8sResource
+	err = json.Unmarshal([]byte(csrsJson), &csrs)
+	if err != nil {
+		return err
+	}
+	for _, csr := range csrs.Items {
+		/* When the CSR hasn't been approved, csr.status is empty in the json data */
+		if len(csr.Status.Conditions) == 0 {
+			logging.Debugf("Approving csr %s", csr.Metadata.Name)
+			_, stderr, err := ocConfig.RunOcCommand("adm", "certificate", "approve", csr.Metadata.Name)
+			if err != nil {
+				return fmt.Errorf("Not able to approve csr (%v : %s)", err, stderr)
+			}
+		}
+	}
+	return nil
+}
diff --git a/pkg/crc/machine/machine.go b/pkg/crc/machine/machine.go
index d0568e99e..69708b3c6 100644
--- a/pkg/crc/machine/machine.go
+++ b/pkg/crc/machine/machine.go
@@ -357,9 +357,10 @@ func Start(startConfig StartConfig) (StartResult, error) {
 		}
 	}
 
+	ocConfig := oc.UseOCWithConfig(startConfig.Name)
 	if needsCertsRenewal {
 		logging.Info("Cluster TLS certificates have expired, renewing them... [will take up to 5 minutes]")
-		err = RegenerateCertificates(sshRunner, startConfig.Name)
+		err = cluster.RegenerateCertificates(sshRunner, ocConfig)
 		if err != nil {
 			logging.Debugf("Failed to renew TLS certificates: %v", err)
 			buildTime, getBuildTimeErr := crcBundleMetadata.GetBundleBuildTime()
@@ -381,7 +382,6 @@ func Start(startConfig StartConfig) (StartResult, error) {
 		result.Error = err.Error()
 		return *result, errors.Newf("Error starting kubelet: %s", err)
 	}
-	ocConfig := oc.UseOCWithConfig(startConfig.Name)
 	if !exists {
 		logging.Info("Configuring cluster for first start")
 		if err := configureCluster(ocConfig, sshRunner, proxyConfig, pullSecret, instanceIP); err != nil {
@@ -427,7 +427,7 @@ func Start(startConfig StartConfig) (StartResult, error) {
 	time.Sleep(time.Minute * 3)
 
 	// Approve the node certificate.
-	if err := ocConfig.ApproveNodeCSR(); err != nil {
+	if err := cluster.ApproveNodeCSR(ocConfig); err != nil {
 		result.Error = err.Error()
 		return *result, errors.Newf("Error approving the node csr %v", err)
 	}
@@ -602,7 +602,7 @@ func Status(statusConfig ClusterStatusConfig) (ClusterStatusResult, error) {
 
 		// check if all the clusteroperators are running
 		ocConfig := oc.UseOCWithConfig(statusConfig.Name)
-		operatorsStatus, err := oc.GetClusterOperatorStatus(ocConfig)
+		operatorsStatus, err := cluster.GetClusterOperatorStatus(ocConfig)
 		if err != nil {
 			openshiftStatus = "Not Reachable"
 			logging.Debug(err.Error())
diff --git a/pkg/crc/oc/oc.go b/pkg/crc/oc/oc.go
index 77be772aa..4b624ac89 100644
--- a/pkg/crc/oc/oc.go
+++ b/pkg/crc/oc/oc.go
@@ -1,14 +1,9 @@
 package oc
 
 import (
-	"encoding/json"
-	"fmt"
 	"path/filepath"
-	"time"
 
 	"github.com/code-ready/crc/pkg/crc/constants"
-	"github.com/code-ready/crc/pkg/crc/errors"
-	"github.com/code-ready/crc/pkg/crc/logging"
 	crcos "github.com/code-ready/crc/pkg/os"
 )
 
@@ -19,7 +14,7 @@ type OcRunner interface {
 }
 
 type OcConfig struct {
-	runner  OcRunner
+	Runner  OcRunner
 	Context string
 	Cluster string
 }
@@ -51,64 +46,19 @@ func UseOCWithConfig(machineName string) OcConfig {
 }
 
 func (oc OcConfig) RunOcCommand(args ...string) (string, string, error) {
-	args = append(args, "--kubeconfig", oc.runner.GetKubeconfigPath(), "--context", oc.Context, "--cluster", oc.Cluster)
-	return oc.runner.Run(args...)
+	args = append(args, "--kubeconfig", oc.Runner.GetKubeconfigPath(), "--context", oc.Context, "--cluster", oc.Cluster)
+	return oc.Runner.Run(args...)
 }
 
 func (oc OcConfig) RunOcCommandPrivate(args ...string) (string, string, error) {
-	args = append(args, "--kubeconfig", oc.runner.GetKubeconfigPath(), "--context", oc.Context, "--cluster", oc.Cluster)
-	return oc.runner.RunPrivate(args...)
+	args = append(args, "--kubeconfig", oc.Runner.GetKubeconfigPath(), "--context", oc.Context, "--cluster", oc.Cluster)
+	return oc.Runner.RunPrivate(args...)
 }
 
 func NewOcConfig(runner OcRunner, context string, clusterName string) OcConfig {
 	return OcConfig{
-		runner:  runner,
+		Runner:  runner,
 		Context: context,
 		Cluster: clusterName,
 	}
 }
-
-func (oc OcConfig) WaitForOpenshiftResource(resource string) error {
-	logging.Debugf("Waiting for availability of resource type '%s'", resource)
-	waitForApiServer := func() error {
-		stdout, stderr, err := oc.RunOcCommand("get", resource)
-		if err != nil {
-			logging.Debug(stderr)
-			return &errors.RetriableError{Err: err}
-		}
-		logging.Debug(stdout)
-		return nil
-	}
-	return errors.RetryAfter(80, waitForApiServer, time.Second)
-}
-
-// ApproveNodeCSR approves the certificate for the node.
-func (oc OcConfig) ApproveNodeCSR() error {
-	err := oc.WaitForOpenshiftResource("csr")
-	if err != nil {
-		return err
-	}
-
-	logging.Debug("Approving pending CSRs")
-	// Execute 'oc get csr -oname' and store the output
-	csrsJson, stderr, err := oc.RunOcCommand("get", "csr", "-ojson")
-	if err != nil {
-		return fmt.Errorf("Not able to get csr names (%v : %s)", err, stderr)
-	}
-	var csrs K8sResource
-	err = json.Unmarshal([]byte(csrsJson), &csrs)
-	if err != nil {
-		return err
-	}
-	for _, csr := range csrs.Items {
-		/* When the CSR hasn't been approved, csr.status is empty in the json data */
-		if len(csr.Status.Conditions) == 0 {
-			logging.Debugf("Approving csr %s", csr.Metadata.Name)
-			_, stderr, err := oc.RunOcCommand("adm", "certificate", "approve", csr.Metadata.Name)
-			if err != nil {
-				return fmt.Errorf("Not able to approve csr (%v : %s)", err, stderr)
-			}
-		}
-	}
-	return nil
-}
diff --git a/pkg/extract/extract.go b/pkg/extract/extract.go
index 223ca1618..46e64c5b4 100644
--- a/pkg/extract/extract.go
+++ b/pkg/extract/extract.go
@@ -12,18 +12,18 @@ import (
 
 	"github.com/cheggaaa/pb/v3"
 	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/mattn/go-isatty"
 	"github.com/xi2/xz"
+	"golang.org/x/crypto/ssh/terminal"
 )
 
 const minSizeForProgressBar = 100_000_000
 
 func UncompressWithFilter(tarball, targetDir string, showProgress bool, fileFilter func(string) bool) ([]string, error) {
-	return uncompress(tarball, targetDir, fileFilter, showProgress && isatty.IsTerminal(os.Stdout.Fd()))
+	return uncompress(tarball, targetDir, fileFilter, showProgress && terminal.IsTerminal(int(os.Stdout.Fd())))
 }
 
 func Uncompress(tarball, targetDir string, showProgress bool) ([]string, error) {
-	return uncompress(tarball, targetDir, nil, showProgress && isatty.IsTerminal(os.Stdout.Fd()))
+	return uncompress(tarball, targetDir, nil, showProgress && terminal.IsTerminal(int(os.Stdout.Fd())))
 }
 
 func uncompress(tarball, targetDir string, fileFilter func(string) bool, showProgress bool) ([]string, error) {
diff --git a/vendor/github.com/mattn/go-isatty/.travis.yml b/vendor/github.com/mattn/go-isatty/.travis.yml
index 604314dd4..5597e026d 100644
--- a/vendor/github.com/mattn/go-isatty/.travis.yml
+++ b/vendor/github.com/mattn/go-isatty/.travis.yml
@@ -1,14 +1,13 @@
 language: go
-sudo: false
 go:
-  - 1.13.x
   - tip
 
-before_install:
-  - go get -t -v ./...
+os:
+  - linux
+  - osx
 
+before_install:
+  - go get github.com/mattn/goveralls
+  - go get golang.org/x/tools/cmd/cover
 script:
-  - ./go.test.sh
-
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
+  - $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5
diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md
index 38418353e..1e69004bb 100644
--- a/vendor/github.com/mattn/go-isatty/README.md
+++ b/vendor/github.com/mattn/go-isatty/README.md
@@ -1,7 +1,7 @@
 # go-isatty
 
 [![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty)
-[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty)
+[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty)
 [![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master)
 [![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty)
 
diff --git a/vendor/github.com/mattn/go-isatty/go.mod b/vendor/github.com/mattn/go-isatty/go.mod
index 605c4c221..a8ddf404f 100644
--- a/vendor/github.com/mattn/go-isatty/go.mod
+++ b/vendor/github.com/mattn/go-isatty/go.mod
@@ -1,5 +1,5 @@
 module github.com/mattn/go-isatty
 
-go 1.12
+require golang.org/x/sys v0.0.0-20191008105621-543471e840be
 
-require golang.org/x/sys v0.0.0-20200116001909-b77594299b42
+go 1.14
diff --git a/vendor/github.com/mattn/go-isatty/go.sum b/vendor/github.com/mattn/go-isatty/go.sum
index 912e29cbc..c141fc53a 100644
--- a/vendor/github.com/mattn/go-isatty/go.sum
+++ b/vendor/github.com/mattn/go-isatty/go.sum
@@ -1,2 +1,4 @@
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/vendor/github.com/mattn/go-isatty/isatty_android.go b/vendor/github.com/mattn/go-isatty/isatty_android.go
new file mode 100644
index 000000000..d3567cb5b
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_android.go
@@ -0,0 +1,23 @@
+// +build android
+
+package isatty
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+const ioctlReadTermios = syscall.TCGETS
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+	var termios syscall.Termios
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+	return err == 0
+}
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+	return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go
index 711f28808..07e93039d 100644
--- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go
+++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go
@@ -3,12 +3,18 @@
 
 package isatty
 
-import "golang.org/x/sys/unix"
+import (
+	"syscall"
+	"unsafe"
+)
+
+const ioctlReadTermios = syscall.TIOCGETA
 
 // IsTerminal return true if the file descriptor is terminal.
 func IsTerminal(fd uintptr) bool {
-	_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
-	return err == nil
+	var termios syscall.Termios
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+	return err == 0
 }
 
 // IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go
index c5b6e0c08..bc0a70920 100644
--- a/vendor/github.com/mattn/go-isatty/isatty_plan9.go
+++ b/vendor/github.com/mattn/go-isatty/isatty_plan9.go
@@ -8,7 +8,7 @@ import (
 
 // IsTerminal returns true if the given file descriptor is a terminal.
 func IsTerminal(fd uintptr) bool {
-	path, err := syscall.Fd2path(int(fd))
+	path, err := syscall.Fd2path(fd)
 	if err != nil {
 		return false
 	}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
index 31a1ca973..453b025d0 100644
--- a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
+++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go
@@ -1,5 +1,6 @@
 // +build linux aix
 // +build !appengine
+// +build !android
 
 package isatty
 
diff --git a/vendor/github.com/mattn/go-isatty/renovate.json b/vendor/github.com/mattn/go-isatty/renovate.json
deleted file mode 100644
index 5ae9d96b7..000000000
--- a/vendor/github.com/mattn/go-isatty/renovate.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "extends": [
-    "config:base"
-  ],
-  "postUpdateOptions": [
-    "gomodTidy"
-  ]
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f05e3f59c..521578e53 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -74,7 +74,7 @@ github.com/konsorten/go-windows-terminal-sequences
 github.com/magiconair/properties
 # github.com/mattn/go-colorable v0.1.2
 github.com/mattn/go-colorable
-# github.com/mattn/go-isatty v0.0.12
+# github.com/mattn/go-isatty v0.0.10
 github.com/mattn/go-isatty
 # github.com/mattn/go-runewidth v0.0.7
 github.com/mattn/go-runewidth


Test patch:
diff --git a/pkg/crc/cluster/clusteroperator_test.go b/pkg/crc/cluster/clusteroperator_test.go
new file mode 100644
index 000000000..e33009451
--- /dev/null
+++ b/pkg/crc/cluster/clusteroperator_test.go
@@ -0,0 +1,49 @@
+package cluster
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	"github.com/code-ready/crc/pkg/crc/errors"
+	"github.com/code-ready/crc/pkg/crc/oc"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetClusterOperatorStatus(t *testing.T) {
+	status, err := GetClusterOperatorStatus(oc.OcConfig{
+		Runner: &mockRunner{file: filepath.Join("testdata", "co.json")},
+	})
+	assert.NoError(t, err)
+	assert.Equal(t, &ClusterStatus{
+		Available: true,
+	}, status)
+}
+
+func TestGetClusterOperatorStatusProgressing(t *testing.T) {
+	status, err := GetClusterOperatorStatus(oc.OcConfig{
+		Runner: &mockRunner{file: filepath.Join("testdata", "co-progressing.json")},
+	})
+	assert.NoError(t, err)
+	assert.Equal(t, &ClusterStatus{
+		Available:   true,
+		Progressing: true,
+	}, status)
+}
+
+type mockRunner struct {
+	file string
+}
+
+func (r *mockRunner) Run(args ...string) (string, string, error) {
+	bin, err := ioutil.ReadFile(r.file)
+	return string(bin), "", err
+}
+
+func (r *mockRunner) RunPrivate(args ...string) (string, string, error) {
+	return "", "", errors.New("not implemented")
+}
+
+func (r *mockRunner) GetKubeconfigPath() string {
+	return ""
+}
diff --git a/pkg/crc/cluster/testdata/co-progressing.json b/pkg/crc/cluster/testdata/co-progressing.json
new file mode 100644
index 000000000..1527e7d7a
--- /dev/null
+++ b/pkg/crc/cluster/testdata/co-progressing.json
@@ -0,0 +1,112 @@
+{
+    "apiVersion": "v1",
+    "items": [
+        {
+            "apiVersion": "config.openshift.io/v1",
+            "kind": "ClusterOperator",
+            "metadata": {
+                "name": "authentication"
+            },
+            "status": {
+                "conditions": [
+                    {
+                        "lastTransitionTime": "2020-07-09T13:33:35Z",
+                        "reason": "AsExpected",
+                        "status": "False",
+                        "type": "Degraded"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-21T10:40:40Z",
+                        "reason": "AsExpected",
+                        "status": "True",
+                        "type": "Progressing"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:29:47Z",
+                        "reason": "AsExpected",
+                        "status": "True",
+                        "type": "Available"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:03:21Z",
+                        "reason": "AsExpected",
+                        "status": "True",
+                        "type": "Upgradeable"
+                    }
+                ]
+            }
+        },
+        {
+            "apiVersion": "config.openshift.io/v1",
+            "kind": "ClusterOperator",
+            "metadata": {
+                "name": "cloud-credential"
+            },
+            "status": {
+                "conditions": [
+                    {
+                        "lastTransitionTime": "2020-06-20T06:58:30Z",
+                        "message": "No credentials requests reporting errors.",
+                        "reason": "NoCredentialsFailing",
+                        "status": "False",
+                        "type": "Degraded"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:21:19Z",
+                        "message": "0 of 0 credentials requests provisioned and reconciled.",
+                        "reason": "ReconcilingComplete",
+                        "status": "False",
+                        "type": "Progressing"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T06:58:30Z",
+                        "status": "True",
+                        "type": "Available"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T06:58:30Z",
+                        "status": "True",
+                        "type": "Upgradeable"
+                    }
+                ]
+            }
+        },
+        {
+            "apiVersion": "config.openshift.io/v1",
+            "kind": "ClusterOperator",
+            "metadata": {
+                "name": "cluster-autoscaler"
+            },
+            "status": {
+                "conditions": [
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "message": "at version 4.4.8",
+                        "status": "True",
+                        "type": "Available"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "status": "False",
+                        "type": "Progressing"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "status": "False",
+                        "type": "Degraded"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "status": "True",
+                        "type": "Upgradeable"
+                    }
+                ]
+            }
+        }
+    ],
+    "kind": "List",
+    "metadata": {
+        "resourceVersion": "",
+        "selfLink": ""
+    }
+}
diff --git a/pkg/crc/cluster/testdata/co.json b/pkg/crc/cluster/testdata/co.json
new file mode 100644
index 000000000..bf3935fcf
--- /dev/null
+++ b/pkg/crc/cluster/testdata/co.json
@@ -0,0 +1,112 @@
+{
+    "apiVersion": "v1",
+    "items": [
+        {
+            "apiVersion": "config.openshift.io/v1",
+            "kind": "ClusterOperator",
+            "metadata": {
+                "name": "authentication"
+            },
+            "status": {
+                "conditions": [
+                    {
+                        "lastTransitionTime": "2020-07-09T13:33:35Z",
+                        "reason": "AsExpected",
+                        "status": "False",
+                        "type": "Degraded"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-21T10:40:40Z",
+                        "reason": "AsExpected",
+                        "status": "False",
+                        "type": "Progressing"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:29:47Z",
+                        "reason": "AsExpected",
+                        "status": "True",
+                        "type": "Available"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:03:21Z",
+                        "reason": "AsExpected",
+                        "status": "True",
+                        "type": "Upgradeable"
+                    }
+                ]
+            }
+        },
+        {
+            "apiVersion": "config.openshift.io/v1",
+            "kind": "ClusterOperator",
+            "metadata": {
+                "name": "cloud-credential"
+            },
+            "status": {
+                "conditions": [
+                    {
+                        "lastTransitionTime": "2020-06-20T06:58:30Z",
+                        "message": "No credentials requests reporting errors.",
+                        "reason": "NoCredentialsFailing",
+                        "status": "False",
+                        "type": "Degraded"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:21:19Z",
+                        "message": "0 of 0 credentials requests provisioned and reconciled.",
+                        "reason": "ReconcilingComplete",
+                        "status": "False",
+                        "type": "Progressing"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T06:58:30Z",
+                        "status": "True",
+                        "type": "Available"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T06:58:30Z",
+                        "status": "True",
+                        "type": "Upgradeable"
+                    }
+                ]
+            }
+        },
+        {
+            "apiVersion": "config.openshift.io/v1",
+            "kind": "ClusterOperator",
+            "metadata": {
+                "name": "cluster-autoscaler"
+            },
+            "status": {
+                "conditions": [
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "message": "at version 4.4.8",
+                        "status": "True",
+                        "type": "Available"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "status": "False",
+                        "type": "Progressing"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "status": "False",
+                        "type": "Degraded"
+                    },
+                    {
+                        "lastTransitionTime": "2020-06-20T07:26:09Z",
+                        "status": "True",
+                        "type": "Upgradeable"
+                    }
+                ]
+            }
+        }
+    ],
+    "kind": "List",
+    "metadata": {
+        "resourceVersion": "",
+        "selfLink": ""
+    }
+}
diff --git a/test/integration/crcsuite/crcsuite.go b/test/integration/crcsuite/crcsuite.go
index 33d1e8072..543dc867c 100644
--- a/test/integration/crcsuite/crcsuite.go
+++ b/test/integration/crcsuite/crcsuite.go
@@ -20,8 +20,6 @@ package crcsuite
 import (
 	"crypto/tls"
 	"fmt"
-	"github.com/DATA-DOG/godog"
-	"github.com/DATA-DOG/godog/gherkin"
 	"io/ioutil"
 	"net/http"
 	"os"
@@ -31,6 +29,10 @@ import (
 	"strings"
 	"time"
 
+	"github.com/DATA-DOG/godog"
+	"github.com/DATA-DOG/godog/gherkin"
+	"github.com/code-ready/crc/pkg/crc/cluster"
+
 	clicumber "github.com/code-ready/clicumber/testsuite"
 	"github.com/code-ready/crc/pkg/crc/oc"
 )
@@ -182,7 +184,7 @@ func CheckClusterOperatorsWithRetry(retryCount int, retryWait string) error {
 
 	ocConfig := oc.UseOCWithConfig("crc")
 	for i := 0; i < retryCount; i++ {
-		s, err := oc.GetClusterOperatorStatus(ocConfig)
+		s, err := cluster.GetClusterOperatorStatus(ocConfig)
 		if err != nil {
 			return err
 		}
diff --git a/vendor/github.com/mattn/go-isatty/go.test.sh b/vendor/github.com/mattn/go-isatty/go.test.sh
deleted file mode 100644
index 012162b07..000000000
--- a/vendor/github.com/mattn/go-isatty/go.test.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-echo "" > coverage.txt
-
-for d in $(go list ./... | grep -v vendor); do
-    go test -race -coverprofile=profile.out -covermode=atomic "$d"
-    if [ -f profile.out ]; then
-        cat profile.out >> coverage.txt
-        rm profile.out
-    fi
-done