Repository: code-ready/crc
Pull request #1647 (2020-11-06 09:21:38)

Add basic Ubuntu support
Only what is needed if used with vsock

```
guillaumerose@gurose-crc-ubuntu:~$ ./crc setup
INFO Checking if podman remote executable is cached 
INFO Checking if goodhosts executable is cached   
INFO Caching goodhosts executable                 
INFO Will use root access: change ownership of /home/guillaumerose/.crc/bin/goodhosts 
INFO Will use root access: set suid for /home/guillaumerose/.crc/bin/goodhosts 
INFO Checking if CRC bundle is cached in '$HOME/.crc' 
INFO Checking minimum RAM requirements            
INFO Checking if running as non-root              
INFO Checking if Virtualization is enabled        
INFO Checking if KVM is enabled                   
INFO Checking if libvirt is installed             
INFO Installing libvirt service and dependencies  
INFO Will use root access: install virtualization related packages 
INFO Checking if user is part of libvirt group    
INFO Adding user to libvirt group                 
INFO Will use root access: add user to libvirt group 
User guillaumerose added to libvirt group. Please log out and login to get access to libvirt daemon

... logout / login ...

guillaumerose@gurose-crc-ubuntu:~$ ./crc setup
INFO Checking if podman remote executable is cached 
INFO Checking if goodhosts executable is cached   
INFO Checking if CRC bundle is cached in '$HOME/.crc' 
INFO Checking minimum RAM requirements            
INFO Checking if running as non-root              
INFO Checking if Virtualization is enabled        
INFO Checking if KVM is enabled                   
INFO Checking if libvirt is installed             
INFO Checking if user is part of libvirt group    
INFO Checking if libvirt daemon is running        
INFO Checking if a supported libvirt version is installed 
INFO Checking if crc-driver-libvirt is installed  
INFO Installing crc-driver-libvirt                
INFO Checking for obsolete crc-driver-libvirt     
INFO Checking if libvirt 'crc' network is available 
INFO Setting up libvirt 'crc' network             
INFO Checking if libvirt 'crc' network is active  
INFO Starting libvirt 'crc' network               
INFO Checking if AppArmor is configured           
INFO Updating AppArmor configuration              
INFO Will use root access: updating AppArmor configuration 
INFO Will use root access: Changing permission for /etc/apparmor.d/libvirt/TEMPLATE.qemu to 0644  
Setup is complete, you can now run 'crc start -b $bundlename' to start the OpenShift cluster
```

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

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1647#" 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":[]} -->
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 8d98a65bd84a9efb16e7904c9eb32df00ef1da53 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1324650826144157696) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
cfergeau: Just a quick note before I forget, ubuntu still has this issue where one needs to log out/log in again after the user is added to the `libvirt` group. Without that, the `virsh` commands won't be able to access `qemu:///system`.
guillaumerose: Added a line for the group change on Ubuntu !
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 5b07b1a8373b69e40f7ed7d53d6f43c39a320764 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1325728411661897728) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 1e52ebeb0e3ab131eee379506dce680dfc36b624 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1325791374288621568) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
guillaumerose: I tried with a fresh ubuntu install and vsock. Works well.
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | c7c2ad5a6913be56c2922af9d2a547be12a3ca3d | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1326071496958808064) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
guillaumerose: Related to https://github.com/code-ready/crc/issues/97
openshift-ci-robot: @guillaumerose: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 00678c4c825a4af5de5ca003b52c5a1ca6535fcd | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1333360758561443840) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-merge-robot: @guillaumerose: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 00678c4c825a4af5de5ca003b52c5a1ca6535fcd | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1333360758561443840) | `/test e2e-crc`
ci/prow/images | 00678c4c825a4af5de5ca003b52c5a1ca6535fcd | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-images/1333360758611775488) | `/test images`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
cfergeau: I've rebased this (+ added a few more changes) on top of master at https://github.com/cfergeau/crc/tree/ubuntu
Needs testing though, I've relied on the preflight unit tests for sanity checks but did not run it.
openshift-merge-robot: @guillaumerose: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e-crc | 27e4776097e1ac662bec3bcf9c6371f082bb7d14 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/1647/pull-ci-code-ready-crc-master-e2e-crc/1334806989506613248) | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=1647). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:guillaumerose).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/pkg/crc/machine/libvirt/constants.go b/pkg/crc/machine/libvirt/constants.go
index ec1720ada..27d970ea3 100644
--- a/pkg/crc/machine/libvirt/constants.go
+++ b/pkg/crc/machine/libvirt/constants.go
@@ -16,7 +16,7 @@ const (
 
 const (
 	MachineDriverCommand = "crc-driver-libvirt"
-	MachineDriverVersion = "0.12.12"
+	MachineDriverVersion = "0.12.13"
 )
 
 var (
diff --git a/pkg/crc/preflight/preflight_checks_linux.go b/pkg/crc/preflight/preflight_checks_linux.go
index 9f057ec90..eda0353fb 100644
--- a/pkg/crc/preflight/preflight_checks_linux.go
+++ b/pkg/crc/preflight/preflight_checks_linux.go
@@ -20,6 +20,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/systemd"
 	"github.com/code-ready/crc/pkg/crc/systemd/states"
 	crcos "github.com/code-ready/crc/pkg/os"
+	"github.com/code-ready/crc/pkg/os/linux"
 	libvirtxml "github.com/libvirt/libvirt-go-xml"
 )
 
@@ -129,14 +130,29 @@ func checkLibvirtInstalled() error {
 	return nil
 }
 
-func fixLibvirtInstalled() error {
-	logging.Debug("Trying to install libvirt")
-	stdOut, stdErr, err := crcos.RunWithPrivilege("install virtualization related packages", "yum", "install", "-y", "libvirt", "libvirt-daemon-kvm", "qemu-kvm")
-	if err != nil {
-		return fmt.Errorf("Could not install required packages: %s %v: %s", stdOut, err, stdErr)
+func fixLibvirtInstalled(distro *linux.OsRelease) func() error {
+	return func() error {
+		logging.Debug("Trying to install libvirt")
+		stdOut, stdErr, err := crcos.RunWithPrivilege("install virtualization related packages", "/bin/sh", "-c", installLibvirtCommand(distro))
+		if err != nil {
+			return fmt.Errorf("Could not install required packages: %s %v: %s", stdOut, err, stdErr)
+		}
+		logging.Debug("libvirt was successfully installed")
+		return nil
+	}
+}
+
+func installLibvirtCommand(distro *linux.OsRelease) string {
+	yumCommand := "yum install -y libvirt libvirt-daemon-kvm qemu-kvm"
+	switch distroID(distro) {
+	case linux.Ubuntu:
+		return "apt-get update && apt-get install -y libvirt-daemon libvirt-daemon-system libvirt-clients"
+	case linux.RHEL, linux.CentOS, linux.Fedora:
+		return yumCommand
+	default:
+		logging.Warnf("unsupported distribution %s, trying to install libvirt with yum", distro)
+		return yumCommand
 	}
-	logging.Debug("libvirt was successfully installed")
-	return nil
 }
 
 func fixLibvirtVersion() error {
@@ -188,19 +204,25 @@ func checkUserPartOfLibvirtGroup() error {
 	return fmt.Errorf("%s not part of libvirtd group", currentUser.Username)
 }
 
-func fixUserPartOfLibvirtGroup() error {
-	logging.Debug("Adding current user to the libvirt group")
-	currentUser, err := user.Current()
-	if err != nil {
-		logging.Debugf("user.Current() failed: %v", err)
-		return fmt.Errorf("Failed to get current user id")
-	}
-	_, _, err = crcos.RunWithPrivilege("add user to libvirt group", "usermod", "-a", "-G", "libvirt", currentUser.Username)
-	if err != nil {
-		return fmt.Errorf("Failed to add user to libvirt group")
+func fixUserPartOfLibvirtGroup(distro *linux.OsRelease) func() error {
+	return func() error {
+		logging.Debug("Adding current user to the libvirt group")
+		currentUser, err := user.Current()
+		if err != nil {
+			logging.Debugf("user.Current() failed: %v", err)
+			return fmt.Errorf("Failed to get current user id")
+		}
+		_, _, err = crcos.RunWithPrivilege("add user to libvirt group", "usermod", "-a", "-G", "libvirt", currentUser.Username)
+		if err != nil {
+			return fmt.Errorf("Failed to add user to libvirt group")
+		}
+		logging.Debug("Current user is in the libvirt group")
+
+		if distroIsLike(distro, linux.Ubuntu) {
+			return fmt.Errorf("Current user added to libvirt group. Please logout and login again")
+		}
+		return err
 	}
-	logging.Debug("Current user is in the libvirt group")
-	return nil
 }
 
 func checkLibvirtServiceRunning() error {
diff --git a/pkg/crc/preflight/preflight_linux.go b/pkg/crc/preflight/preflight_linux.go
index 246465c9c..c62be4746 100644
--- a/pkg/crc/preflight/preflight_linux.go
+++ b/pkg/crc/preflight/preflight_linux.go
@@ -14,61 +14,67 @@ import (
 	"github.com/code-ready/crc/pkg/os/linux"
 )
 
-var libvirtPreflightChecks = [...]Check{
-	{
-		configKeySuffix:  "check-virt-enabled",
-		checkDescription: "Checking if Virtualization is enabled",
-		check:            checkVirtualizationEnabled,
-		fixDescription:   "Setting up virtualization",
-		fix:              fixVirtualizationEnabled,
-	},
-	{
-		configKeySuffix:  "check-kvm-enabled",
-		checkDescription: "Checking if KVM is enabled",
-		check:            checkKvmEnabled,
-		fixDescription:   "Setting up KVM",
-		fix:              fixKvmEnabled,
-	},
-	{
-		configKeySuffix:  "check-libvirt-installed",
-		checkDescription: "Checking if libvirt is installed",
-		check:            checkLibvirtInstalled,
-		fixDescription:   "Installing libvirt service and dependencies",
-		fix:              fixLibvirtInstalled,
-	},
-	{
-		configKeySuffix:  "check-user-in-libvirt-group",
-		checkDescription: "Checking if user is part of libvirt group",
-		check:            checkUserPartOfLibvirtGroup,
-		fixDescription:   "Adding user to libvirt group",
-		fix:              fixUserPartOfLibvirtGroup,
-	},
-	{
-		configKeySuffix:  "check-libvirt-running",
-		checkDescription: "Checking if libvirt daemon is running",
-		check:            checkLibvirtServiceRunning,
-		fixDescription:   "Starting libvirt service",
-		fix:              fixLibvirtServiceRunning,
-	},
-	{
-		configKeySuffix:  "check-libvirt-version",
-		checkDescription: "Checking if a supported libvirt version is installed",
-		check:            checkLibvirtVersion,
-		fixDescription:   "Installing a supported libvirt version",
-		fix:              fixLibvirtVersion,
-	},
-	{
-		configKeySuffix:  "check-libvirt-driver",
-		checkDescription: "Checking if crc-driver-libvirt is installed",
-		check:            checkMachineDriverLibvirtInstalled,
-		fixDescription:   "Installing crc-driver-libvirt",
-		fix:              fixMachineDriverLibvirtInstalled,
-	},
-	{
-		cleanupDescription: "Removing the crc VM if exists",
-		cleanup:            removeCrcVM,
-		flags:              CleanUpOnly,
-	},
+func libvirtPreflightChecks(distro *linux.OsRelease) []Check {
+	checks := []Check{
+		{
+			configKeySuffix:  "check-virt-enabled",
+			checkDescription: "Checking if Virtualization is enabled",
+			check:            checkVirtualizationEnabled,
+			fixDescription:   "Setting up virtualization",
+			fix:              fixVirtualizationEnabled,
+		},
+		{
+			configKeySuffix:  "check-kvm-enabled",
+			checkDescription: "Checking if KVM is enabled",
+			check:            checkKvmEnabled,
+			fixDescription:   "Setting up KVM",
+			fix:              fixKvmEnabled,
+		},
+		{
+			configKeySuffix:  "check-libvirt-installed",
+			checkDescription: "Checking if libvirt is installed",
+			check:            checkLibvirtInstalled,
+			fixDescription:   "Installing libvirt service and dependencies",
+			fix:              fixLibvirtInstalled(distro),
+		},
+		{
+			configKeySuffix:  "check-user-in-libvirt-group",
+			checkDescription: "Checking if user is part of libvirt group",
+			check:            checkUserPartOfLibvirtGroup,
+			fixDescription:   "Adding user to libvirt group",
+			fix:              fixUserPartOfLibvirtGroup(distro),
+		},
+		{
+			configKeySuffix:  "check-libvirt-running",
+			checkDescription: "Checking if libvirt daemon is running",
+			check:            checkLibvirtServiceRunning,
+			fixDescription:   "Starting libvirt service",
+			fix:              fixLibvirtServiceRunning,
+		},
+		{
+			configKeySuffix:  "check-libvirt-version",
+			checkDescription: "Checking if a supported libvirt version is installed",
+			check:            checkLibvirtVersion,
+			fixDescription:   "Installing a supported libvirt version",
+			fix:              fixLibvirtVersion,
+		},
+		{
+			configKeySuffix:  "check-libvirt-driver",
+			checkDescription: "Checking if crc-driver-libvirt is installed",
+			check:            checkMachineDriverLibvirtInstalled,
+			fixDescription:   "Installing crc-driver-libvirt",
+			fix:              fixMachineDriverLibvirtInstalled,
+		},
+		{
+			cleanupDescription: "Removing the crc VM if exists",
+			cleanup:            removeCrcVM,
+			flags:              CleanUpOnly,
+		},
+	}
+	if distroID(distro) == linux.Ubuntu {
+		checks = append(checks, ubuntuPreflightChecks...)
+	}
+	return checks
 }
 
 var libvirtNetworkPreflightChecks = [...]Check{
@@ -172,19 +178,17 @@ func getNetworkChecksForDistro(distro *linux.OsRelease, networkMode network.Mode
 		return append(checks, vsockPreflightChecks)
 	}
 
-	switch distroID(distro) {
+	switch {
 	default:
 		logging.Warnf("distribution-specific preflight checks are not implemented for '%s'", distroID(distro))
 		fallthrough
-	case linux.RHEL, linux.CentOS, linux.Fedora:
+	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[:]...)
 		}
-	case linux.Ubuntu:
-		break
 	}
 
 	return checks
@@ -194,15 +198,19 @@ func getPreflightChecksForDistro(distro *linux.OsRelease, networkMode network.Mo
 	var checks []Check
 	checks = append(checks, genericPreflightChecks[:]...)
 	checks = append(checks, nonWinPreflightChecks[:]...)
-	checks = append(checks, libvirtPreflightChecks[:]...)
+	checks = append(checks, libvirtPreflightChecks(distro)...)
 	networkChecks := getNetworkChecksForDistro(distro, networkMode)
 	checks = append(checks, networkChecks...)
-	checks = append(checks, libvirtNetworkPreflightChecks[:]...)
+	if networkMode == network.DefaultMode {
+		checks = append(checks, libvirtNetworkPreflightChecks[:]...)
+	}
 	return checks
 }
 
 func usesSystemdResolved(osRelease *linux.OsRelease) bool {
 	switch distroID(osRelease) {
+	case linux.Ubuntu:
+		return true
 	case linux.Fedora:
 		return osRelease.VersionID >= "33"
 	default:
@@ -218,6 +226,23 @@ func distroID(osRelease *linux.OsRelease) linux.OsType {
 	return osRelease.ID
 }
 
+func distroIsLike(osRelease *linux.OsRelease, osType linux.OsType) bool {
+	if osRelease == nil {
+		return false
+	}
+	if osRelease.ID == osType {
+		return true
+	}
+
+	for _, id := range osRelease.GetIDLike() {
+		if id == osType {
+			return true
+		}
+	}
+
+	return false
+}
+
 func distro() *linux.OsRelease {
 	distro, err := linux.GetOsRelease()
 	if err != nil {
diff --git a/pkg/crc/preflight/preflight_ubuntu_linux.go b/pkg/crc/preflight/preflight_ubuntu_linux.go
new file mode 100644
index 000000000..6f38f8c4c
--- /dev/null
+++ b/pkg/crc/preflight/preflight_ubuntu_linux.go
@@ -0,0 +1,79 @@
+// +build linux
+
+package preflight
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/code-ready/crc/pkg/crc/constants"
+	crcos "github.com/code-ready/crc/pkg/os"
+)
+
+var ubuntuPreflightChecks = []Check{
+	{
+		configKeySuffix:    "check-apparmor-profile-setup",
+		checkDescription:   "Checking if AppArmor is configured",
+		check:              checkAppArmorExceptionIsPresent(ioutil.ReadFile),
+		fixDescription:     "Updating AppArmor configuration",
+		fix:                addAppArmorExceptionForQcowDisks(ioutil.ReadFile, crcos.WriteToFileAsRoot),
+		cleanupDescription: "Cleaning up AppArmor configuration",
+		cleanup:            removeAppArmorExceptionForQcowDisks(ioutil.ReadFile, crcos.WriteToFileAsRoot),
+	},
+}
+
+const (
+	appArmorTemplate = "/etc/apparmor.d/libvirt/TEMPLATE.qemu"
+	appArmorHeader   = "profile LIBVIRT_TEMPLATE flags=(attach_disconnected) {"
+)
+
+type reader func(filename string) ([]byte, error)
+type writer func(reason, content, filepath string, mode os.FileMode) error
+
+// Verify the line is present in AppArmor template
+func checkAppArmorExceptionIsPresent(reader reader) func() error {
+	return func() error {
+		template, err := reader(appArmorTemplate)
+		if err != nil {
+			return err
+		}
+		if !strings.Contains(string(template), expectedLines()) {
+			return errors.New("AppArmor profile not configured")
+		}
+		return nil
+	}
+}
+
+// Add the exception `cacheDir/*/crc.qcow2 rk` in AppArmor template
+func addAppArmorExceptionForQcowDisks(reader reader, writer writer) func() error {
+	return replaceInAppArmorTemplate(reader, writer, appArmorHeader, expectedLines())
+}
+
+// Eventually remove the exception in AppArmor template
+func removeAppArmorExceptionForQcowDisks(reader reader, writer writer) func() error {
+	return replaceInAppArmorTemplate(reader, writer, expectedLines(), appArmorHeader)
+}
+
+// Search for the string `before` in the AppArmor template and replace it with `after` string.
+func replaceInAppArmorTemplate(reader reader, writer writer, before string, after string) func() error {
+	return func() error {
+		template, err := reader(appArmorTemplate)
+		if err != nil {
+			return err
+		}
+		if !strings.Contains(string(template), before) {
+			return fmt.Errorf("unexpected AppArmor template file %s, cannot configure it automatically", appArmorTemplate)
+		}
+		content := strings.Replace(string(template), before, after, 1)
+		return writer("updating AppArmor configuration", content, appArmorTemplate, 0644)
+	}
+}
+
+func expectedLines() string {
+	line := fmt.Sprintf("  %s rk,", filepath.Join(constants.MachineCacheDir, "*", "crc.qcow2"))
+	return fmt.Sprintf("%s\n%s\n", appArmorHeader, line)
+}
diff --git a/pkg/os/linux/release_info.go b/pkg/os/linux/release_info.go
index 614e6f21d..bd951be21 100644
--- a/pkg/os/linux/release_info.go
+++ b/pkg/os/linux/release_info.go
@@ -114,3 +114,17 @@ func GetOsRelease() (*OsRelease, error) {
 	}
 	return &osr, nil
 }
+
+func (osr *OsRelease) GetIDLike() []OsType {
+	var idLike []OsType
+
+	if osr == nil {
+		return idLike
+	}
+
+	for _, id := range strings.Split(osr.IDLike, " ") {
+		idLike = append(idLike, OsType(id))
+	}
+
+	return idLike
+}


Test patch:
diff --git a/pkg/crc/preflight/preflight_linux_test.go b/pkg/crc/preflight/preflight_linux_test.go
index f7171b440..ad6571aae 100644
--- a/pkg/crc/preflight/preflight_linux_test.go
+++ b/pkg/crc/preflight/preflight_linux_test.go
@@ -1,6 +1,8 @@
 package preflight
 
 import (
+	"reflect"
+	"runtime"
 	"testing"
 
 	"github.com/code-ready/crc/pkg/crc/config"
@@ -24,29 +26,279 @@ func TestCountConfigurationOptions(t *testing.T) {
 }
 
 var (
-	rhel crcos.OsRelease = crcos.OsRelease{
+	fedora = crcos.OsRelease{
+		ID:        crcos.Fedora,
+		VersionID: "35",
+	}
+
+	rhel = crcos.OsRelease{
 		ID:        crcos.RHEL,
 		VersionID: "8.2",
+		IDLike:    string(crcos.Fedora),
 	}
 
-	ubuntu crcos.OsRelease = crcos.OsRelease{
+	ubuntu = crcos.OsRelease{
 		ID:        crcos.Ubuntu,
 		VersionID: "20.04",
 	}
 
-	unexpected crcos.OsRelease = crcos.OsRelease{
+	unexpected = crcos.OsRelease{
 		ID:        "unexpected",
 		VersionID: "1234",
 	}
 )
 
+type checkListForDistro struct {
+	distro      *crcos.OsRelease
+	networkMode network.Mode
+	checks      []Check
+}
+
+var checkListForDistros = []checkListForDistro{
+	{
+		distro:      &fedora,
+		networkMode: network.DefaultMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcDnsmasqAndNetworkManagerConfigFile},
+			{check: checkSystemdResolvedIsRunning},
+			{check: checkCrcNetworkManagerDispatcherFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+		},
+	},
+	{
+		distro:      &fedora,
+		networkMode: network.VSockMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkVsock},
+		},
+	},
+	{
+		distro:      &rhel,
+		networkMode: network.DefaultMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcNetworkManagerConfig},
+			{check: checkCrcDnsmasqConfigFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+		},
+	},
+	{
+		distro:      &rhel,
+		networkMode: network.VSockMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkVsock},
+		},
+	},
+	{
+		distro:      &unexpected,
+		networkMode: network.DefaultMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcNetworkManagerConfig},
+			{check: checkCrcDnsmasqConfigFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+		},
+	},
+	{
+		distro:      &unexpected,
+		networkMode: network.VSockMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{check: checkVsock},
+		},
+	},
+	{
+		distro:      &ubuntu,
+		networkMode: network.DefaultMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{configKeySuffix: "check-apparmor-profile-setup"},
+			{check: checkSystemdNetworkdIsNotRunning},
+			{check: checkNetworkManagerInstalled},
+			{check: checkNetworkManagerIsRunning},
+			{check: checkCrcDnsmasqAndNetworkManagerConfigFile},
+			{check: checkSystemdResolvedIsRunning},
+			{check: checkCrcNetworkManagerDispatcherFile},
+			{check: checkLibvirtCrcNetworkAvailable},
+			{check: checkLibvirtCrcNetworkActive},
+		},
+	},
+	{
+		distro:      &ubuntu,
+		networkMode: network.VSockMode,
+		checks: []Check{
+			{check: checkPodmanExecutableCached},
+			{check: checkGoodhostsExecutableCached},
+			{check: checkBundleExtracted},
+			{configKeySuffix: "check-ram"},
+			{cleanup: removeCRCMachinesDir},
+			{check: checkIfRunningAsNormalUser},
+			{check: checkVirtualizationEnabled},
+			{check: checkKvmEnabled},
+			{check: checkLibvirtInstalled},
+			{check: checkUserPartOfLibvirtGroup},
+			{check: checkLibvirtServiceRunning},
+			{check: checkLibvirtVersion},
+			{check: checkMachineDriverLibvirtInstalled},
+			{cleanup: removeCrcVM},
+			{configKeySuffix: "check-apparmor-profile-setup"},
+			{check: checkVsock},
+		},
+	},
+}
+
+func funcToString(f interface{}) string {
+	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
+}
+
+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)
+	var expected checkListForDistro
+	for _, expected = range checkListForDistros {
+		if expected.distro == distro && expected.networkMode == networkMode {
+			break
+		}
+	}
+
+	assert.Equal(t, len(preflights), len(expected.checks), "%s %s - %s - expected: %d - got: %d", distro.ID, distro.VersionID, networkMode, len(expected.checks), len(preflights))
+
+	for i := range preflights {
+		expectedCheck := expected.checks[i]
+		if expectedCheck.configKeySuffix != "" {
+			assert.Equal(t, preflights[i].configKeySuffix, expectedCheck.configKeySuffix)
+		}
+		if expectedCheck.check != nil {
+			assertFuncEqual(t, preflights[i].check, expectedCheck.check)
+		}
+		if expectedCheck.fix != nil {
+			assertFuncEqual(t, preflights[i].fix, expectedCheck.fix)
+		}
+		if expectedCheck.cleanup != nil {
+			assertFuncEqual(t, preflights[i].cleanup, expectedCheck.cleanup)
+		}
+	}
+}
+
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecksForDistro(&rhel, network.DefaultMode), 21)
-	assert.Len(t, getPreflightChecksForDistro(&rhel, network.VSockMode), 17)
+	assertExpectedPreflights(t, &fedora, network.DefaultMode)
+	assertExpectedPreflights(t, &fedora, network.VSockMode)
+
+	assertExpectedPreflights(t, &rhel, network.DefaultMode)
+	assertExpectedPreflights(t, &rhel, network.VSockMode)
 
-	assert.Len(t, getPreflightChecksForDistro(&unexpected, network.DefaultMode), 21)
-	assert.Len(t, getPreflightChecksForDistro(&unexpected, network.VSockMode), 17)
+	assertExpectedPreflights(t, &unexpected, network.DefaultMode)
+	assertExpectedPreflights(t, &unexpected, network.VSockMode)
 
-	assert.Len(t, getPreflightChecksForDistro(&ubuntu, network.DefaultMode), 16)
-	assert.Len(t, getPreflightChecksForDistro(&ubuntu, network.VSockMode), 17)
+	assertExpectedPreflights(t, &ubuntu, network.DefaultMode)
+	assertExpectedPreflights(t, &ubuntu, network.VSockMode)
 }
diff --git a/pkg/crc/preflight/preflight_ubuntu_linux_test.go b/pkg/crc/preflight/preflight_ubuntu_linux_test.go
new file mode 100644
index 000000000..b268443ab
--- /dev/null
+++ b/pkg/crc/preflight/preflight_ubuntu_linux_test.go
@@ -0,0 +1,56 @@
+// +build linux
+
+package preflight
+
+import (
+	"errors"
+	"os"
+	"testing"
+
+	"github.com/code-ready/crc/pkg/crc/constants"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	source = `#include <tunables/global>
+profile LIBVIRT_TEMPLATE flags=(attach_disconnected) {
+  #include <abstractions/libvirt-qemu>
+}`
+	expected = `#include <tunables/global>
+profile LIBVIRT_TEMPLATE flags=(attach_disconnected) {
+  ` + constants.MachineCacheDir + `/*/crc.qcow2 rk,
+
+  #include <abstractions/libvirt-qemu>
+}`
+)
+
+func TestCheckAppArmor(t *testing.T) {
+	assert.EqualError(t, checkAppArmorExceptionIsPresent(mockReader(source))(), "AppArmor profile not configured")
+	assert.NoError(t, checkAppArmorExceptionIsPresent(mockReader(expected))())
+}
+
+func TestFixAppArmor(t *testing.T) {
+	assert.NoError(t, addAppArmorExceptionForQcowDisks(mockReader(source), writerVerifier(expected))())
+	assert.EqualError(t, addAppArmorExceptionForQcowDisks(mockReader("invalid"), writerVerifier(expected))(),
+		"unexpected AppArmor template file /etc/apparmor.d/libvirt/TEMPLATE.qemu, cannot configure it automatically")
+}
+
+func TestCleanupAppArmor(t *testing.T) {
+	assert.NoError(t, removeAppArmorExceptionForQcowDisks(mockReader(expected), writerVerifier(source))())
+}
+
+func writerVerifier(expected string) func(reason string, content string, filepath string, mode os.FileMode) error {
+	return func(reason, content, filepath string, mode os.FileMode) error {
+		if content == expected {
+			return nil
+		}
+		return errors.New("unexpected content")
+	}
+}
+
+func mockReader(input string) func(string) ([]byte, error) {
+	return func(string) ([]byte, error) {
+		return []byte(input), nil
+	}
+}