Repository: code-ready/crc
Pull request #2163 (2021-03-29 09:37:40)

Change install path of crc to /Applications/CodeReady Containers.app
Requires https://github.com/code-ready/tray-macos/pull/91

It uses the tray as a conveyor for the full crc application.
Everything is installed in /Applications/CodeReady Containers.app.
If the user wants to uninstall the app, he just has to drag and drop the
app in the trash (after running crc cleanup).

When upgrading, all files will be replaced with newer. There is no more
one directory per version.



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

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/2163#" 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/Makefile b/Makefile
index f1ce20708..49f1e52e9 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ BUNDLE_VERSION = 4.7.4
 BUNDLE_EXTENSION = crcbundle
 CRC_VERSION = 1.24.0
 COMMIT_SHA=$(shell git rev-parse --short HEAD)
-MACOS_INSTALL_PATH = /Library/CodeReady Containers
+MACOS_INSTALL_PATH = /Applications/CodeReady Containers.app/Contents/Resources/
 CONTAINER_RUNTIME ?= podman
 
 ifdef OKD_VERSION
@@ -252,20 +252,26 @@ update-go-version:
 goversioncheck:
 	./verify-go-version.sh
 
+TRAY_RELEASE ?= packaging/tmp/crc-tray-macos.tar.gz
+
 packagedir: LDFLAGS+= -X '$(REPOPATH)/pkg/crc/version.macosInstallPath=$(MACOS_INSTALL_PATH)' $(RELEASE_VERSION_VARIABLES)
 packagedir: clean check_bundledir $(BUILD_DIR)/macos-amd64/crc $(HOST_BUILD_DIR)/crc-embedder
+	echo -n $(CRC_VERSION) > packaging/VERSION
 	sed -e 's/__VERSION__/'$(CRC_VERSION)'/g' -e 's@__INSTALL_PATH__@$(MACOS_INSTALL_PATH)@g' packaging/darwin/Distribution.in >packaging/darwin/Distribution
 	sed -e 's/__VERSION__/'$(CRC_VERSION)'/g' -e 's@__INSTALL_PATH__@$(MACOS_INSTALL_PATH)@g' packaging/darwin/welcome.html.in >packaging/darwin/Resources/welcome.html
 	sed -e 's/__VERSION__/'$(CRC_VERSION)'/g' -e 's@__INSTALL_PATH__@$(MACOS_INSTALL_PATH)@g' packaging/darwin/postinstall.in >packaging/darwin/scripts/postinstall
 	chmod 755 packaging/darwin/scripts/postinstall
-	mkdir -p packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/
-	$(HOST_BUILD_DIR)/crc-embedder download packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/
+	mkdir -p packaging/tmp/
+	$(HOST_BUILD_DIR)/crc-embedder download packaging/tmp/
+
+	mkdir -p packaging/root/Applications
+	tar -C packaging/root/Applications -xvzf $(TRAY_RELEASE)
+	rm packaging/tmp/crc-tray-macos.tar.gz
 
-	tar -C packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/ -xvzf packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/crc-tray-macos.tar.gz
-	rm packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/crc-tray-macos.tar.gz
+	mv packaging/tmp/* packaging/root/"$(MACOS_INSTALL_PATH)"
 
-	cp $(HYPERKIT_BUNDLENAME) packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/
-	cp $(BUILD_DIR)/macos-amd64/crc packaging/root/"$(MACOS_INSTALL_PATH)"/$(CRC_VERSION)/
+	cp $(HYPERKIT_BUNDLENAME) packaging/root/"$(MACOS_INSTALL_PATH)"
+	cp $(BUILD_DIR)/macos-amd64/crc packaging/root/"$(MACOS_INSTALL_PATH)"
 	cp LICENSE packaging/darwin/Resources/LICENSE.txt
 	pkgbuild --analyze --root packaging/root packaging/components.plist
 	plutil -replace BundleIsRelocatable -bool NO packaging/components.plist
diff --git a/cmd/crc/cmd/console.go b/cmd/crc/cmd/console.go
index 717a811f3..83e6f5c31 100644
--- a/cmd/crc/cmd/console.go
+++ b/cmd/crc/cmd/console.go
@@ -8,6 +8,7 @@ import (
 
 	crcErrors "github.com/code-ready/crc/pkg/crc/errors"
 	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/machine/libmachine/state"
 	"github.com/pkg/browser"
 	"github.com/spf13/cobra"
@@ -36,7 +37,7 @@ var consoleCmd = &cobra.Command{
 	},
 }
 
-func showConsole(client machine.Client) (*machine.ConsoleResult, error) {
+func showConsole(client machine.Client) (*types.ConsoleResult, error) {
 	if err := checkIfMachineMissing(client); err != nil {
 		// In case of machine doesn't exist then consoleResult error
 		// should be updated so that when rendering the result it have
@@ -105,14 +106,14 @@ func (s *consoleResult) prettyPrintTo(writer io.Writer) error {
 	return nil
 }
 
-func toState(result *machine.ConsoleResult) state.State {
+func toState(result *types.ConsoleResult) state.State {
 	if result == nil {
 		return state.Error
 	}
 	return result.State
 }
 
-func toConsoleClusterConfig(result *machine.ConsoleResult) *clusterConfig {
+func toConsoleClusterConfig(result *types.ConsoleResult) *clusterConfig {
 	if result == nil {
 		return nil
 	}
diff --git a/cmd/crc/cmd/daemon.go b/cmd/crc/cmd/daemon.go
index fec7cd2a3..148fd7f2f 100644
--- a/cmd/crc/cmd/daemon.go
+++ b/cmd/crc/cmd/daemon.go
@@ -1,7 +1,9 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
+	"io/ioutil"
 	"net"
 	"net/http"
 	"os"
@@ -14,6 +16,7 @@ import (
 	cmdConfig "github.com/code-ready/crc/cmd/crc/cmd/config"
 	"github.com/code-ready/crc/pkg/crc/api"
 	"github.com/code-ready/crc/pkg/crc/constants"
+	"github.com/code-ready/crc/pkg/crc/daemonclient"
 	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/gvisor-tap-vsock/pkg/types"
 	"github.com/code-ready/gvisor-tap-vsock/pkg/virtualnetwork"
@@ -22,7 +25,10 @@ import (
 	"github.com/spf13/cobra"
 )
 
+var watchdog bool
+
 func init() {
+	daemonCmd.Flags().BoolVar(&watchdog, "watchdog", false, "Monitor stdin and shutdown the daemon if stdin is closed")
 	rootCmd.AddCommand(daemonCmd)
 }
 
@@ -34,6 +40,10 @@ var daemonCmd = &cobra.Command{
 	Long:   "Run the crc daemon",
 	Hidden: true,
 	RunE: func(cmd *cobra.Command, args []string) error {
+		if _, err := daemonclient.New().APIClient.Version(); err == nil {
+			return errors.New("daemon is already running")
+		}
+
 		virtualNetworkConfig := types.Configuration{
 			Debug:             false, // never log packets
 			CaptureFile:       captureFile(),
@@ -152,6 +162,17 @@ func run(configuration *types.Configuration) error {
 	}()
 
 	c := make(chan os.Signal, 1)
+
+	if watchdog {
+		go func() {
+			if _, err := ioutil.ReadAll(os.Stdin); err != nil {
+				logging.Errorf("unexpected error while reading stdin: %v", err)
+			}
+			logging.Error("stdin is closed, shutdown...")
+			c <- syscall.SIGTERM
+		}()
+	}
+
 	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
 	select {
 	case <-c:
diff --git a/cmd/crc/cmd/root.go b/cmd/crc/cmd/root.go
index 9bb02180f..f18991ea4 100644
--- a/cmd/crc/cmd/root.go
+++ b/cmd/crc/cmd/root.go
@@ -164,7 +164,7 @@ func newViperConfig() (*crcConfig.Config, *crcConfig.ViperStorage, error) {
 }
 
 func newMachine() machine.Client {
-	return machine.NewClient(constants.DefaultName, isDebugLog(), config)
+	return machine.NewSynchronizedMachine(machine.NewClient(constants.DefaultName, isDebugLog(), config))
 }
 
 func addForceFlag(cmd *cobra.Command) {
diff --git a/cmd/crc/cmd/start.go b/cmd/crc/cmd/start.go
index 67dcbadb1..97aadf5ad 100644
--- a/cmd/crc/cmd/start.go
+++ b/cmd/crc/cmd/start.go
@@ -15,7 +15,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/constants"
 	crcErrors "github.com/code-ready/crc/pkg/crc/errors"
 	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/preflight"
 	"github.com/code-ready/crc/pkg/crc/validation"
 	crcversion "github.com/code-ready/crc/pkg/crc/version"
@@ -55,14 +55,14 @@ var startCmd = &cobra.Command{
 	},
 }
 
-func runStart(ctx context.Context) (*machine.StartResult, error) {
+func runStart(ctx context.Context) (*types.StartResult, error) {
 	if err := validateStartFlags(); err != nil {
 		return nil, err
 	}
 
 	checkIfNewVersionAvailable(config.Get(cmdConfig.DisableUpdateCheck).AsBool())
 
-	startConfig := machine.StartConfig{
+	startConfig := types.StartConfig{
 		BundlePath: config.Get(cmdConfig.Bundle).AsString(),
 		Memory:     config.Get(cmdConfig.Memory).AsInt(),
 		DiskSize:   config.Get(cmdConfig.DiskSize).AsInt(),
@@ -83,7 +83,7 @@ func runStart(ctx context.Context) (*machine.StartResult, error) {
 	return client.Start(ctx, startConfig)
 }
 
-func renderStartResult(result *machine.StartResult, err error) error {
+func renderStartResult(result *types.StartResult, err error) error {
 	return render(&startResult{
 		Success:       err == nil,
 		Error:         crcErrors.ToSerializableError(err),
@@ -91,7 +91,7 @@ func renderStartResult(result *machine.StartResult, err error) error {
 	}, os.Stdout, outputFormat)
 }
 
-func toClusterConfig(result *machine.StartResult) *clusterConfig {
+func toClusterConfig(result *types.StartResult) *clusterConfig {
 	if result == nil {
 		return nil
 	}
diff --git a/docs/source/topics/proc_setting-up-remote-server.adoc b/docs/source/topics/proc_setting-up-remote-server.adoc
index d9b6449ab..45b56e2b6 100644
--- a/docs/source/topics/proc_setting-up-remote-server.adoc
+++ b/docs/source/topics/proc_setting-up-remote-server.adoc
@@ -31,7 +31,7 @@ Ensure that the cluster remains running throughout this procedure.
 . Install the [package]`haproxy` package and other utilities:
 +
 ----
-$ sudo dnf install haproxy policycoreutils-python-utils jq
+$ sudo dnf install haproxy policycoreutils-python-utils
 ----
 
 . Modify the firewall to allow communication with the cluster:
@@ -63,49 +63,28 @@ $ sudo cp /etc/haproxy/haproxy.cfg{,.bak}
 $ export CRC_IP=$({bin} ip)
 $ sudo tee /etc/haproxy/haproxy.cfg &>/dev/null <<EOF
 global
-    debug
+    log /dev/log local0
 
 defaults
+    balance roundrobin
     log global
-    mode http
-    timeout connect 5000
-    timeout client 500000
-    timeout server 500000
+    maxconn 100
+    mode tcp
+    timeout connect 5s
+    timeout client 500s
+    timeout server 500s
 
-frontend apps
+listen apps
     bind 0.0.0.0:80
-    option tcplog
-    mode tcp
-    default_backend apps
+    server crcvm $CRC_IP:80 check
 
-frontend apps_ssl
+listen apps_ssl
     bind 0.0.0.0:443
-    option tcplog
-    mode tcp
-    default_backend apps_ssl
-
-backend apps
-    mode tcp
-    balance roundrobin
-    server webserver1 $CRC_IP:80 check
+    server crcvm $CRC_IP:443 check
 
-backend apps_ssl
-    mode tcp
-    balance roundrobin
-    option ssl-hello-chk
-    server webserver1 $CRC_IP:443 check
-
-frontend api
+listen api
     bind 0.0.0.0:6443
-    option tcplog
-    mode tcp
-    default_backend api
-
-backend api
-    mode tcp
-    balance roundrobin
-    option ssl-hello-chk
-    server webserver1 $CRC_IP:6443 check
+    server crcvm $CRC_IP:6443 check
 EOF
 ----
 
diff --git a/docs/source/topics/ref_dns-configuration.adoc b/docs/source/topics/ref_dns-configuration.adoc
index 1ff4d1c5e..6f8a6f2cc 100644
--- a/docs/source/topics/ref_dns-configuration.adoc
+++ b/docs/source/topics/ref_dns-configuration.adoc
@@ -11,7 +11,7 @@ The `apps-crc.testing` domain is for accessing OpenShift applications deployed o
 For example, the OpenShift API server will be exposed as `api.crc.testing` while the OpenShift console is accessed through `console-openshift-console.apps-crc.testing`.
 These DNS domains are served by a `dnsmasq` DNS container running inside the {prod} virtual machine.
 
-Running [command]`{bin} setup` will adjust your system DNS configuration so that it can resolve these domains.
+Running [command]`{bin} setup` will detect and adjust your system DNS configuration so that it can resolve these domains.
 Additional checks are done to verify DNS is properly configured when running [command]`{bin} start`.
 
 [id="dns-configuration-linux_{context}"]
@@ -21,7 +21,7 @@ On Linux, depending on your distribution, {prod} expects the following DNS confi
 
 === NetworkManager + systemd-resolved
 
-This configuration is used on Fedora 33 or newer, and on Ubuntu Desktop editions.
+This configuration is used by default on Fedora 33 or newer, and on Ubuntu Desktop editions.
 
 * {prod} expects NetworkManager to manage networking.
 * {prod} configures `systemd-resolved` to forward requests for the `testing` domain to the `192.168.130.11` DNS server.
@@ -30,29 +30,23 @@ This configuration is used on Fedora 33 or newer, and on Ubuntu Desktop editions
 +
 ----
 #!/bin/sh
-resolvectl domain crc ~testing
-resolvectl dns crc 192.168.130.11
-resolvectl default-route crc false
+
+export LC_ALL=C
+
+systemd-resolve --interface crc --set-dns 192.168.130.11 --set-domain ~testing
 
 exit 0
 ----
 
 [NOTE]
 ====
-`systemd-resolved` is also available as an unsupported Technology Preview on {rhel} and {centos} 8.3. After {rhel-resolved-docs}[configuring the host] to use `systemd-resolved`, any {prod} `dnsmasq` configurations need to be removed and the above script needs to be created with executable permissions.
-
-Configure the {prod} binary to skip startup checks for `dnsmasq` and NetworkManager configuration:
-
-[subs="+quotes,attributes"]
-----
-$ {bin} config set skip-check-crc-dnsmasq-file true
-$ {bin} config set skip-check-network-manager-config true
-----
+`systemd-resolved` is also available as an unsupported Technology Preview on {rhel} and {centos} 8.3.
+After {rhel-resolved-docs}[configuring the host] to use `systemd-resolved`, stop any running clusters and rerun [command]`{bin} setup`.
 ====
 
 === NetworkManager + dnsmasq
 
-This configuration is used on Fedora 32 or older, on {rhel}, and on {centos}.
+This configuration is used by default on Fedora 32 or older, on {rhel}, and on {centos}.
 
 * {prod} expects NetworkManager to manage networking.
 * NetworkManager uses `dnsmasq` through the [filename]`/etc/NetworkManager/conf.d/crc-nm-dnsmasq.conf` configuration file.
diff --git a/packaging/.gitignore b/packaging/.gitignore
index ff7927c1d..1b86fe5fd 100644
--- a/packaging/.gitignore
+++ b/packaging/.gitignore
@@ -4,3 +4,4 @@ root/
 /darwin/Resources/LICENSE.txt
 /darwin/Resources/welcome.html
 /darwin/scripts/postinstall
+/VERSION
\ No newline at end of file
diff --git a/packaging/darwin/postinstall.in b/packaging/darwin/postinstall.in
index fe350332a..8f423a1d4 100755
--- a/packaging/darwin/postinstall.in
+++ b/packaging/darwin/postinstall.in
@@ -1,10 +1,10 @@
 #!/bin/bash
 set -e
 
-chmod 755 "__INSTALL_PATH__"/__VERSION__/crc
+chmod 755 "__INSTALL_PATH__"/crc
 
 [ -d /usr/local/bin ] || mkdir /usr/local/bin
 
 rm -f /usr/local/bin/crc
 
-ln -s "__INSTALL_PATH__"/__VERSION__/crc /usr/local/bin/crc
+ln -s "__INSTALL_PATH__"/crc /usr/local/bin/crc
diff --git a/packaging/package.sh b/packaging/package.sh
index 48935b083..065d2dfdd 100755
--- a/packaging/package.sh
+++ b/packaging/package.sh
@@ -19,17 +19,19 @@ function sign() {
   codesign --deep --sign "${CODESIGN_IDENTITY}" --options runtime --force ${opts} "$1"
 }
 
-version=$(ls "${BASEDIR}/root/Library/CodeReady Containers/")
+binDir="${BASEDIR}/root/Applications/CodeReady Containers.app/Contents/Resources"
+
+version=$(cat "${BASEDIR}/VERSION")
 
-binDir="${BASEDIR}/root/Library/CodeReady Containers/${version}"
 sign "${binDir}/crc"
 sign "${binDir}/admin-helper-darwin"
 sign "${binDir}/crc-driver-hyperkit"
-sign "${binDir}/CodeReady Containers.app"
+
+sign "${BASEDIR}/root/Applications/CodeReady Containers.app"
 
 codesign --verify --verbose "${binDir}/hyperkit"
 
-pkgbuild --identifier com.redhat.crc.${version} --version ${version} \
+pkgbuild --identifier com.redhat.crc --version ${version} \
   --scripts "${BASEDIR}/darwin/scripts" \
   --root "${BASEDIR}/root" \
   --install-location / \
diff --git a/pkg/crc/api/adapter.go b/pkg/crc/api/adapter.go
index 4abcb41e8..21bb94bb6 100644
--- a/pkg/crc/api/adapter.go
+++ b/pkg/crc/api/adapter.go
@@ -6,6 +6,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/api/client"
 	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/machine/libmachine/state"
 )
 
@@ -13,7 +14,7 @@ type AdaptedClient interface {
 	GetName() string
 	Delete() client.Result
 	GetConsoleURL() client.ConsoleResult
-	Start(ctx context.Context, startConfig machine.StartConfig) client.StartResult
+	Start(ctx context.Context, startConfig types.StartConfig) client.StartResult
 	Status() client.ClusterStatusResult
 	Stop() client.Result
 }
@@ -57,7 +58,7 @@ func (a *Adapter) GetConsoleURL() client.ConsoleResult {
 	}
 }
 
-func (a *Adapter) Start(ctx context.Context, startConfig machine.StartConfig) client.StartResult {
+func (a *Adapter) Start(ctx context.Context, startConfig types.StartConfig) client.StartResult {
 	res, err := a.Underlying.Start(ctx, startConfig)
 	if err != nil {
 		logging.Error(err)
diff --git a/pkg/crc/api/api.go b/pkg/crc/api/api.go
index 7b7953b37..d55e0ff34 100644
--- a/pkg/crc/api/api.go
+++ b/pkg/crc/api/api.go
@@ -22,8 +22,7 @@ func CreateServer(socketPath string, config crcConfig.Storage, machine machine.C
 
 func createServerWithListener(listener net.Listener, config crcConfig.Storage, machine machine.Client, logger Logger) (Server, error) {
 	apiServer := Server{
-		listener:               listener,
-		clusterOpsRequestsChan: make(chan clusterOpsRequest, 10),
+		listener: listener,
 		handler: &Handler{
 			Config:        config,
 			MachineClient: &Adapter{Underlying: machine},
@@ -34,7 +33,6 @@ func createServerWithListener(listener net.Listener, config crcConfig.Storage, m
 }
 
 func (api Server) Serve() error {
-	go api.handleClusterOperations() // go routine that handles start, stop and delete calls
 	for {
 		conn, err := api.listener.Accept()
 		if err != nil {
@@ -48,12 +46,6 @@ func (api Server) Serve() error {
 	}
 }
 
-func (api Server) handleClusterOperations() {
-	for req := range api.clusterOpsRequestsChan {
-		api.handleRequest(req.command, req.socket)
-	}
-}
-
 func (api Server) handleRequest(req commandRequest, conn net.Conn) {
 	defer conn.Close()
 	var result string
@@ -99,32 +91,7 @@ func (api Server) handleConnections(conn net.Conn) {
 		logging.Error("Error decoding request: ", err.Error())
 		return
 	}
-	// start, stop and delete are slow operations, and change the VM state so they have to run sequentially.
-	// We don't want other operations querying the status of the VM to be blocked by these,
-	// so they are treated by a dedicated go routine
-
-	switch req.Command {
-	case "start", "stop", "delete":
-		// queue new request to channel
-		r := clusterOpsRequest{
-			command: req,
-			socket:  conn,
-		}
-		if !addRequestToChannel(r, api.clusterOpsRequestsChan) {
-			logging.Error("Channel capacity reached, unable to add new request")
-			errMsg := encodeErrorToJSON("Sockets channel capacity reached, unable to add new request")
-			writeStringToSocket(conn, errMsg)
-			conn.Close()
-		}
-
-	case "status", "version", "setconfig", "getconfig", "unsetconfig", "webconsoleurl", "logs":
-		go api.handleRequest(req, conn)
-
-	default:
-		err := encodeErrorToJSON(fmt.Sprintf("Unknown command supplied: %s", req.Command))
-		writeStringToSocket(conn, err)
-		conn.Close()
-	}
+	go api.handleRequest(req, conn)
 }
 
 func writeStringToSocket(socket net.Conn, msg string) {
@@ -142,12 +109,3 @@ func writeStringToSocket(socket net.Conn, msg string) {
 		return
 	}
 }
-
-func addRequestToChannel(req clusterOpsRequest, requestsChan chan clusterOpsRequest) bool {
-	select {
-	case requestsChan <- req:
-		return true
-	default:
-		return false
-	}
-}
diff --git a/pkg/crc/api/client/client.go b/pkg/crc/api/client/client.go
index ed34ef7de..a6c22bc42 100644
--- a/pkg/crc/api/client/client.go
+++ b/pkg/crc/api/client/client.go
@@ -7,8 +7,6 @@ import (
 	"io"
 	"io/ioutil"
 	"net/http"
-
-	"github.com/code-ready/crc/pkg/crc/logging"
 )
 
 type Client struct {
@@ -27,12 +25,10 @@ func (c *Client) Version() (VersionResult, error) {
 	var vr = VersionResult{}
 	body, err := c.sendGetRequest("/version")
 	if err != nil {
-		logging.Error(err)
 		return vr, err
 	}
 	err = json.Unmarshal(body, &vr)
 	if err != nil {
-		logging.Error(err)
 		return vr, err
 	}
 	return vr, nil
@@ -42,12 +38,10 @@ func (c *Client) Status() (ClusterStatusResult, error) {
 	var sr = ClusterStatusResult{}
 	body, err := c.sendGetRequest("/status")
 	if err != nil {
-		logging.Error(err)
 		return sr, err
 	}
 	err = json.Unmarshal(body, &sr)
 	if err != nil {
-		logging.Error(err)
 		return sr, err
 	}
 	return sr, nil
@@ -64,12 +58,10 @@ func (c *Client) Start(config StartConfig) (StartResult, error) {
 	}
 	body, err := c.sendPostRequest("/start", data)
 	if err != nil {
-		logging.Error(err)
 		return sr, err
 	}
 	err = json.Unmarshal(body, &sr)
 	if err != nil {
-		logging.Error(err)
 		return sr, err
 	}
 	return sr, nil
@@ -79,12 +71,10 @@ func (c *Client) Stop() (Result, error) {
 	var sr = Result{}
 	body, err := c.sendGetRequest("/stop")
 	if err != nil {
-		logging.Error(err)
 		return sr, err
 	}
 	err = json.Unmarshal(body, &sr)
 	if err != nil {
-		logging.Error(err)
 		return sr, err
 	}
 	return sr, nil
@@ -94,12 +84,10 @@ func (c *Client) Delete() (Result, error) {
 	var dr = Result{}
 	body, err := c.sendGetRequest("/delete")
 	if err != nil {
-		logging.Error(err)
 		return dr, err
 	}
 	err = json.Unmarshal(body, &dr)
 	if err != nil {
-		logging.Error(err)
 		return dr, err
 	}
 	return dr, nil
@@ -109,12 +97,10 @@ func (c *Client) WebconsoleURL() (ConsoleResult, error) {
 	var cr = ConsoleResult{}
 	body, err := c.sendGetRequest("/webconsoleurl")
 	if err != nil {
-		logging.Error(err)
 		return cr, err
 	}
 	err = json.Unmarshal(body, &cr)
 	if err != nil {
-		logging.Error(err)
 		return cr, err
 	}
 	return cr, nil
@@ -134,12 +120,10 @@ func (c *Client) GetConfig(configs []string) (GetConfigResult, error) {
 	}
 	body, err := c.sendPostRequest("/config/get", data)
 	if err != nil {
-		logging.Error(err)
 		return gcr, err
 	}
 	err = json.Unmarshal(body, &gcr)
 	if err != nil {
-		logging.Error(err)
 		return gcr, err
 	}
 	return gcr, nil
@@ -159,7 +143,6 @@ func (c *Client) SetConfig(configs SetConfigRequest) (SetOrUnsetConfigResult, er
 
 	body, err := c.sendPostRequest("/config/set", data)
 	if err != nil {
-		logging.Error(err)
 		return scr, err
 	}
 
@@ -182,12 +165,10 @@ func (c *Client) UnsetConfig(configs []string) (SetOrUnsetConfigResult, error) {
 	}
 	body, err := c.sendPostRequest("/config/unset", data)
 	if err != nil {
-		logging.Error(err)
 		return ucr, err
 	}
 	err = json.Unmarshal(body, &ucr)
 	if err != nil {
-		logging.Error(err)
 		return ucr, err
 	}
 	return ucr, nil
@@ -196,7 +177,6 @@ func (c *Client) UnsetConfig(configs []string) (SetOrUnsetConfigResult, error) {
 func (c *Client) sendGetRequest(url string) ([]byte, error) {
 	res, err := c.client.Get(fmt.Sprintf("%s%s", c.base, url))
 	if err != nil {
-		logging.Error(err)
 		return nil, err
 	}
 	if res.StatusCode != http.StatusOK {
@@ -219,7 +199,6 @@ func (c *Client) sendPostRequest(url string, data io.Reader) ([]byte, error) {
 
 	res, err := c.client.Do(req)
 	if err != nil {
-		logging.Error(err)
 		return nil, err
 	}
 	if res.StatusCode != http.StatusOK {
diff --git a/pkg/crc/api/client/types.go b/pkg/crc/api/client/types.go
index 5767ae969..9acd8922b 100644
--- a/pkg/crc/api/client/types.go
+++ b/pkg/crc/api/client/types.go
@@ -1,7 +1,7 @@
 package client
 
 import (
-	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 )
 
 type VersionResult struct {
@@ -21,7 +21,7 @@ type StartResult struct {
 	Name           string
 	Status         string
 	Error          string
-	ClusterConfig  machine.ClusterConfig
+	ClusterConfig  types.ClusterConfig
 	KubeletStarted bool
 }
 
@@ -37,7 +37,7 @@ type ClusterStatusResult struct {
 }
 
 type ConsoleResult struct {
-	ClusterConfig machine.ClusterConfig
+	ClusterConfig types.ClusterConfig
 	Success       bool
 	Error         string
 }
diff --git a/pkg/crc/api/handlers.go b/pkg/crc/api/handlers.go
index d862cb54b..729f08f5e 100644
--- a/pkg/crc/api/handlers.go
+++ b/pkg/crc/api/handlers.go
@@ -13,6 +13,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/errors"
 	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/preflight"
 	"github.com/code-ready/crc/pkg/crc/version"
 )
@@ -89,8 +90,8 @@ func parseStartArgs(args json.RawMessage) (startArgs, error) {
 	return parsedArgs, nil
 }
 
-func getStartConfig(cfg crcConfig.Storage, args startArgs) machine.StartConfig {
-	return machine.StartConfig{
+func getStartConfig(cfg crcConfig.Storage, args startArgs) types.StartConfig {
+	return types.StartConfig{
 		BundlePath: cfg.Get(config.Bundle).AsString(),
 		Memory:     cfg.Get(config.Memory).AsInt(),
 		CPUs:       cfg.Get(config.CPUs).AsInt(),
diff --git a/pkg/crc/api/types.go b/pkg/crc/api/types.go
index c4aa6cac2..6bcee06d7 100644
--- a/pkg/crc/api/types.go
+++ b/pkg/crc/api/types.go
@@ -6,9 +6,8 @@ import (
 )
 
 type Server struct {
-	handler                RequestHandler
-	listener               net.Listener
-	clusterOpsRequestsChan chan clusterOpsRequest
+	handler  RequestHandler
+	listener net.Listener
 }
 
 type RequestHandler interface {
@@ -24,12 +23,6 @@ type RequestHandler interface {
 	Logs() string
 }
 
-// clusterOpsRequest struct is used to store the command request and associated socket
-type clusterOpsRequest struct {
-	command commandRequest
-	socket  net.Conn
-}
-
 // commandRequest struct is used to decode the json request from tray
 type commandRequest struct {
 	Command string          `json:"command"`
diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go
index 447f77687..a090dd0dd 100644
--- a/pkg/crc/constants/constants.go
+++ b/pkg/crc/constants/constants.go
@@ -106,7 +106,7 @@ var (
 
 func defaultBundlePath() string {
 	if runtime.GOOS == "darwin" && version.IsMacosInstallPathSet() {
-		path := filepath.Join(version.GetMacosInstallPath(), version.GetCRCVersion(), GetDefaultBundle())
+		path := filepath.Join(version.GetMacosInstallPath(), GetDefaultBundle())
 		if _, err := os.Stat(path); err == nil {
 			return path
 		}
diff --git a/pkg/crc/constants/constants_darwin.go b/pkg/crc/constants/constants_darwin.go
index 152c820d2..618345643 100644
--- a/pkg/crc/constants/constants_darwin.go
+++ b/pkg/crc/constants/constants_darwin.go
@@ -23,7 +23,7 @@ var (
 
 func trayAppBundlePath() string {
 	if version.IsMacosInstallPathSet() {
-		path := filepath.Join(version.GetMacosInstallPath(), version.GetCRCVersion(), TrayExecutableName)
+		path := filepath.Join(filepath.Dir(filepath.Dir(version.GetMacosInstallPath())), TrayExecutableName)
 		if _, err := os.Stat(path); err == nil {
 			return path
 		}
diff --git a/pkg/crc/daemonclient/client.go b/pkg/crc/daemonclient/client.go
index aa866634a..7df516385 100644
--- a/pkg/crc/daemonclient/client.go
+++ b/pkg/crc/daemonclient/client.go
@@ -3,17 +3,22 @@ package daemonclient
 import (
 	"net/http"
 
+	"github.com/code-ready/crc/pkg/crc/api/client"
 	networkclient "github.com/code-ready/gvisor-tap-vsock/pkg/client"
 )
 
 type Client struct {
-	*networkclient.Client
+	NetworkClient *networkclient.Client
+	APIClient     *client.Client
 }
 
 func New() *Client {
 	return &Client{
-		Client: networkclient.New(&http.Client{
+		NetworkClient: networkclient.New(&http.Client{
 			Transport: transport(),
 		}, "http://unix/network"),
+		APIClient: client.New(&http.Client{
+			Transport: transport(),
+		}, "http://unix/api"),
 	}
 }
diff --git a/pkg/crc/machine/client.go b/pkg/crc/machine/client.go
index b3770047f..58101cd2e 100644
--- a/pkg/crc/machine/client.go
+++ b/pkg/crc/machine/client.go
@@ -5,6 +5,7 @@ import (
 
 	cmdConfig "github.com/code-ready/crc/cmd/crc/cmd/config"
 	crcConfig "github.com/code-ready/crc/pkg/crc/config"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/machine/libmachine/state"
 )
@@ -14,11 +15,11 @@ type Client interface {
 
 	Delete() error
 	Exists() (bool, error)
-	GetConsoleURL() (*ConsoleResult, error)
+	GetConsoleURL() (*types.ConsoleResult, error)
 	IP() (string, error)
 	PowerOff() error
-	Start(ctx context.Context, startConfig StartConfig) (*StartResult, error)
-	Status() (*ClusterStatusResult, error)
+	Start(ctx context.Context, startConfig types.StartConfig) (*types.StartResult, error)
+	Status() (*types.ClusterStatusResult, error)
 	Stop() (state.State, error)
 	IsRunning() (bool, error)
 }
diff --git a/pkg/crc/machine/console.go b/pkg/crc/machine/console.go
index c5c1a5ff0..54b216938 100644
--- a/pkg/crc/machine/console.go
+++ b/pkg/crc/machine/console.go
@@ -1,9 +1,12 @@
 package machine
 
-import "github.com/pkg/errors"
+import (
+	"github.com/code-ready/crc/pkg/crc/machine/types"
+	"github.com/pkg/errors"
+)
 
 // Return console URL if the VM is present.
-func (client *client) GetConsoleURL() (*ConsoleResult, error) {
+func (client *client) GetConsoleURL() (*types.ConsoleResult, error) {
 	// Here we are only checking if the VM exist and not the status of the VM.
 	// We might need to improve and use crc status logic, only
 	// return if the Openshift is running as part of status.
@@ -29,7 +32,7 @@ func (client *client) GetConsoleURL() (*ConsoleResult, error) {
 		return nil, errors.Wrap(err, "Error loading cluster configuration")
 	}
 
-	return &ConsoleResult{
+	return &types.ConsoleResult{
 		ClusterConfig: *clusterConfig,
 		State:         vmState,
 	}, nil
diff --git a/pkg/crc/machine/fakemachine/client.go b/pkg/crc/machine/fakemachine/client.go
index 64387aece..0e9ecb9ec 100644
--- a/pkg/crc/machine/fakemachine/client.go
+++ b/pkg/crc/machine/fakemachine/client.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"errors"
 
-	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/machine/libmachine/state"
 )
@@ -23,7 +23,7 @@ type Client struct {
 	Failing bool
 }
 
-var DummyClusterConfig = machine.ClusterConfig{
+var DummyClusterConfig = types.ClusterConfig{
 	ClusterCACert: "MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ",
 	KubeConfig:    "/tmp/kubeconfig",
 	KubeAdminPass: "foobar",
@@ -43,11 +43,11 @@ func (c *Client) Delete() error {
 	return nil
 }
 
-func (c *Client) GetConsoleURL() (*machine.ConsoleResult, error) {
+func (c *Client) GetConsoleURL() (*types.ConsoleResult, error) {
 	if c.Failing {
 		return nil, errors.New("console failed")
 	}
-	return &machine.ConsoleResult{
+	return &types.ConsoleResult{
 		ClusterConfig: DummyClusterConfig,
 		State:         state.Running,
 	}, nil
@@ -68,11 +68,11 @@ func (c *Client) PowerOff() error {
 	return nil
 }
 
-func (c *Client) Start(ctx context.Context, startConfig machine.StartConfig) (*machine.StartResult, error) {
+func (c *Client) Start(ctx context.Context, startConfig types.StartConfig) (*types.StartResult, error) {
 	if c.Failing {
 		return nil, errors.New("Failed to start")
 	}
-	return &machine.StartResult{
+	return &types.StartResult{
 		ClusterConfig:  DummyClusterConfig,
 		KubeletStarted: true,
 	}, nil
@@ -85,11 +85,11 @@ func (c *Client) Stop() (state.State, error) {
 	return state.Stopped, nil
 }
 
-func (c *Client) Status() (*machine.ClusterStatusResult, error) {
+func (c *Client) Status() (*types.ClusterStatusResult, error) {
 	if c.Failing {
 		return nil, errors.New("broken")
 	}
-	return &machine.ClusterStatusResult{
+	return &types.ClusterStatusResult{
 		CrcStatus:        state.Running,
 		OpenshiftStatus:  "Running",
 		OpenshiftVersion: "4.5.1",
diff --git a/pkg/crc/machine/kubeconfig.go b/pkg/crc/machine/kubeconfig.go
index a398f03b6..fd6b950bd 100644
--- a/pkg/crc/machine/kubeconfig.go
+++ b/pkg/crc/machine/kubeconfig.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/code-ready/crc/pkg/crc/constants"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/openshift/oc/pkg/helpers/tokencmd"
 	restclient "k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
@@ -25,7 +26,7 @@ const (
 	developerContext = "crc-developer"
 )
 
-func writeKubeconfig(ip string, clusterConfig *ClusterConfig) error {
+func writeKubeconfig(ip string, clusterConfig *types.ClusterConfig) error {
 	kubeconfig := getGlobalKubeConfigPath()
 	dir := filepath.Dir(kubeconfig)
 	if err := os.MkdirAll(dir, 0755); err != nil {
@@ -86,7 +87,7 @@ func hostname(clusterAPI string) (string, error) {
 	return p.Host, nil
 }
 
-func addContext(cfg *api.Config, ip string, clusterConfig *ClusterConfig, ca []byte, context, username, password string) error {
+func addContext(cfg *api.Config, ip string, clusterConfig *types.ClusterConfig, ca []byte, context, username, password string) error {
 	host, err := hostname(clusterConfig.ClusterAPI)
 	if err != nil {
 		return err
diff --git a/pkg/crc/machine/machine.go b/pkg/crc/machine/machine.go
index ae7fdd3df..01dfbd189 100644
--- a/pkg/crc/machine/machine.go
+++ b/pkg/crc/machine/machine.go
@@ -6,13 +6,14 @@ import (
 
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/machine/bundle"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/libmachine"
 	"github.com/code-ready/crc/pkg/libmachine/host"
 	"github.com/code-ready/machine/libmachine/drivers"
 )
 
-func getClusterConfig(bundleInfo *bundle.CrcBundleInfo, generatedKubeadminPassword string) (*ClusterConfig, error) {
+func getClusterConfig(bundleInfo *bundle.CrcBundleInfo, generatedKubeadminPassword string) (*types.ClusterConfig, error) {
 	kubeadminPassword, err := bundleInfo.GetKubeadminPassword()
 	if err != nil {
 		return nil, fmt.Errorf("Error reading kubeadmin password from bundle %v", err)
@@ -28,7 +29,7 @@ func getClusterConfig(bundleInfo *bundle.CrcBundleInfo, generatedKubeadminPasswo
 	if err != nil {
 		return nil, err
 	}
-	return &ClusterConfig{
+	return &types.ClusterConfig{
 		ClusterCACert: base64.StdEncoding.EncodeToString(clusterCACert),
 		KubeConfig:    bundleInfo.GetKubeConfigPath(),
 		KubeAdminPass: kubeadminPassword,
diff --git a/pkg/crc/machine/start.go b/pkg/crc/machine/start.go
index 1716f5ea0..0096c1169 100644
--- a/pkg/crc/machine/start.go
+++ b/pkg/crc/machine/start.go
@@ -17,6 +17,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/crc/pkg/crc/machine/bundle"
 	"github.com/code-ready/crc/pkg/crc/machine/config"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/crc/oc"
 	"github.com/code-ready/crc/pkg/crc/services"
@@ -52,7 +53,7 @@ func getCrcBundleInfo(bundlePath string) (*bundle.CrcBundleInfo, error) {
 	return bundle.Use(bundleName)
 }
 
-func (client *client) updateVMConfig(startConfig StartConfig, api libmachine.API, host *host.Host) error {
+func (client *client) updateVMConfig(startConfig types.StartConfig, api libmachine.API, host *host.Host) error {
 	/* Memory */
 	logging.Debugf("Updating CRC VM configuration")
 	if err := setMemory(host, startConfig.Memory); err != nil {
@@ -128,7 +129,7 @@ func growRootFileSystem(sshRunner *crcssh.Runner) error {
 
 	return nil
 }
-func (client *client) Start(ctx context.Context, startConfig StartConfig) (*StartResult, error) {
+func (client *client) Start(ctx context.Context, startConfig types.StartConfig) (*types.StartResult, error) {
 	telemetry.SetCPUs(ctx, startConfig.CPUs)
 	telemetry.SetMemory(ctx, uint64(startConfig.Memory)*1024*1024)
 	telemetry.SetDiskSize(ctx, uint64(startConfig.DiskSize)*1024*1024*1024)
@@ -228,7 +229,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 			}
 
 			telemetry.SetStartType(ctx, telemetry.AlreadyRunningStartType)
-			return &StartResult{
+			return &types.StartResult{
 				Status:         vmState,
 				ClusterConfig:  *clusterConfig,
 				KubeletStarted: true,
@@ -459,7 +460,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 		logging.Errorf("Cannot update kubeconfig: %v", err)
 	}
 
-	return &StartResult{
+	return &types.StartResult{
 		KubeletStarted: true,
 		ClusterConfig:  *clusterConfig,
 		Status:         vmState,
@@ -487,7 +488,7 @@ func (client *client) IsRunning() (bool, error) {
 	return true, nil
 }
 
-func (client *client) validateStartConfig(startConfig StartConfig) error {
+func (client *client) validateStartConfig(startConfig types.StartConfig) error {
 	if client.monitoringEnabled() && startConfig.Memory < minimumMemoryForMonitoring {
 		return fmt.Errorf("Too little memory (%s) allocated to the virtual machine to start the monitoring stack, %s is the minimum",
 			units.BytesSize(float64(startConfig.Memory)*1024*1024),
diff --git a/pkg/crc/machine/status.go b/pkg/crc/machine/status.go
index 43630ae04..d1fae2a77 100644
--- a/pkg/crc/machine/status.go
+++ b/pkg/crc/machine/status.go
@@ -4,13 +4,14 @@ import (
 	"github.com/code-ready/crc/pkg/crc/cluster"
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/oc"
 	crcssh "github.com/code-ready/crc/pkg/crc/ssh"
 	"github.com/code-ready/machine/libmachine/state"
 	"github.com/pkg/errors"
 )
 
-func (client *client) Status() (*ClusterStatusResult, error) {
+func (client *client) Status() (*types.ClusterStatusResult, error) {
 	libMachineAPIClient, cleanup := createLibMachineClient()
 	defer cleanup()
 
@@ -34,7 +35,7 @@ func (client *client) Status() (*ClusterStatusResult, error) {
 	}
 
 	if vmStatus != state.Running {
-		return &ClusterStatusResult{
+		return &types.ClusterStatusResult{
 			CrcStatus:        vmStatus,
 			OpenshiftStatus:  "Stopped",
 			OpenshiftVersion: crcBundleMetadata.GetOpenshiftVersion(),
@@ -55,7 +56,7 @@ func (client *client) Status() (*ClusterStatusResult, error) {
 	if err != nil {
 		logging.Debugf("Cannot get root partition usage: %v", err)
 	}
-	return &ClusterStatusResult{
+	return &types.ClusterStatusResult{
 		CrcStatus:        state.Running,
 		OpenshiftStatus:  getOpenShiftStatus(sshRunner, client.monitoringEnabled()),
 		OpenshiftVersion: crcBundleMetadata.GetOpenshiftVersion(),
diff --git a/pkg/crc/machine/sync.go b/pkg/crc/machine/sync.go
new file mode 100644
index 000000000..5099e5781
--- /dev/null
+++ b/pkg/crc/machine/sync.go
@@ -0,0 +1,171 @@
+package machine
+
+import (
+	"context"
+	"errors"
+	"sync"
+	"time"
+
+	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
+	"github.com/code-ready/machine/libmachine/state"
+)
+
+const startCancelTimeout = 15 * time.Second
+
+type State string
+
+const (
+	Idle     State = "Idle"
+	Deleting State = "Deleting"
+	Stopping State = "Stopping"
+	Starting State = "Starting"
+)
+
+type Synchronized struct {
+	underlying Client
+
+	stateLock    sync.Mutex
+	currentState State
+	startCancel  context.CancelFunc
+
+	syncOperationDone chan State
+}
+
+func NewSynchronizedMachine(machine Client) *Synchronized {
+	return &Synchronized{
+		underlying:        machine,
+		currentState:      Idle,
+		syncOperationDone: make(chan State, 1),
+	}
+}
+
+func (s *Synchronized) CurrentState() State {
+	s.stateLock.Lock()
+	defer s.stateLock.Unlock()
+
+	return s.currentStateUnlocked()
+}
+
+func (s *Synchronized) currentStateUnlocked() State {
+	select {
+	case st := <-s.syncOperationDone:
+		if s.currentState == st {
+			s.currentState = Idle
+		}
+		if st == Starting {
+			s.startCancel = nil
+		}
+	default:
+	}
+	return s.currentState
+}
+
+func (s *Synchronized) Delete() error {
+	if err := s.prepareStopDelete(Deleting); err != nil {
+		return err
+	}
+
+	err := s.underlying.Delete()
+	s.syncOperationDone <- Deleting
+	return err
+}
+
+func (s *Synchronized) prepareStart(startCancel context.CancelFunc) error {
+	s.stateLock.Lock()
+	defer s.stateLock.Unlock()
+	if s.currentStateUnlocked() != Idle {
+		return errors.New("cluster is busy")
+	}
+	s.startCancel = startCancel
+	s.currentState = Starting
+
+	return nil
+}
+
+func (s *Synchronized) Start(ctx context.Context, startConfig types.StartConfig) (*types.StartResult, error) {
+	ctx, startCancel := context.WithCancel(ctx)
+	if err := s.prepareStart(startCancel); err != nil {
+		return nil, err
+	}
+
+	startResult, err := s.underlying.Start(ctx, startConfig)
+	s.syncOperationDone <- Starting
+	return startResult, err
+}
+
+/* cancel ongoing start, and wait until the start is fully cancelled. Time out if cancellation takes more than 'timeout'
+ * s.stateLock must be locked before calling this function
+ */
+func (s *Synchronized) cancelUnlocked(timeout time.Duration) error {
+	if s.startCancel != nil {
+		logging.Infof("Cancelling virtual machine start...")
+		s.startCancel()
+	}
+	select {
+	case <-s.syncOperationDone:
+		return nil
+	case <-time.After(timeout):
+		return errors.New("cannot abort startup sequence quickly enough")
+	}
+}
+
+func (s *Synchronized) prepareStopDelete(state State) error {
+	s.stateLock.Lock()
+	defer s.stateLock.Unlock()
+
+	switch s.currentStateUnlocked() {
+	case Starting:
+		if err := s.cancelUnlocked(startCancelTimeout); err != nil {
+			return err
+		}
+	case Idle:
+		break
+	case Deleting, Stopping:
+		return errors.New("cluster is stopping or deleting")
+	default:
+		return errors.New("invalid condition")
+	}
+
+	s.currentState = state
+	return nil
+}
+
+func (s *Synchronized) Stop() (state.State, error) {
+	if err := s.prepareStopDelete(Stopping); err != nil {
+		return state.Error, err
+	}
+
+	st, err := s.underlying.Stop()
+	s.syncOperationDone <- Stopping
+
+	return st, err
+}
+
+func (s *Synchronized) GetName() string {
+	return s.underlying.GetName()
+}
+
+func (s *Synchronized) Exists() (bool, error) {
+	return s.underlying.Exists()
+}
+
+func (s *Synchronized) GetConsoleURL() (*types.ConsoleResult, error) {
+	return s.underlying.GetConsoleURL()
+}
+
+func (s *Synchronized) IP() (string, error) {
+	return s.underlying.IP()
+}
+
+func (s *Synchronized) PowerOff() error {
+	return s.underlying.PowerOff()
+}
+
+func (s *Synchronized) Status() (*types.ClusterStatusResult, error) {
+	return s.underlying.Status()
+}
+
+func (s *Synchronized) IsRunning() (bool, error) {
+	return s.underlying.IsRunning()
+}
diff --git a/pkg/crc/machine/types.go b/pkg/crc/machine/types/types.go
similarity index 98%
rename from pkg/crc/machine/types.go
rename to pkg/crc/machine/types/types.go
index 196bd07fa..6bb212e75 100644
--- a/pkg/crc/machine/types.go
+++ b/pkg/crc/machine/types/types.go
@@ -1,4 +1,4 @@
-package machine
+package types
 
 import (
 	"github.com/code-ready/crc/pkg/crc/cluster"
diff --git a/pkg/crc/machine/vsock.go b/pkg/crc/machine/vsock.go
index b45b030c2..b09601ec5 100644
--- a/pkg/crc/machine/vsock.go
+++ b/pkg/crc/machine/vsock.go
@@ -12,7 +12,7 @@ import (
 func exposePorts() error {
 	daemonClient := daemonclient.New()
 	portsToExpose := vsockPorts()
-	alreadyOpenedPorts, err := daemonClient.List()
+	alreadyOpenedPorts, err := daemonClient.NetworkClient.List()
 	if err != nil {
 		return err
 	}
@@ -24,7 +24,7 @@ func exposePorts() error {
 	}
 	for i := range missingPorts {
 		port := &missingPorts[i]
-		if err := daemonClient.Expose(port); err != nil {
+		if err := daemonClient.NetworkClient.Expose(port); err != nil {
 			return errors.Wrapf(err, "failed to expose port %s -> %s", port.Local, port.Remote)
 		}
 	}
diff --git a/pkg/crc/preflight/preflight_checks_network_linux.go b/pkg/crc/preflight/preflight_checks_network_linux.go
index 385c98227..1171154e6 100644
--- a/pkg/crc/preflight/preflight_checks_network_linux.go
+++ b/pkg/crc/preflight/preflight_checks_network_linux.go
@@ -80,9 +80,13 @@ dns=dnsmasq
 #
 # The corresponding crc bridge is not created through NetworkManager, so
 # it cannot be configured permanently through NetworkManager. We make the
-# change directly using resolvectl instead.
+# change directly using systemd-resolve instead.
 #
-# NetworkManager will overwrite this resolvectl configuration every time a
+# systemd-resolve is used instead of resolvectl due to distributions shipping
+# systemd releases older than 239 not having the newer renamed tool. resolvectl
+# supports being called as systemd-resolve, correctly handling the old CLI.
+#
+# NetworkManager will overwrite this systemd-resolve configuration every time a
 # network connection goes up/down, so we run this script on each of these events
 # to restore our settings. This is a NetworkManager bug which is fixed in
 # version 1.26.6 by this commit:
@@ -90,9 +94,7 @@ dns=dnsmasq
 
 export LC_ALL=C
 
-resolvectl domain crc ~testing
-resolvectl dns crc 192.168.130.11
-resolvectl default-route crc false
+systemd-resolve --interface crc --set-dns 192.168.130.11 --set-domain ~testing
 
 exit 0
 `
diff --git a/pkg/crc/preflight/preflight_linux.go b/pkg/crc/preflight/preflight_linux.go
index b589ea2d9..c06c7d27a 100644
--- a/pkg/crc/preflight/preflight_linux.go
+++ b/pkg/crc/preflight/preflight_linux.go
@@ -217,16 +217,18 @@ func removeVsockCrcSettings() error {
 }
 
 func getAllPreflightChecks() []Check {
-	checks := getPreflightChecksForDistro(distro(), network.DefaultMode)
+	usingSystemdResolved := checkSystemdResolvedIsRunning()
+	checks := getPreflightChecksForDistro(distro(), network.DefaultMode, usingSystemdResolved == nil)
 	checks = append(checks, vsockPreflightChecks)
 	return checks
 }
 
 func getPreflightChecks(_ bool, _ bool, networkMode network.Mode) []Check {
-	return getPreflightChecksForDistro(distro(), networkMode)
+	usingSystemdResolved := checkSystemdResolvedIsRunning()
+	return getPreflightChecksForDistro(distro(), networkMode, usingSystemdResolved == nil)
 }
 
-func getNetworkChecksForDistro(distro *linux.OsRelease, networkMode network.Mode) []Check {
+func getNetworkChecks(networkMode network.Mode, systemdResolved bool) []Check {
 	var checks []Check
 
 	if networkMode == network.VSockMode {
@@ -234,28 +236,22 @@ func getNetworkChecksForDistro(distro *linux.OsRelease, networkMode network.Mode
 		return append(checks, daemonRunningChecks)
 	}
 
-	switch {
-	default:
-		logging.Warnf("distribution-specific preflight checks are not implemented for '%s'", distro.ID)
-		fallthrough
-	case distroIsLike(distro, linux.Ubuntu), distroIsLike(distro, linux.Fedora):
-		checks = append(checks, nmPreflightChecks[:]...)
-		if usesSystemdResolved(distro) {
-			checks = append(checks, systemdResolvedPreflightChecks[:]...)
-		} else {
-			checks = append(checks, dnsmasqPreflightChecks[:]...)
-		}
+	checks = append(checks, nmPreflightChecks[:]...)
+	if systemdResolved {
+		checks = append(checks, systemdResolvedPreflightChecks[:]...)
+	} else {
+		checks = append(checks, dnsmasqPreflightChecks[:]...)
 	}
 
 	return checks
 }
 
-func getPreflightChecksForDistro(distro *linux.OsRelease, networkMode network.Mode) []Check {
+func getPreflightChecksForDistro(distro *linux.OsRelease, networkMode network.Mode, systemdResolved bool) []Check {
 	var checks []Check
 	checks = append(checks, nonWinPreflightChecks[:]...)
 	checks = append(checks, genericPreflightChecks[:]...)
 	checks = append(checks, libvirtPreflightChecks(distro)...)
-	networkChecks := getNetworkChecksForDistro(distro, networkMode)
+	networkChecks := getNetworkChecks(networkMode, systemdResolved)
 	checks = append(checks, networkChecks...)
 	if networkMode == network.DefaultMode {
 		checks = append(checks, libvirtNetworkPreflightChecks[:]...)
@@ -264,17 +260,6 @@ func getPreflightChecksForDistro(distro *linux.OsRelease, networkMode network.Mo
 	return checks
 }
 
-func usesSystemdResolved(distro *linux.OsRelease) bool {
-	switch {
-	case distroIsLike(distro, linux.Ubuntu):
-		return true
-	case distro.ID == linux.Fedora:
-		return distro.VersionID >= "33"
-	default:
-		return false
-	}
-}
-
 func distroIsLike(osRelease *linux.OsRelease, osType linux.OsType) bool {
 	if osRelease == nil {
 		return false
diff --git a/pkg/embed/embed.go b/pkg/embed/embed.go
index 65d6a80c1..e9512ef4e 100644
--- a/pkg/embed/embed.go
+++ b/pkg/embed/embed.go
@@ -14,7 +14,7 @@ import (
 
 func openEmbeddedFile(executablePath, embedName string) (io.ReadCloser, error) {
 	if runtime.GOOS == "darwin" && version.IsMacosInstallPathSet() {
-		path := filepath.Join(version.GetMacosInstallPath(), version.GetCRCVersion(), embedName)
+		path := filepath.Join(version.GetMacosInstallPath(), embedName)
 		if _, err := os.Stat(path); err == nil {
 			return os.Open(path)
 		}


Test patch:
diff --git a/pkg/crc/api/api_http_test.go b/pkg/crc/api/api_http_test.go
index 9be5e88f4..8b1025dec 100644
--- a/pkg/crc/api/api_http_test.go
+++ b/pkg/crc/api/api_http_test.go
@@ -6,8 +6,8 @@ import (
 	"testing"
 
 	apiClient "github.com/code-ready/crc/pkg/crc/api/client"
-	"github.com/code-ready/crc/pkg/crc/machine"
 	"github.com/code-ready/crc/pkg/crc/machine/fakemachine"
+	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/version"
 	"github.com/stretchr/testify/assert"
 )
@@ -59,7 +59,7 @@ func TestHTTPApi(t *testing.T) {
 			Status:         "",
 			Error:          "",
 			KubeletStarted: true,
-			ClusterConfig: machine.ClusterConfig{
+			ClusterConfig: types.ClusterConfig{
 				ClusterCACert: "MIIDODCCAiCgAwIBAgIIRVfCKNUa1wIwDQYJ",
 				KubeConfig:    "/tmp/kubeconfig",
 				KubeAdminPass: "foobar",
diff --git a/pkg/crc/machine/sync_test.go b/pkg/crc/machine/sync_test.go
new file mode 100644
index 000000000..7214a78d6
--- /dev/null
+++ b/pkg/crc/machine/sync_test.go
@@ -0,0 +1,167 @@
+package machine
+
+import (
+	"context"
+	"errors"
+	"sync"
+	"testing"
+
+	"github.com/code-ready/crc/pkg/crc/machine/types"
+	"github.com/code-ready/machine/libmachine/state"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestOneStartAtTheSameTime(t *testing.T) {
+	isRunning := make(chan struct{}, 1)
+	startCh := make(chan struct{}, 1)
+	waitingMachine := &waitingMachine{
+		isRunning:       isRunning,
+		startCompleteCh: startCh,
+	}
+	syncMachine := NewSynchronizedMachine(waitingMachine)
+	assert.Equal(t, Idle, syncMachine.CurrentState())
+
+	lock := &sync.WaitGroup{}
+	lock.Add(1)
+	go func() {
+		defer lock.Done()
+		_, err := syncMachine.Start(context.Background(), types.StartConfig{})
+		assert.NoError(t, err)
+	}()
+
+	<-isRunning
+	assert.Equal(t, Starting, syncMachine.CurrentState())
+	assert.Equal(t, waitingMachine.GetName(), syncMachine.GetName())
+	_, err := syncMachine.Start(context.Background(), types.StartConfig{})
+	assert.EqualError(t, err, "cluster is busy")
+
+	startCh <- struct{}{}
+	lock.Wait()
+
+	assert.Equal(t, Idle, syncMachine.CurrentState())
+}
+
+func TestDeleteStop(t *testing.T) {
+	isRunning := make(chan struct{}, 1)
+	deleteCh := make(chan struct{}, 1)
+	waitingMachine := &waitingMachine{
+		isRunning:        isRunning,
+		deleteCompleteCh: deleteCh,
+	}
+	syncMachine := NewSynchronizedMachine(waitingMachine)
+	assert.Equal(t, Idle, syncMachine.CurrentState())
+
+	lock := &sync.WaitGroup{}
+	lock.Add(1)
+	go func() {
+		defer lock.Done()
+		assert.NoError(t, syncMachine.Delete())
+	}()
+
+	<-isRunning
+	assert.Equal(t, Deleting, syncMachine.CurrentState())
+	assert.EqualError(t, syncMachine.Delete(), "cluster is stopping or deleting")
+	_, err := syncMachine.Stop()
+	assert.EqualError(t, err, "cluster is stopping or deleting")
+	_, err = syncMachine.Start(context.Background(), types.StartConfig{})
+	assert.EqualError(t, err, "cluster is busy")
+
+	deleteCh <- struct{}{}
+	lock.Wait()
+
+	assert.Equal(t, Idle, syncMachine.CurrentState())
+}
+
+func TestCancelStart(t *testing.T) {
+	isRunning := make(chan struct{}, 1)
+	deleteCh := make(chan struct{}, 1)
+	waitingMachine := &waitingMachine{
+		isRunning:        isRunning,
+		startCompleteCh:  make(chan struct{}, 1),
+		deleteCompleteCh: deleteCh,
+	}
+	syncMachine := NewSynchronizedMachine(waitingMachine)
+	assert.Equal(t, Idle, syncMachine.CurrentState())
+
+	lock := &sync.WaitGroup{}
+	lock.Add(1)
+	go func() {
+		defer lock.Done()
+		_, err := syncMachine.Start(context.Background(), types.StartConfig{})
+		assert.EqualError(t, err, "context canceled")
+	}()
+
+	<-isRunning
+	assert.Equal(t, Starting, syncMachine.CurrentState())
+
+	lock.Add(1)
+	go func() {
+		defer lock.Done()
+		assert.NoError(t, syncMachine.Delete())
+	}()
+
+	deleteCh <- struct{}{}
+	lock.Wait()
+
+	assert.Equal(t, Idle, syncMachine.CurrentState())
+}
+
+type waitingMachine struct {
+	isRunning        chan struct{}
+	startCompleteCh  chan struct{}
+	stopCompleteCh   chan struct{}
+	deleteCompleteCh chan struct{}
+}
+
+func (m *waitingMachine) IsRunning() (bool, error) {
+	return false, errors.New("not implemented")
+}
+
+func (m *waitingMachine) GetName() string {
+	return "waiting machine"
+}
+
+func (m *waitingMachine) Delete() error {
+	m.isRunning <- struct{}{}
+	<-m.deleteCompleteCh
+	return nil
+}
+
+func (m *waitingMachine) Exists() (bool, error) {
+	return false, errors.New("not implemented")
+}
+
+func (m *waitingMachine) GetConsoleURL() (*types.ConsoleResult, error) {
+	return nil, errors.New("not implemented")
+}
+
+func (m *waitingMachine) IP() (string, error) {
+	return "", errors.New("not implemented")
+}
+
+func (m *waitingMachine) PowerOff() error {
+	return nil
+}
+
+func (m *waitingMachine) Start(context context.Context, _ types.StartConfig) (*types.StartResult, error) {
+	m.isRunning <- struct{}{}
+	select {
+	case <-context.Done():
+		return nil, context.Err()
+	case <-m.startCompleteCh:
+		return &types.StartResult{
+			Status:         state.Running,
+			KubeletStarted: true,
+		}, nil
+	}
+}
+
+func (m *waitingMachine) Status() (*types.ClusterStatusResult, error) {
+	return nil, errors.New("not implemented")
+}
+
+func (m *waitingMachine) Stop() (state.State, error) {
+	m.isRunning <- struct{}{}
+	<-m.stopCompleteCh
+	return state.Stopped, nil
+}
diff --git a/pkg/crc/preflight/preflight_linux_test.go b/pkg/crc/preflight/preflight_linux_test.go
index 1cc33c3b0..f8f7d0dd3 100644
--- a/pkg/crc/preflight/preflight_linux_test.go
+++ b/pkg/crc/preflight/preflight_linux_test.go
@@ -35,7 +35,7 @@ var (
 
 	rhel = crcos.OsRelease{
 		ID:        crcos.RHEL,
-		VersionID: "8.2",
+		VersionID: "8.3",
 		IDLike:    string(crcos.Fedora),
 	}
 
@@ -51,15 +51,17 @@ var (
 )
 
 type checkListForDistro struct {
-	distro      *crcos.OsRelease
-	networkMode network.Mode
-	checks      []Check
+	distro          *crcos.OsRelease
+	networkMode     network.Mode
+	systemdResolved bool
+	checks          []Check
 }
 
 var checkListForDistros = []checkListForDistro{
 	{
-		distro:      &fedora,
-		networkMode: network.DefaultMode,
+		distro:          &fedora,
+		networkMode:     network.DefaultMode,
+		systemdResolved: true,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -90,8 +92,41 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &fedora,
-		networkMode: network.VSockMode,
+		distro:          &fedora,
+		networkMode:     network.DefaultMode,
+		systemdResolved: false,
+		checks: []Check{
+			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
+			{check: checkPodmanExecutableCached},
+			{check: checkAdminHelperExecutableCached},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{configKeySuffix: "check-libvirt-group-active"},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcNetworkManagerConfig},
+			{check: checkCrcDnsmasqConfigFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+			{check: checkBundleExtracted},
+		},
+	},
+	{
+		distro:          &fedora,
+		networkMode:     network.VSockMode,
+		systemdResolved: false,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -116,8 +151,42 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &rhel,
-		networkMode: network.DefaultMode,
+		distro:          &rhel,
+		networkMode:     network.DefaultMode,
+		systemdResolved: true,
+		checks: []Check{
+			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
+			{check: checkPodmanExecutableCached},
+			{check: checkAdminHelperExecutableCached},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{configKeySuffix: "check-libvirt-group-active"},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcDnsmasqAndNetworkManagerConfigFile},
+			{check: checkSystemdResolvedIsRunning},
+			{check: checkCrcNetworkManagerDispatcherFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+			{check: checkBundleExtracted},
+		},
+	},
+	{
+		distro:          &rhel,
+		networkMode:     network.DefaultMode,
+		systemdResolved: false,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -147,8 +216,9 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &rhel,
-		networkMode: network.VSockMode,
+		distro:          &rhel,
+		networkMode:     network.VSockMode,
+		systemdResolved: false,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -173,8 +243,42 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &unexpected,
-		networkMode: network.DefaultMode,
+		distro:          &unexpected,
+		networkMode:     network.DefaultMode,
+		systemdResolved: true,
+		checks: []Check{
+			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
+			{check: checkPodmanExecutableCached},
+			{check: checkAdminHelperExecutableCached},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{configKeySuffix: "check-libvirt-group-active"},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcDnsmasqAndNetworkManagerConfigFile},
+			{check: checkSystemdResolvedIsRunning},
+			{check: checkCrcNetworkManagerDispatcherFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+			{check: checkBundleExtracted},
+		},
+	},
+	{
+		distro:          &unexpected,
+		networkMode:     network.DefaultMode,
+		systemdResolved: false,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -204,8 +308,9 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &unexpected,
-		networkMode: network.VSockMode,
+		distro:          &unexpected,
+		networkMode:     network.VSockMode,
+		systemdResolved: false,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -230,8 +335,9 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &ubuntu,
-		networkMode: network.DefaultMode,
+		distro:          &ubuntu,
+		networkMode:     network.DefaultMode,
+		systemdResolved: true,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -263,8 +369,42 @@ var checkListForDistros = []checkListForDistro{
 		},
 	},
 	{
-		distro:      &ubuntu,
-		networkMode: network.VSockMode,
+		distro:          &ubuntu,
+		networkMode:     network.DefaultMode,
+		systemdResolved: false,
+		checks: []Check{
+			{check: checkIfRunningAsNormalUser},
+			{cleanup: adminhelper.CleanHostsFile},
+			{check: checkPodmanExecutableCached},
+			{check: checkAdminHelperExecutableCached},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{configKeySuffix: "check-libvirt-group-active"},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{configKeySuffix: "check-apparmor-profile-setup"},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcNetworkManagerConfig},
+			{check: checkCrcDnsmasqConfigFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+			{check: checkBundleExtracted},
+		},
+	},
+	{
+		distro:          &ubuntu,
+		networkMode:     network.VSockMode,
+		systemdResolved: false,
 		checks: []Check{
 			{check: checkIfRunningAsNormalUser},
 			{cleanup: adminhelper.CleanHostsFile},
@@ -299,11 +439,11 @@ func assertFuncEqual(t *testing.T, func1 interface{}, func2 interface{}) {
 	assert.Equal(t, reflect.ValueOf(func1).Pointer(), reflect.ValueOf(func2).Pointer(), "%s != %s", funcToString(func1), funcToString(func2))
 }
 
-func assertExpectedPreflights(t *testing.T, distro *crcos.OsRelease, networkMode network.Mode) {
-	preflights := getPreflightChecksForDistro(distro, networkMode)
+func assertExpectedPreflights(t *testing.T, distro *crcos.OsRelease, networkMode network.Mode, systemdResolved bool) {
+	preflights := getPreflightChecksForDistro(distro, networkMode, systemdResolved)
 	var expected checkListForDistro
 	for _, expected = range checkListForDistros {
-		if expected.distro == distro && expected.networkMode == networkMode {
+		if expected.distro == distro && expected.networkMode == networkMode && expected.systemdResolved == systemdResolved {
 			break
 		}
 	}
@@ -328,15 +468,19 @@ func assertExpectedPreflights(t *testing.T, distro *crcos.OsRelease, networkMode
 }
 
 func TestCountPreflights(t *testing.T) {
-	assertExpectedPreflights(t, &fedora, network.DefaultMode)
-	assertExpectedPreflights(t, &fedora, network.VSockMode)
+	assertExpectedPreflights(t, &fedora, network.DefaultMode, true)
+	assertExpectedPreflights(t, &fedora, network.DefaultMode, false)
+	assertExpectedPreflights(t, &fedora, network.VSockMode, false)
 
-	assertExpectedPreflights(t, &rhel, network.DefaultMode)
-	assertExpectedPreflights(t, &rhel, network.VSockMode)
+	assertExpectedPreflights(t, &rhel, network.DefaultMode, true)
+	assertExpectedPreflights(t, &rhel, network.DefaultMode, false)
+	assertExpectedPreflights(t, &rhel, network.VSockMode, false)
 
-	assertExpectedPreflights(t, &unexpected, network.DefaultMode)
-	assertExpectedPreflights(t, &unexpected, network.VSockMode)
+	assertExpectedPreflights(t, &unexpected, network.DefaultMode, true)
+	assertExpectedPreflights(t, &unexpected, network.DefaultMode, false)
+	assertExpectedPreflights(t, &unexpected, network.VSockMode, false)
 
-	assertExpectedPreflights(t, &ubuntu, network.DefaultMode)
-	assertExpectedPreflights(t, &ubuntu, network.VSockMode)
+	assertExpectedPreflights(t, &ubuntu, network.DefaultMode, true)
+	assertExpectedPreflights(t, &ubuntu, network.DefaultMode, false)
+	assertExpectedPreflights(t, &ubuntu, network.VSockMode, false)
 }
diff --git a/test/integration/resize_test.go b/test/integration/resize_test.go
index 9356bc035..4fd8c0f52 100644
--- a/test/integration/resize_test.go
+++ b/test/integration/resize_test.go
@@ -16,7 +16,12 @@ var _ = Describe("vary VM parameters: memory cpus, disk", func() {
 		})
 
 		It("start CRC", func() {
-			Expect(RunCRCExpectSuccess("start", "-b", bundlePath, "-p", pullSecretPath)).To(ContainSubstring("Started the OpenShift cluster")) // default values: "--memory", "9216", "--cpus", "4", "disk-size", "31"
+			// default values: "--memory", "9216", "--cpus", "4", "disk-size", "31"
+			if bundlePath == "embedded" {
+				Expect(RunCRCExpectSuccess("start", "-p", pullSecretPath)).To(ContainSubstring("Started the OpenShift cluster"))
+			} else {
+				Expect(RunCRCExpectSuccess("start", "-b", bundlePath, "-p", pullSecretPath)).To(ContainSubstring("Started the OpenShift cluster"))
+			}
 		})
 
 		It("check VM's memory size", func() {
@@ -48,10 +53,10 @@ var _ = Describe("vary VM parameters: memory cpus, disk", func() {
 
 		It("start CRC", func() {
 			if runtime.GOOS == "darwin" {
-				Expect(RunCRCExpectFail("start", "-b", bundlePath, "--memory", "12000", "--cpus", "6", "--disk-size", "40")).To(ContainSubstring("Disk resizing is not supported on macOS"))
-				Expect(RunCRCExpectSuccess("start", "-b", bundlePath, "--memory", "12000", "--cpus", "6")).To(ContainSubstring("Started the OpenShift cluster"))
+				Expect(RunCRCExpectFail("start", "--memory", "12000", "--cpus", "6", "--disk-size", "40")).To(ContainSubstring("Disk resizing is not supported on macOS"))
+				Expect(RunCRCExpectSuccess("start", "--memory", "12000", "--cpus", "6")).To(ContainSubstring("Started the OpenShift cluster"))
 			} else {
-				Expect(RunCRCExpectSuccess("start", "-b", bundlePath, "--memory", "12000", "--cpus", "6", "--disk-size", "40")).To(ContainSubstring("Started the OpenShift cluster"))
+				Expect(RunCRCExpectSuccess("start", "--memory", "12000", "--cpus", "6", "--disk-size", "40")).To(ContainSubstring("Started the OpenShift cluster"))
 			}
 		})
 
@@ -71,7 +76,11 @@ var _ = Describe("vary VM parameters: memory cpus, disk", func() {
 		It("check VM's disk size", func() {
 			out, err := SendCommandToVM("df -h | grep sysroot")
 			Expect(err).NotTo(HaveOccurred())
-			Expect(out).Should(MatchRegexp(`.*40G[\s].*[\s]/sysroot`))
+			if runtime.GOOS == "darwin" { // darwin does not support resize
+				Expect(out).Should(MatchRegexp(`.*31G[\s].*[\s]/sysroot`))
+			} else {
+				Expect(out).Should(MatchRegexp(`.*40G[\s].*[\s]/sysroot`))
+			}
 		})
 
 		It("stop CRC", func() {
@@ -105,7 +114,7 @@ var _ = Describe("vary VM parameters: memory cpus, disk", func() {
 	Describe("use default values again", func() {
 
 		It("start CRC", func() {
-			Expect(RunCRCExpectSuccess("start", "-b", bundlePath)).To(ContainSubstring("Started the OpenShift cluster")) // default values: "--memory", "9216", "--cpus", "4", "disk-size", "31"
+			Expect(RunCRCExpectSuccess("start")).To(ContainSubstring("Started the OpenShift cluster")) // default values: "--memory", "9216", "--cpus", "4", "disk-size", "31"
 		})
 
 		It("check VM's memory size", func() {
diff --git a/test/integration/testsuite_test.go b/test/integration/testsuite_test.go
index 935f8aaad..a425889d4 100644
--- a/test/integration/testsuite_test.go
+++ b/test/integration/testsuite_test.go
@@ -55,16 +55,12 @@ var _ = BeforeSuite(func() {
 	Expect(err).NotTo(HaveOccurred())
 
 	// bundle location
+	bundlePath = "embedded"
 	if !versionInfo.Embedded {
-		bundlePath = os.Getenv("BUNDLE_PATH") // this env var should contain location of bundle
-		if bundlePath == "" {
-			logrus.Infof("Error: You need to set BUNDLE_PATH because your binary does not contain a bundle.")
-			logrus.Infof("%v", err)
-			Expect(err).NotTo(HaveOccurred())
+		bundlePath = os.Getenv("BUNDLE_PATH") // this env var should contain location of bundle or string "embedded"
+		if bundlePath != "embedded" {         // if real bundle
+			Expect(bundlePath).To(BeAnExistingFile())
 		}
-		Expect(bundlePath).To(BeAnExistingFile()) // not checking if it's an actual bundle
-	} else {
-		bundlePath = "embedded"
 	}
 
 	// pull-secret location