Skip to content

Commit

Permalink
Add cleanup feature as part of command set
Browse files Browse the repository at this point in the history
The 'cleanup' methods should ignore as many errors as possible when
doing their job, to cope with both the nominal case, and cases where
some of the checks are disabled/skipped. Which would mean we don't need to
check the config options, as the 'cleanup' method will be appropriate
in both cases.

As of now, cleanup command for linux is partial since we are not sure
if virt installed packages as part of setup command or by the user.
Also user added himself as part of libvirt group or it happened by
setup command.
  • Loading branch information
praveenkumar committed Apr 9, 2020
1 parent 27c3e22 commit 51fe265
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 23 deletions.
27 changes: 27 additions & 0 deletions cmd/crc/cmd/cleanup.go
@@ -0,0 +1,27 @@
package cmd

import (
"fmt"

"github.com/code-ready/crc/pkg/crc/output"
"github.com/code-ready/crc/pkg/crc/preflight"
"github.com/spf13/cobra"
)

func init() {
rootCmd.AddCommand(cleanupCmd)
}

var cleanupCmd = &cobra.Command{
Use: "cleanup",
Short: fmt.Sprintf("Undo config changes"),
Long: "Undo all the configuration changes done by 'crc setup' command",
Run: func(cmd *cobra.Command, args []string) {
runCleanup(args)
},
}

func runCleanup(arguments []string) {
preflight.CleanUpHost()
output.Outln("Cleanup finished")
}
44 changes: 36 additions & 8 deletions pkg/crc/preflight/preflight.go
Expand Up @@ -19,18 +19,22 @@ const (
// Indicates a PreflightCheck should only be run as part of "crc start"
StartOnly
NoFix
CleanUpOnly
)

type PreflightCheckFunc func() error
type PreflightFixFunc func() error
type PreflightCleanUpFunc func() error

type PreflightCheck struct {
configKeySuffix string
checkDescription string
check PreflightCheckFunc
fixDescription string
fix PreflightFixFunc
flags PreflightCheckFlags
configKeySuffix string
checkDescription string
check PreflightCheckFunc
fixDescription string
fix PreflightFixFunc
flags PreflightCheckFlags
cleanupDescription string
cleanup PreflightCleanUpFunc
}

func (check *PreflightCheck) getSkipConfigName() string {
Expand Down Expand Up @@ -92,9 +96,19 @@ func (check *PreflightCheck) doFix() error {
return check.fix()
}

func (check *PreflightCheck) doCleanUp() error {
if check.cleanupDescription == "" {
panic(fmt.Sprintf("Should not happen, empty description for cleanup '%s'", check.configKeySuffix))
}

logging.Infof("%s", check.cleanupDescription)

return check.cleanup()
}

func doPreflightChecks(checks []PreflightCheck) {
for _, check := range checks {
if check.flags&SetupOnly == SetupOnly {
if check.flags&SetupOnly == SetupOnly || check.flags&CleanUpOnly == CleanUpOnly {
continue
}
err := check.doCheck()
Expand All @@ -110,7 +124,7 @@ func doPreflightChecks(checks []PreflightCheck) {

func doFixPreflightChecks(checks []PreflightCheck) {
for _, check := range checks {
if check.flags&StartOnly == StartOnly {
if check.flags&StartOnly == StartOnly || check.flags&CleanUpOnly == CleanUpOnly {
continue
}
err := check.doCheck()
Expand All @@ -128,6 +142,20 @@ func doFixPreflightChecks(checks []PreflightCheck) {
}
}

func doCleanUpPreflightChecks(checks []PreflightCheck) {
// Do the cleanup in reverse order to avoid any dependency during cleanup
for i := len(checks) - 1; i >= 0; i-- {
check := checks[i]
if check.cleanup == nil {
continue
}
err := check.doCleanUp()
if err != nil {
logging.Fatal(err.Error())
}
}
}

func doRegisterSettings(checks []PreflightCheck) {
for _, check := range checks {
if check.configKeySuffix != "" {
Expand Down
100 changes: 100 additions & 0 deletions pkg/crc/preflight/preflight_checks_linux.go
Expand Up @@ -362,6 +362,56 @@ func fixLibvirtCrcNetworkAvailable() error {
return nil
}

func removeLibvirtCrcNetwork() error {
logging.Debug("Removing libvirt 'crc' network")
_, _, err := crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "net-info", libvirt.DefaultNetwork)
if err != nil {
// Ignore if no crc network exists for libvirt
// User may have manually deleted the `crc` network from libvirt
return nil
}
_, stderr, err := crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "net-destroy", libvirt.DefaultNetwork)
if err != nil {
logging.Debugf("%v : %s", err, stderr)
return fmt.Errorf("Failed to destroy libvirt 'crc' network")
}

_, stderr, err = crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "net-undefine", libvirt.DefaultNetwork)
if err != nil {
logging.Debugf("%v : %s", err, stderr)
return fmt.Errorf("Failed to undefine libvirt 'crc' network")
}
logging.Debug("libvirt 'crc' network removed")
return nil
}

func removeCrcVM() error {
logging.Debug("Removing 'crc' VM")
_, _, err := crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "dominfo", constants.DefaultName)
if err != nil {
// User may have run `crc delete` before `crc cleanup`
// in that case there is no crc vm so return early.
return nil
}
_, stderr, err := crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "destroy", constants.DefaultName)
if err != nil {
logging.Debugf("%v : %s", err, stderr)
return fmt.Errorf("Failed to destroy 'crc' VM")
}
_, stderr, err = crcos.RunWithDefaultLocale("virsh", "--connect", "qemu:///system", "undefine", constants.DefaultName)
if err != nil {
logging.Debugf("%v : %s", err, stderr)
return fmt.Errorf("Failed to undefine 'crc' VM")
}
if err := os.RemoveAll(constants.MachineInstanceDir); err != nil {
logging.Debugf("Error removing %s dir: %v", constants.MachineInstanceDir, err)
return fmt.Errorf("Error removing %s dir", constants.MachineInstanceDir)
}
logging.Debug("'crc' VM is removed")

return nil
}

func trimSpacesFromXML(str string) string {
strs := strings.Split(str, "\n")
var builder strings.Builder
Expand Down Expand Up @@ -472,6 +522,32 @@ func fixCrcDnsmasqConfigFile() error {
return nil
}

func removeCrcDnsmasqConfigFile() error {
if err := checkNetworkManagerInstalled(); err != nil {
// When NetworkManager is not installed, this file won't exist
return nil
}
// Delete the `crcDnsmasqConfigPath` file if exists,
// ignore all the os PathError except `IsNotExist` one.
if _, err := os.Stat(crcDnsmasqConfigPath); !os.IsNotExist(err) {
logging.Debug("Removing dnsmasq configuration")
err := crcos.RemoveFileAsRoot(
fmt.Sprintf("removing dnsmasq configuration in %s", crcDnsmasqConfigPath),
crcDnsmasqConfigPath,
)
if err != nil {
return fmt.Errorf("Failed to remove dnsmasq config file: %s: %v", crcDnsmasqConfigPath, err)
}

logging.Debug("Reloading NetworkManager")
sd := systemd.NewHostSystemdCommander()
if _, err := sd.Reload("NetworkManager"); err != nil {
return fmt.Errorf("Failed to restart NetworkManager: %v", err)
}
}
return nil
}

func checkCrcNetworkManagerConfig() error {
logging.Debug("Checking NetworkManager configuration")
c := []byte(crcNetworkManagerConfig)
Expand Down Expand Up @@ -511,6 +587,30 @@ func fixCrcNetworkManagerConfig() error {
return nil
}

func removeCrcNetworkManagerConfig() error {
if err := checkNetworkManagerInstalled(); err != nil {
// When NetworkManager is not installed, this file won't exist
return nil
}
if _, err := os.Stat(crcNetworkManagerConfigPath); !os.IsNotExist(err) {
logging.Debug("Removing NetworkManager configuration")
err := crcos.RemoveFileAsRoot(
fmt.Sprintf("Removing NetworkManager config in %s", crcNetworkManagerConfigPath),
crcNetworkManagerConfigPath,
)
if err != nil {
return fmt.Errorf("Failed to remove NetworkManager config file: %s: %v", crcNetworkManagerConfigPath, err)
}

logging.Debug("Reloading NetworkManager")
sd := systemd.NewHostSystemdCommander()
if _, err := sd.Reload("NetworkManager"); err != nil {
return fmt.Errorf("Failed to restart NetworkManager: %v", err)
}
}
return nil
}

func checkNetworkManagerInstalled() error {
logging.Debug("Checking if 'nmcli' is available")
path, err := exec.LookPath("nmcli")
Expand Down
7 changes: 7 additions & 0 deletions pkg/crc/preflight/preflight_darwin.go
Expand Up @@ -2,6 +2,8 @@ package preflight

import (
"fmt"

"github.com/code-ready/crc/pkg/crc/logging"
)

// SetupHost performs the prerequisite checks and setups the host to run the cluster
Expand Down Expand Up @@ -113,3 +115,8 @@ func SetupHost() {
func RegisterSettings() {
doRegisterSettings(getPreflightChecks())
}

func CleanUpHost() {
logging.Warn("Cleanup is not supported for MacOS")
doCleanUpPreflightChecks(getPreflightChecks())
}
45 changes: 30 additions & 15 deletions pkg/crc/preflight/preflight_linux.go
Expand Up @@ -65,11 +65,13 @@ var libvirtPreflightChecks = [...]PreflightCheck{
flags: SetupOnly,
},
{
configKeySuffix: "check-crc-network",
checkDescription: "Checking if libvirt 'crc' network is available",
check: checkLibvirtCrcNetworkAvailable,
fixDescription: "Setting up libvirt 'crc' network",
fix: fixLibvirtCrcNetworkAvailable,
configKeySuffix: "check-crc-network",
checkDescription: "Checking if libvirt 'crc' network is available",
check: checkLibvirtCrcNetworkAvailable,
fixDescription: "Setting up libvirt 'crc' network",
fix: fixLibvirtCrcNetworkAvailable,
cleanupDescription: "Removing 'crc' network from libvirt",
cleanup: removeLibvirtCrcNetwork,
},
{
configKeySuffix: "check-crc-network-active",
Expand All @@ -93,18 +95,27 @@ var libvirtPreflightChecks = [...]PreflightCheck{
fix: fixNetworkManagerIsRunning,
},
{
configKeySuffix: "check-network-manager-config",
checkDescription: "Checking if /etc/NetworkManager/conf.d/crc-nm-dnsmasq.conf exists",
check: checkCrcNetworkManagerConfig,
fixDescription: "Writing Network Manager config for crc",
fix: fixCrcNetworkManagerConfig,
configKeySuffix: "check-network-manager-config",
checkDescription: "Checking if /etc/NetworkManager/conf.d/crc-nm-dnsmasq.conf exists",
check: checkCrcNetworkManagerConfig,
fixDescription: "Writing Network Manager config for crc",
fix: fixCrcNetworkManagerConfig,
cleanupDescription: "Removing /etc/NetworkManager/conf.d/crc-nm-dnsmasq.conf file",
cleanup: removeCrcNetworkManagerConfig,
},
{
configKeySuffix: "check-crc-dnsmasq-file",
checkDescription: "Checking if /etc/NetworkManager/dnsmasq.d/crc.conf exists",
check: checkCrcDnsmasqConfigFile,
fixDescription: "Writing dnsmasq config for crc",
fix: fixCrcDnsmasqConfigFile,
configKeySuffix: "check-crc-dnsmasq-file",
checkDescription: "Checking if /etc/NetworkManager/dnsmasq.d/crc.conf exists",
check: checkCrcDnsmasqConfigFile,
fixDescription: "Writing dnsmasq config for crc",
fix: fixCrcDnsmasqConfigFile,
cleanupDescription: "Removing /etc/NetworkManager/dnsmasq.d/crc.conf file",
cleanup: removeCrcDnsmasqConfigFile,
},
{
cleanupDescription: "Removing the crc VM if exists",
cleanup: removeCrcVM,
flags: CleanUpOnly,
},
}

Expand All @@ -130,3 +141,7 @@ func SetupHost() {
func RegisterSettings() {
doRegisterSettings(getPreflightChecks())
}

func CleanUpHost() {
doCleanUpPreflightChecks(getPreflightChecks())
}
7 changes: 7 additions & 0 deletions pkg/crc/preflight/preflight_windows.go
@@ -1,5 +1,7 @@
package preflight

import "github.com/code-ready/crc/pkg/crc/logging"

var hypervPreflightChecks = [...]PreflightCheck{
{
configKeySuffix: "check-administrator-user",
Expand Down Expand Up @@ -66,3 +68,8 @@ func SetupHost() {
func RegisterSettings() {
doRegisterSettings(getPreflightChecks())
}

func CleanUpHost() {
logging.Warn("Cleanup is not supported for Windows")
doCleanUpPreflightChecks(getPreflightChecks())
}
5 changes: 5 additions & 0 deletions pkg/os/util_linux.go
Expand Up @@ -20,3 +20,8 @@ func WriteToFileAsRoot(reason, content, filepath string) error {
}
return nil
}

func RemoveFileAsRoot(reason, filepath string) error {
_, _, err := RunWithPrivilege(reason, "rm", "-fr", filepath)
return err
}

0 comments on commit 51fe265

Please sign in to comment.