Repository: code-ready/crc
Pull request #2702 (2021-09-07 08:04:26)

Use context to eventually cancel waiting loops
When the user hit the stop or delete button while the VM is still starting, it 
allows a fast abort.
The daemon quicky stops the start sequence and can run stop or delete function.

---

I saw this when clicking on start in the tray with an old bundle. I couldnt stop or delete while kubelet was starting.

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

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/2702#" 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":[]} -->
guillaumerose: Yes done!

Patch:
diff --git a/Makefile b/Makefile
index 3157f8ae0..a746ce2a9 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ SHELL := /bin/bash
 
 BUNDLE_VERSION ?= 4.8.5
 BUNDLE_EXTENSION = crcbundle
-CRC_VERSION = 1.32.0
+CRC_VERSION = 1.32.1
 COMMIT_SHA=$(shell git rev-parse --short HEAD)
 MACOS_INSTALL_PATH = /Applications/CodeReady Containers.app/Contents/Resources/
 CONTAINER_RUNTIME ?= podman
diff --git a/pkg/crc/cluster/cert_renewal.go b/pkg/crc/cluster/cert_renewal.go
index a9bd592f6..f1f939581 100644
--- a/pkg/crc/cluster/cert_renewal.go
+++ b/pkg/crc/cluster/cert_renewal.go
@@ -1,6 +1,7 @@
 package cluster
 
 import (
+	"context"
 	"fmt"
 	"time"
 
@@ -16,9 +17,9 @@ func isPending(csr *k8scerts.CertificateSigningRequest) bool {
 	return len(csr.Status.Conditions) == 0 && len(csr.Status.Certificate) == 0
 }
 
-func approvePendingCSRs(ocConfig oc.Config, expectedSignerName string) error {
-	return crcerrors.RetryAfter(8*time.Minute, func() error {
-		csrs, err := getCSRList(ocConfig, expectedSignerName)
+func approvePendingCSRs(ctx context.Context, ocConfig oc.Config, expectedSignerName string) error {
+	return crcerrors.Retry(ctx, 8*time.Minute, func() error {
+		csrs, err := getCSRList(ctx, ocConfig, expectedSignerName)
 		if err != nil {
 			return &crcerrors.RetriableError{Err: err}
 		}
@@ -42,7 +43,7 @@ func approvePendingCSRs(ocConfig oc.Config, expectedSignerName string) error {
 	}, time.Second*5)
 }
 
-func ApproveCSRAndWaitForCertsRenewal(sshRunner *ssh.Runner, ocConfig oc.Config, client, server bool) error {
+func ApproveCSRAndWaitForCertsRenewal(ctx context.Context, sshRunner *ssh.Runner, ocConfig oc.Config, client, server bool) error {
 	const (
 		kubeletClientSignerName = "kubernetes.io/kube-apiserver-client-kubelet"
 		authClientSignerName    = "kubernetes.io/kube-apiserver-client"
@@ -53,7 +54,7 @@ func ApproveCSRAndWaitForCertsRenewal(sshRunner *ssh.Runner, ocConfig oc.Config,
 	// Kubelet stores the cert in /var/lib/kubelet/pki/kubelet-client-current.pem
 	if client {
 		logging.Info("Kubelet client certificate has expired, renewing it... [will take up to 8 minutes]")
-		if err := approvePendingCSRs(ocConfig, kubeletClientSignerName); err != nil {
+		if err := approvePendingCSRs(ctx, ocConfig, kubeletClientSignerName); err != nil {
 			logging.Debugf("Error approving pending kube-apiserver-client-kubelet CSRs: %v", err)
 			return err
 		}
@@ -62,11 +63,11 @@ func ApproveCSRAndWaitForCertsRenewal(sshRunner *ssh.Runner, ocConfig oc.Config,
 		// if the patch backported to 4.8 z stream.
 		// https://github.com/openshift/library-go/pull/1190 and https://github.com/openshift/cluster-authentication-operator/pull/475
 		// https://bugzilla.redhat.com/show_bug.cgi?id=1997906
-		if err := deleteCSR(ocConfig, authClientSignerName); err != nil {
+		if err := deleteCSR(ctx, ocConfig, authClientSignerName); err != nil {
 			logging.Debugf("Error deleting openshift-authenticator csr: %v", err)
 			return err
 		}
-		if err := crcerrors.RetryAfter(5*time.Minute, waitForCertRenewal(sshRunner, KubeletClientCert), time.Second*5); err != nil {
+		if err := crcerrors.Retry(ctx, 5*time.Minute, waitForCertRenewal(sshRunner, KubeletClientCert), time.Second*5); err != nil {
 			logging.Debugf("Error approving pending kube-apiserver-client-kubelet CSR: %v", err)
 			return err
 		}
@@ -77,7 +78,7 @@ func ApproveCSRAndWaitForCertsRenewal(sshRunner *ssh.Runner, ocConfig oc.Config,
 	// This CSR is automatically approved by the cluster-machine-approver. The k8s controller manager issues the cert and kubelet fetches it.
 	if server {
 		logging.Info("Kubelet serving certificate has expired, waiting for automatic renewal... [will take up to 8 minutes]")
-		return crcerrors.RetryAfter(5*time.Minute, waitForCertRenewal(sshRunner, KubeletServerCert), time.Second*5)
+		return crcerrors.Retry(ctx, 5*time.Minute, waitForCertRenewal(sshRunner, KubeletServerCert), time.Second*5)
 	}
 	return nil
 }
diff --git a/pkg/crc/cluster/cluster.go b/pkg/crc/cluster/cluster.go
index d445eb8f7..a782120c1 100644
--- a/pkg/crc/cluster/cluster.go
+++ b/pkg/crc/cluster/cluster.go
@@ -82,13 +82,13 @@ func GetRootPartitionUsage(sshRunner *ssh.Runner) (int64, int64, error) {
 	return diskSize, diskUsage, nil
 }
 
-func EnsureSSHKeyPresentInTheCluster(ocConfig oc.Config, sshPublicKeyPath string) error {
+func EnsureSSHKeyPresentInTheCluster(ctx context.Context, ocConfig oc.Config, sshPublicKeyPath string) error {
 	sshPublicKeyByte, err := ioutil.ReadFile(sshPublicKeyPath)
 	if err != nil {
 		return err
 	}
 	sshPublicKey := strings.TrimRight(string(sshPublicKeyByte), "\r\n")
-	if err := WaitForOpenshiftResource(ocConfig, "machineconfigs"); err != nil {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "machineconfigs"); err != nil {
 		return err
 	}
 	stdout, stderr, err := ocConfig.RunOcCommand("get", "machineconfigs", "99-master-ssh", "-o", `jsonpath='{.spec.config.passwd.users[0].sshAuthorizedKeys[0]}'`)
@@ -109,8 +109,8 @@ func EnsureSSHKeyPresentInTheCluster(ocConfig oc.Config, sshPublicKeyPath string
 	return nil
 }
 
-func EnsurePullSecretPresentInTheCluster(ocConfig oc.Config, pullSec PullSecretLoader) error {
-	if err := WaitForOpenshiftResource(ocConfig, "secret"); err != nil {
+func EnsurePullSecretPresentInTheCluster(ctx context.Context, ocConfig oc.Config, pullSec PullSecretLoader) error {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "secret"); err != nil {
 		return err
 	}
 
@@ -143,9 +143,9 @@ func EnsurePullSecretPresentInTheCluster(ocConfig oc.Config, pullSec PullSecretL
 	return nil
 }
 
-func EnsureGeneratedClientCAPresentInTheCluster(ocConfig oc.Config, sshRunner *ssh.Runner, selfSignedCACert *x509.Certificate, adminCert string) error {
+func EnsureGeneratedClientCAPresentInTheCluster(ctx context.Context, ocConfig oc.Config, sshRunner *ssh.Runner, selfSignedCACert *x509.Certificate, adminCert string) error {
 	selfSignedCAPem := crctls.CertToPem(selfSignedCACert)
-	if err := WaitForOpenshiftResource(ocConfig, "configmaps"); err != nil {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "configmaps"); err != nil {
 		return err
 	}
 	clusterClientCA, stderr, err := ocConfig.RunOcCommand("get", "configmaps", "admin-kubeconfig-client-ca", "-n", "openshift-config", "-o", `jsonpath="{.data.ca-bundle\.crt}"`)
@@ -176,7 +176,7 @@ func EnsureGeneratedClientCAPresentInTheCluster(ocConfig oc.Config, sshRunner *s
 	return nil
 }
 
-func RemovePullSecretFromCluster(ocConfig oc.Config, sshRunner *ssh.Runner) error {
+func RemovePullSecretFromCluster(ctx context.Context, ocConfig oc.Config, sshRunner *ssh.Runner) error {
 	logging.Info("Removing user's pull secret from instance disk and from cluster secret...")
 	cmdArgs := []string{"patch", "secret", "pull-secret", "-p",
 		`'{"data":{".dockerconfigjson":"e30K"}}'`,
@@ -186,10 +186,10 @@ func RemovePullSecretFromCluster(ocConfig oc.Config, sshRunner *ssh.Runner) erro
 	if err != nil {
 		return fmt.Errorf("Failed to remove Pull secret %w: %s", err, stderr)
 	}
-	return waitForPullSecretRemovedFromInstanceDisk(sshRunner)
+	return waitForPullSecretRemovedFromInstanceDisk(ctx, sshRunner)
 }
 
-func waitForPullSecretRemovedFromInstanceDisk(sshRunner *ssh.Runner) error {
+func waitForPullSecretRemovedFromInstanceDisk(ctx context.Context, sshRunner *ssh.Runner) error {
 	logging.Info("Waiting for user's pull secret removed from instance disk...")
 	pullSecretPresentFunc := func() error {
 		stdout, stderr, err := sshRunner.RunPrivate(fmt.Sprintf("sudo cat %s", vmPullSecretPath))
@@ -201,7 +201,7 @@ func waitForPullSecretRemovedFromInstanceDisk(sshRunner *ssh.Runner) error {
 		}
 		return nil
 	}
-	return errors.RetryAfter(1*time.Minute, pullSecretPresentFunc, 2*time.Second)
+	return errors.Retry(ctx, 1*time.Minute, pullSecretPresentFunc, 2*time.Second)
 }
 
 func RemoveOldRenderedMachineConfig(ocConfig oc.Config) error {
@@ -249,8 +249,8 @@ func RemoveOldRenderedMachineConfig(ocConfig oc.Config) error {
 	return nil
 }
 
-func EnsureClusterIDIsNotEmpty(ocConfig oc.Config) error {
-	if err := WaitForOpenshiftResource(ocConfig, "clusterversion"); err != nil {
+func EnsureClusterIDIsNotEmpty(ctx context.Context, ocConfig oc.Config) error {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "clusterversion"); err != nil {
 		return err
 	}
 
@@ -275,7 +275,7 @@ func EnsureClusterIDIsNotEmpty(ocConfig oc.Config) error {
 	return nil
 }
 
-func AddProxyConfigToCluster(sshRunner *ssh.Runner, ocConfig oc.Config, proxy *network.ProxyConfig) error {
+func AddProxyConfigToCluster(ctx context.Context, sshRunner *ssh.Runner, ocConfig oc.Config, proxy *network.ProxyConfig) error {
 	type trustedCA struct {
 		Name string `json:"name"`
 	}
@@ -299,7 +299,7 @@ func AddProxyConfigToCluster(sshRunner *ssh.Runner, ocConfig oc.Config, proxy *n
 		},
 	}
 
-	if err := WaitForOpenshiftResource(ocConfig, "proxy"); err != nil {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "proxy"); err != nil {
 		return err
 	}
 
@@ -419,7 +419,7 @@ func EnsurePullSecretPresentOnInstanceDisk(sshRunner *ssh.Runner, pullSecret Pul
 	return sshRunner.CopyData([]byte(content), vmPullSecretPath, 0600)
 }
 
-func WaitForPullSecretPresentOnInstanceDisk(sshRunner *ssh.Runner) error {
+func WaitForPullSecretPresentOnInstanceDisk(ctx context.Context, sshRunner *ssh.Runner) error {
 	logging.Info("Waiting for user's pull secret part of instance disk...")
 	pullSecretPresentFunc := func() error {
 		stdout, stderr, err := sshRunner.RunPrivate(fmt.Sprintf("sudo cat %s", vmPullSecretPath))
@@ -431,10 +431,10 @@ func WaitForPullSecretPresentOnInstanceDisk(sshRunner *ssh.Runner) error {
 		}
 		return nil
 	}
-	return errors.RetryAfter(7*time.Minute, pullSecretPresentFunc, 2*time.Second)
+	return errors.Retry(ctx, 7*time.Minute, pullSecretPresentFunc, 2*time.Second)
 }
 
-func WaitForRequestHeaderClientCaFile(sshRunner *ssh.Runner) error {
+func WaitForRequestHeaderClientCaFile(ctx context.Context, sshRunner *ssh.Runner) error {
 	lookupRequestHeaderClientCa := func() error {
 		expired, err := checkCertValidity(sshRunner, AggregatorClientCert)
 		if err != nil {
@@ -445,7 +445,7 @@ func WaitForRequestHeaderClientCaFile(sshRunner *ssh.Runner) error {
 		}
 		return nil
 	}
-	return errors.RetryAfter(8*time.Minute, lookupRequestHeaderClientCa, 2*time.Second)
+	return errors.Retry(ctx, 8*time.Minute, lookupRequestHeaderClientCa, 2*time.Second)
 }
 
 func WaitForAPIServer(ctx context.Context, ocConfig oc.Config) error {
@@ -459,11 +459,11 @@ func WaitForAPIServer(ctx context.Context, ocConfig oc.Config) error {
 		logging.Debug(stdout)
 		return nil
 	}
-	return errors.RetryAfterWithContext(ctx, 4*time.Minute, waitForAPIServer, time.Second)
+	return errors.Retry(ctx, 4*time.Minute, waitForAPIServer, time.Second)
 }
 
-func DeleteOpenshiftAPIServerPods(ocConfig oc.Config) error {
-	if err := WaitForOpenshiftResource(ocConfig, "pod"); err != nil {
+func DeleteOpenshiftAPIServerPods(ctx context.Context, ocConfig oc.Config) error {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "pod"); err != nil {
 		return err
 	}
 
@@ -476,7 +476,7 @@ func DeleteOpenshiftAPIServerPods(ocConfig oc.Config) error {
 		return nil
 	}
 
-	return errors.RetryAfter(60*time.Second, deleteOpenshiftAPIServerPods, time.Second)
+	return errors.Retry(ctx, 60*time.Second, deleteOpenshiftAPIServerPods, time.Second)
 }
 
 func CheckProxySettingsForOperator(ocConfig oc.Config, proxy *network.ProxyConfig, deployment, namespace string) (bool, error) {
@@ -495,8 +495,8 @@ func CheckProxySettingsForOperator(ocConfig oc.Config, proxy *network.ProxyConfi
 	return false, nil
 }
 
-func DeleteMCOLeaderLease(ocConfig oc.Config) error {
-	if err := WaitForOpenshiftResource(ocConfig, "cm"); err != nil {
+func DeleteMCOLeaderLease(ctx context.Context, ocConfig oc.Config) error {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "cm"); err != nil {
 		return err
 	}
 	_, _, err := ocConfig.RunOcCommand("delete", "-n", "openshift-machine-config-operator", "cm", "machine-config-controller")
diff --git a/pkg/crc/cluster/csr.go b/pkg/crc/cluster/csr.go
index ea06b2adf..9ed5e78a6 100644
--- a/pkg/crc/cluster/csr.go
+++ b/pkg/crc/cluster/csr.go
@@ -1,6 +1,7 @@
 package cluster
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"time"
@@ -12,7 +13,7 @@ import (
 	k8scerts "k8s.io/api/certificates/v1beta1"
 )
 
-func WaitForOpenshiftResource(ocConfig oc.Config, resource string) error {
+func WaitForOpenshiftResource(ctx context.Context, ocConfig oc.Config, resource string) error {
 	logging.Debugf("Waiting for availability of resource type '%s'", resource)
 	waitForAPIServer := func() error {
 		stdout, stderr, err := ocConfig.WithFailFast().RunOcCommand("get", resource)
@@ -23,11 +24,11 @@ func WaitForOpenshiftResource(ocConfig oc.Config, resource string) error {
 		logging.Debug(stdout)
 		return nil
 	}
-	return crcerrors.RetryAfter(80*time.Second, waitForAPIServer, time.Second)
+	return crcerrors.Retry(ctx, 80*time.Second, waitForAPIServer, time.Second)
 }
 
-func deleteCSR(ocConfig oc.Config, expectedSignerName string) error {
-	csrs, err := getCSRList(ocConfig, expectedSignerName)
+func deleteCSR(ctx context.Context, ocConfig oc.Config, expectedSignerName string) error {
+	csrs, err := getCSRList(ctx, ocConfig, expectedSignerName)
 	if err != nil {
 		return err
 	}
@@ -41,9 +42,9 @@ func deleteCSR(ocConfig oc.Config, expectedSignerName string) error {
 	return nil
 }
 
-func getCSRList(ocConfig oc.Config, expectedSignerName string) (*k8scerts.CertificateSigningRequestList, error) {
+func getCSRList(ctx context.Context, ocConfig oc.Config, expectedSignerName string) (*k8scerts.CertificateSigningRequestList, error) {
 	var csrs k8scerts.CertificateSigningRequestList
-	if err := WaitForOpenshiftResource(ocConfig, "csr"); err != nil {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "csr"); err != nil {
 		return nil, err
 	}
 	output, stderr, err := ocConfig.WithFailFast().RunOcCommand("get", "csr", "-ojson")
diff --git a/pkg/crc/cluster/kubeadmin_password.go b/pkg/crc/cluster/kubeadmin_password.go
index 305c4653a..87b0f9a60 100644
--- a/pkg/crc/cluster/kubeadmin_password.go
+++ b/pkg/crc/cluster/kubeadmin_password.go
@@ -3,6 +3,7 @@ package cluster
 import (
 	"bufio"
 	"bytes"
+	"context"
 	"crypto/rand"
 	"encoding/base64"
 	"fmt"
@@ -28,7 +29,7 @@ func GenerateKubeAdminUserPassword() error {
 }
 
 // UpdateKubeAdminUserPassword updates the htpasswd secret
-func UpdateKubeAdminUserPassword(ocConfig oc.Config, newPassword string) error {
+func UpdateKubeAdminUserPassword(ctx context.Context, ocConfig oc.Config, newPassword string) error {
 	if newPassword != "" {
 		logging.Infof("Overriding password for kubeadmin user")
 		if err := ioutil.WriteFile(constants.GetKubeAdminPasswordPath(), []byte(strings.TrimSpace(newPassword)), 0600); err != nil {
@@ -45,7 +46,7 @@ func UpdateKubeAdminUserPassword(ocConfig oc.Config, newPassword string) error {
 		"kubeadmin": kubeAdminPassword,
 	}
 
-	if err := WaitForOpenshiftResource(ocConfig, "secret"); err != nil {
+	if err := WaitForOpenshiftResource(ctx, ocConfig, "secret"); err != nil {
 		return err
 	}
 
diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go
index 6dbd53b85..2d03c21f9 100644
--- a/pkg/crc/constants/constants.go
+++ b/pkg/crc/constants/constants.go
@@ -102,10 +102,7 @@ var (
 
 func defaultBundlePath() string {
 	if version.IsInstaller() {
-		path := filepath.Join(version.InstallPath(), GetDefaultBundle())
-		if _, err := os.Stat(path); err == nil {
-			return path
-		}
+		return filepath.Join(version.InstallPath(), GetDefaultBundle())
 	}
 	return filepath.Join(MachineCacheDir, GetDefaultBundle())
 }
diff --git a/pkg/crc/errors/multierror.go b/pkg/crc/errors/multierror.go
index d6daeab28..527a2b918 100644
--- a/pkg/crc/errors/multierror.go
+++ b/pkg/crc/errors/multierror.go
@@ -81,8 +81,8 @@ func (r *RetriableError) Error() string {
 	return "Temporary error: " + r.Err.Error()
 }
 
-// RetryAfterWithContext retries for a certain duration, after a delay
-func RetryAfterWithContext(ctx context.Context, limit time.Duration, callback func() error, d time.Duration) error {
+// Retry retries for a certain duration, after a delay
+func Retry(ctx context.Context, limit time.Duration, callback func() error, d time.Duration) error {
 	if ctx.Err() != nil {
 		return ctx.Err()
 	}
@@ -112,8 +112,3 @@ func RetryAfterWithContext(ctx context.Context, limit time.Duration, callback fu
 	logging.Debugf("RetryAfter timeout after %d tries", attempt)
 	return m
 }
-
-// RetryAfter retries for a certain duration, after a delay
-func RetryAfter(limit time.Duration, callback func() error, d time.Duration) error {
-	return RetryAfterWithContext(context.Background(), limit, callback, d)
-}
diff --git a/pkg/crc/machine/bundle/repository.go b/pkg/crc/machine/bundle/repository.go
index bbe194eca..bb349b001 100644
--- a/pkg/crc/machine/bundle/repository.go
+++ b/pkg/crc/machine/bundle/repository.go
@@ -1,6 +1,7 @@
 package bundle
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -153,7 +154,7 @@ func (repo *Repository) Extract(path string) error {
 	bundleBaseDir := GetBundleNameWithoutExtension(bundleName)
 	bundleDir := filepath.Join(repo.CacheDir, bundleBaseDir)
 	_ = os.RemoveAll(bundleDir)
-	return crcerrors.RetryAfter(time.Minute, func() error {
+	return crcerrors.Retry(context.Background(), time.Minute, func() error {
 		if err := os.Rename(filepath.Join(tmpDir, bundleBaseDir), bundleDir); err != nil {
 			return &crcerrors.RetriableError{Err: err}
 		}
diff --git a/pkg/crc/machine/generate_bundle.go b/pkg/crc/machine/generate_bundle.go
index 7347b2f1b..310bff0b5 100644
--- a/pkg/crc/machine/generate_bundle.go
+++ b/pkg/crc/machine/generate_bundle.go
@@ -1,6 +1,7 @@
 package machine
 
 import (
+	"context"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -23,7 +24,7 @@ func (client *client) GenerateBundle(forceStop bool) error {
 	defer sshRunner.Close()
 
 	ocConfig := oc.UseOCWithSSH(sshRunner)
-	if err := cluster.RemovePullSecretFromCluster(ocConfig, sshRunner); err != nil {
+	if err := cluster.RemovePullSecretFromCluster(context.Background(), ocConfig, sshRunner); err != nil {
 		return errors.Wrap(err, "Error removing pull secret from cluster")
 	}
 
diff --git a/pkg/crc/machine/start.go b/pkg/crc/machine/start.go
index 02c82006d..4dc8eed69 100644
--- a/pkg/crc/machine/start.go
+++ b/pkg/crc/machine/start.go
@@ -338,7 +338,7 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
 	}
 
 	// Check DNS lookup before starting the kubelet
-	if queryOutput, err := dns.CheckCRCLocalDNSReachable(servicePostStartConfig); err != nil {
+	if queryOutput, err := dns.CheckCRCLocalDNSReachable(ctx, servicePostStartConfig); err != nil {
 		if !client.useVSock() {
 			return nil, errors.Wrapf(err, "Failed internal DNS query: %s", queryOutput)
 		}
@@ -385,7 +385,7 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
 
 	ocConfig := oc.UseOCWithSSH(sshRunner)
 
-	if err := cluster.ApproveCSRAndWaitForCertsRenewal(sshRunner, ocConfig, certsExpired[cluster.KubeletClientCert], certsExpired[cluster.KubeletServerCert]); err != nil {
+	if err := cluster.ApproveCSRAndWaitForCertsRenewal(ctx, sshRunner, ocConfig, certsExpired[cluster.KubeletClientCert], certsExpired[cluster.KubeletServerCert]); err != nil {
 		logBundleDate(crcBundleMetadata)
 		return nil, errors.Wrap(err, "Failed to renew TLS certificates: please check if a newer CodeReady Containers release is available")
 	}
@@ -394,31 +394,31 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
 		return nil, errors.Wrap(err, "Error waiting for apiserver")
 	}
 
-	if err := cluster.DeleteMCOLeaderLease(ocConfig); err != nil {
+	if err := cluster.DeleteMCOLeaderLease(ctx, ocConfig); err != nil {
 		return nil, err
 	}
 
-	if err := cluster.EnsurePullSecretPresentInTheCluster(ocConfig, startConfig.PullSecret); err != nil {
+	if err := cluster.EnsurePullSecretPresentInTheCluster(ctx, ocConfig, startConfig.PullSecret); err != nil {
 		return nil, errors.Wrap(err, "Failed to update cluster pull secret")
 	}
 
-	if err := cluster.EnsureSSHKeyPresentInTheCluster(ocConfig, constants.GetPublicKeyPath()); err != nil {
+	if err := cluster.EnsureSSHKeyPresentInTheCluster(ctx, ocConfig, constants.GetPublicKeyPath()); err != nil {
 		return nil, errors.Wrap(err, "Failed to update ssh public key to machine config")
 	}
 
-	if err := cluster.WaitForPullSecretPresentOnInstanceDisk(sshRunner); err != nil {
+	if err := cluster.WaitForPullSecretPresentOnInstanceDisk(ctx, sshRunner); err != nil {
 		return nil, errors.Wrap(err, "Failed to update pull secret on the disk")
 	}
 
-	if err := ensureProxyIsConfiguredInOpenShift(ocConfig, sshRunner, proxyConfig, instanceIP); err != nil {
+	if err := ensureProxyIsConfiguredInOpenShift(ctx, ocConfig, sshRunner, proxyConfig, instanceIP); err != nil {
 		return nil, errors.Wrap(err, "Failed to update cluster proxy configuration")
 	}
 
-	if err := cluster.UpdateKubeAdminUserPassword(ocConfig, startConfig.KubeAdminPassword); err != nil {
+	if err := cluster.UpdateKubeAdminUserPassword(ctx, ocConfig, startConfig.KubeAdminPassword); err != nil {
 		return nil, errors.Wrap(err, "Failed to update kubeadmin user password")
 	}
 
-	if err := cluster.EnsureClusterIDIsNotEmpty(ocConfig); err != nil {
+	if err := cluster.EnsureClusterIDIsNotEmpty(ctx, ocConfig); err != nil {
 		return nil, errors.Wrap(err, "Failed to update cluster ID")
 	}
 
@@ -449,16 +449,16 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
 	// More info: https://bugzilla.redhat.com/show_bug.cgi?id=1795163
 	if certsExpired[cluster.AggregatorClientCert] {
 		logging.Debug("Waiting for the renewal of the request header client ca...")
-		if err := cluster.WaitForRequestHeaderClientCaFile(sshRunner); err != nil {
+		if err := cluster.WaitForRequestHeaderClientCaFile(ctx, sshRunner); err != nil {
 			return nil, errors.Wrap(err, "Failed to wait for aggregator client ca renewal")
 		}
 
-		if err := cluster.DeleteOpenshiftAPIServerPods(ocConfig); err != nil {
+		if err := cluster.DeleteOpenshiftAPIServerPods(ctx, ocConfig); err != nil {
 			return nil, errors.Wrap(err, "Cannot delete OpenShift API Server pods")
 		}
 	}
 
-	if err := updateKubeconfig(ocConfig, sshRunner, crcBundleMetadata.GetKubeConfigPath()); err != nil {
+	if err := updateKubeconfig(ctx, ocConfig, sshRunner, crcBundleMetadata.GetKubeConfigPath()); err != nil {
 		return nil, errors.Wrap(err, "Failed to update kubeconfig file")
 	}
 
@@ -580,7 +580,7 @@ func startHost(ctx context.Context, api libmachine.API, vm *host.Host) error {
 	}
 
 	logging.Debug("Waiting for machine to be running, this may take a few minutes...")
-	if err := crcerrors.RetryAfterWithContext(ctx, 3*time.Minute, host.MachineInState(vm.Driver, libmachinestate.Running), 3*time.Second); err != nil {
+	if err := crcerrors.Retry(ctx, 3*time.Minute, host.MachineInState(vm.Driver, libmachinestate.Running), 3*time.Second); err != nil {
 		return fmt.Errorf("Error waiting for machine to be running: %s", err)
 	}
 
@@ -644,12 +644,12 @@ func ensureKubeletAndCRIOAreConfiguredForProxy(sshRunner *crcssh.Runner, proxy *
 	return cluster.AddProxyToKubeletAndCriO(sshRunner, proxy)
 }
 
-func ensureProxyIsConfiguredInOpenShift(ocConfig oc.Config, sshRunner *crcssh.Runner, proxy *network.ProxyConfig, instanceIP string) (err error) {
+func ensureProxyIsConfiguredInOpenShift(ctx context.Context, ocConfig oc.Config, sshRunner *crcssh.Runner, proxy *network.ProxyConfig, instanceIP string) (err error) {
 	if !proxy.IsEnabled() {
 		return nil
 	}
 	logging.Info("Adding proxy configuration to the cluster...")
-	return cluster.AddProxyConfigToCluster(sshRunner, ocConfig, proxy)
+	return cluster.AddProxyConfigToCluster(ctx, sshRunner, ocConfig, proxy)
 }
 
 func waitForProxyPropagation(ctx context.Context, ocConfig oc.Config, proxyConfig *network.ProxyConfig) {
@@ -670,7 +670,7 @@ func waitForProxyPropagation(ctx context.Context, ocConfig oc.Config, proxyConfi
 		return nil
 	}
 
-	if err := crcerrors.RetryAfterWithContext(ctx, 300*time.Second, checkProxySettingsForOperator, 2*time.Second); err != nil {
+	if err := crcerrors.Retry(ctx, 300*time.Second, checkProxySettingsForOperator, 2*time.Second); err != nil {
 		logging.Debug("Failed to propagate proxy settings to cluster")
 	}
 }
@@ -719,7 +719,7 @@ func ensureRoutesControllerIsRunning(sshRunner *crcssh.Runner, ocConfig oc.Confi
 	return nil
 }
 
-func updateKubeconfig(ocConfig oc.Config, sshRunner *crcssh.Runner, kubeconfigFilePath string) error {
+func updateKubeconfig(ctx context.Context, ocConfig oc.Config, sshRunner *crcssh.Runner, kubeconfigFilePath string) error {
 	selfSignedCAKey, selfSignedCACert, err := crctls.GetSelfSignedCA()
 	if err != nil {
 		return errors.Wrap(err, "Not able to generate root CA key and Cert")
@@ -731,7 +731,7 @@ func updateKubeconfig(ocConfig oc.Config, sshRunner *crcssh.Runner, kubeconfigFi
 	if err != nil {
 		return errors.Wrap(err, "Not able to get user CA")
 	}
-	if err := cluster.EnsureGeneratedClientCAPresentInTheCluster(ocConfig, sshRunner, selfSignedCACert, adminClientCA); err != nil {
+	if err := cluster.EnsureGeneratedClientCAPresentInTheCluster(ctx, ocConfig, sshRunner, selfSignedCACert, adminClientCA); err != nil {
 		return errors.Wrap(err, "Failed to update user CA to cluster")
 	}
 	return nil
diff --git a/pkg/crc/services/dns/dns.go b/pkg/crc/services/dns/dns.go
index 2539b77f0..eb7d86a68 100644
--- a/pkg/crc/services/dns/dns.go
+++ b/pkg/crc/services/dns/dns.go
@@ -1,6 +1,7 @@
 package dns
 
 import (
+	"context"
 	"fmt"
 	"time"
 
@@ -95,7 +96,7 @@ func dnsServers(serviceConfig services.ServicePostStartConfig) ([]network.NameSe
 	return append([]network.NameServer{{IPAddress: dnsContainerIP}}, orgResolvValues.NameServers...), nil
 }
 
-func CheckCRCLocalDNSReachable(serviceConfig services.ServicePostStartConfig) (string, error) {
+func CheckCRCLocalDNSReachable(ctx context.Context, serviceConfig services.ServicePostStartConfig) (string, error) {
 	appsURI := fmt.Sprintf("foo.%s", serviceConfig.BundleMetadata.ClusterInfo.AppsDomain)
 	// Try 30 times for 1 second interval, In nested environment most of time crc failed to get
 	// Internal dns query resolved for some time.
@@ -109,7 +110,7 @@ func CheckCRCLocalDNSReachable(serviceConfig services.ServicePostStartConfig) (s
 		return nil
 	}
 
-	if err := errors.RetryAfter(30*time.Second, checkLocalDNSReach, time.Second); err != nil {
+	if err := errors.Retry(ctx, 30*time.Second, checkLocalDNSReach, time.Second); err != nil {
 		return queryOutput, err
 	}
 	return queryOutput, err
diff --git a/pkg/crc/services/dns/dns_darwin.go b/pkg/crc/services/dns/dns_darwin.go
index e3bd12377..4360e1cdf 100644
--- a/pkg/crc/services/dns/dns_darwin.go
+++ b/pkg/crc/services/dns/dns_darwin.go
@@ -2,6 +2,7 @@ package dns
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"path/filepath"
 	"strings"
@@ -137,7 +138,7 @@ func waitForNetwork() error {
 		}
 		return nil
 	}
-	if err := crcerrors.RetryAfter(15*time.Second, retriableConnectivityCheck, time.Second); err != nil {
+	if err := crcerrors.Retry(context.Background(), 15*time.Second, retriableConnectivityCheck, time.Second); err != nil {
 		return fmt.Errorf("Host is not connected to internet")
 	}
 
diff --git a/pkg/crc/ssh/ssh.go b/pkg/crc/ssh/ssh.go
index 09d3e2808..ab846dd79 100644
--- a/pkg/crc/ssh/ssh.go
+++ b/pkg/crc/ssh/ssh.go
@@ -105,5 +105,5 @@ func (runner *Runner) WaitForConnectivity(ctx context.Context, timeout time.Dura
 		return nil
 	}
 
-	return errors.RetryAfterWithContext(ctx, timeout, checkSSHConnectivity, time.Second)
+	return errors.Retry(ctx, timeout, checkSSHConnectivity, time.Second)
 }
diff --git a/pkg/crc/version/version.go b/pkg/crc/version/version.go
index bfaf18967..640ab3f43 100644
--- a/pkg/crc/version/version.go
+++ b/pkg/crc/version/version.go
@@ -91,7 +91,12 @@ func InstallPath() string {
 		logging.Errorf("Failed to find the installation path: %v", err)
 		return ""
 	}
-	return filepath.Dir(currentExecutablePath)
+	src, err := filepath.EvalSymlinks(currentExecutablePath)
+	if err != nil {
+		logging.Errorf("Failed to find the symlink destination of %s: %v", currentExecutablePath, err)
+		return filepath.Dir(currentExecutablePath)
+	}
+	return filepath.Dir(src)
 }
 
 func GetCRCLatestVersionFromMirror(transport http.RoundTripper) (*CrcReleaseInfo, error) {
diff --git a/pkg/libmachine/host/host.go b/pkg/libmachine/host/host.go
index 9319df22f..3efe7f4f9 100644
--- a/pkg/libmachine/host/host.go
+++ b/pkg/libmachine/host/host.go
@@ -1,6 +1,7 @@
 package host
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"net/rpc"
@@ -40,7 +41,7 @@ func (h *Host) runActionForState(action func() error, desiredState state.State)
 		return err
 	}
 
-	return crcerrors.RetryAfter(3*time.Minute, MachineInState(h.Driver, desiredState), 3*time.Second)
+	return crcerrors.Retry(context.Background(), 3*time.Minute, MachineInState(h.Driver, desiredState), 3*time.Second)
 }
 
 func (h *Host) Stop() error {


Test patch:
diff --git a/pkg/crc/errors/multierror_test.go b/pkg/crc/errors/multierror_test.go
index b051ad423..7710efb32 100644
--- a/pkg/crc/errors/multierror_test.go
+++ b/pkg/crc/errors/multierror_test.go
@@ -1,6 +1,7 @@
 package errors
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"testing"
@@ -9,9 +10,9 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestRetryAfter(t *testing.T) {
+func TestRetryAfterWithContext(t *testing.T) {
 	calls := 0
-	ret := RetryAfter(time.Second, func() error {
+	ret := Retry(context.Background(), time.Second, func() error {
 		calls++
 		return nil
 	}, 0)
@@ -21,7 +22,7 @@ func TestRetryAfter(t *testing.T) {
 
 func TestRetryAfterFailure(t *testing.T) {
 	calls := 0
-	ret := RetryAfter(time.Second, func() error {
+	ret := Retry(context.Background(), time.Second, func() error {
 		calls++
 		return errors.New("failed")
 	}, 0)
@@ -31,7 +32,7 @@ func TestRetryAfterFailure(t *testing.T) {
 
 func TestRetryAfterSlowFailure(t *testing.T) {
 	calls := 0
-	ret := RetryAfter(time.Millisecond, func() error {
+	ret := Retry(context.Background(), time.Millisecond, func() error {
 		time.Sleep(50 * time.Millisecond)
 		calls++
 		if calls < 2 {
@@ -45,7 +46,7 @@ func TestRetryAfterSlowFailure(t *testing.T) {
 
 func TestRetryAfterMaxAttempts(t *testing.T) {
 	calls := 0
-	ret := RetryAfter(10*time.Millisecond, func() error {
+	ret := Retry(context.Background(), 10*time.Millisecond, func() error {
 		calls++
 		return &RetriableError{Err: errors.New("failed")}
 	}, 0)
@@ -55,7 +56,7 @@ func TestRetryAfterMaxAttempts(t *testing.T) {
 
 func TestRetryAfterSuccessAfterFailures(t *testing.T) {
 	calls := 0
-	ret := RetryAfter(time.Second, func() error {
+	ret := Retry(context.Background(), time.Second, func() error {
 		calls++
 		if calls < 3 {
 			return &RetriableError{Err: errors.New("failed")}