diff --git a/pkg/crc/constants/constants_windows.go b/pkg/crc/constants/constants_windows.go index b213c2eb36..b321107401 100644 --- a/pkg/crc/constants/constants_windows.go +++ b/pkg/crc/constants/constants_windows.go @@ -5,4 +5,5 @@ const ( PodmanRemoteExecutableName = "podman.exe" TapSocketPath = "" DaemonHTTPNamedPipe = `\\.\pipe\crc-http` + DaemonTaskName = "crcDaemon" ) diff --git a/pkg/crc/preflight/preflight_daemon_task_check_windows.go b/pkg/crc/preflight/preflight_daemon_task_check_windows.go new file mode 100644 index 0000000000..00a048bf64 --- /dev/null +++ b/pkg/crc/preflight/preflight_daemon_task_check_windows.go @@ -0,0 +1,138 @@ +package preflight + +import ( + "bytes" + "encoding/xml" + "fmt" + "os" + "strings" + + "github.com/code-ready/crc/pkg/crc/constants" + "github.com/code-ready/crc/pkg/crc/logging" + "github.com/code-ready/crc/pkg/crc/version" + "github.com/code-ready/crc/pkg/os/windows/powershell" +) + +var ( + // https://docs.microsoft.com/en-us/windows/win32/taskschd/daily-trigger-example--xml- + daemonTaskTemplate = ` + + + Run crc daemon as a task + %s + + + false + false + true + IgnoreNew + + PT10M + PT1H + true + false + + true + + + + + powershell.exe + -WindowStyle Hidden -Command %s + + + +` +) + +func genDaemonTaskInstallTemplate(crcVersion, daemonServiceName string) string { + return fmt.Sprintf(daemonTaskTemplate, + crcVersion, + daemonServiceName, + ) +} + +func checkIfDaemonTaskInstalled() error { + _, stderr, err := powershell.Execute("Get-ScheduledTask", "-TaskName", constants.DaemonTaskName) + if err != nil { + logging.Debugf("%s task is not installed: %v : %s", constants.DaemonTaskName, err, stderr) + return err + } + if err := checkIfOlderTask(); err != nil { + return err + } + return nil +} + +func fixDaemonTaskInstalled() error { + // prepare the task script + binPath, err := os.Executable() + if err != nil { + return fmt.Errorf("unable to find the current executable location: %v", err) + } + binPathWithArgs := fmt.Sprintf("%s daemon", binPath) + var buf bytes.Buffer + if err := xml.EscapeText(&buf, []byte(binPathWithArgs)); err != nil { + return err + } + taskContent := genDaemonTaskInstallTemplate( + version.GetCRCVersion(), + buf.String(), + ) + + if _, stderr, err := powershell.Execute("Register-ScheduledTask", "-Xml", fmt.Sprintf(`'%s'`, taskContent), "-TaskName", constants.DaemonTaskName); err != nil { + return fmt.Errorf("failed to register %s task, %v: %s", constants.DaemonTaskName, err, stderr) + } + + return nil +} + +func removeDaemonTask() error { + if err := checkIfDaemonTaskRunning(); err == nil { + _, stderr, err := powershell.Execute("Stop-ScheduledTask", "-TaskName", constants.DaemonTaskName) + if err != nil { + logging.Debugf("unable to stop the %s task: %v : %s", constants.DaemonTaskName, err, stderr) + return err + } + } + if err := checkIfDaemonTaskInstalled(); err == nil { + _, stderr, err := powershell.Execute("Unregister-ScheduledTask", "-TaskName", constants.DaemonTaskName, "-Confirm:$false") + if err != nil { + logging.Debugf("unable to unregister the %s task: %v : %s", constants.DaemonTaskName, err, stderr) + return err + } + } + return nil +} + +func checkIfDaemonTaskRunning() error { + stdout, stderr, err := powershell.Execute(fmt.Sprintf(`(Get-ScheduledTask -TaskName "%s").State`, constants.DaemonTaskName)) + if err != nil { + logging.Debugf("%s task is not running: %v : %s", constants.DaemonTaskName, err, stderr) + return err + } + if strings.TrimSpace(stdout) != "Running" { + return fmt.Errorf("expected %s task to be in 'Running' but got '%s'", constants.DaemonTaskName, stdout) + } + return nil +} + +func fixDaemonTaskRunning() error { + _, stderr, err := powershell.Execute("Start-ScheduledTask", "-TaskName", constants.DaemonTaskName) + if err != nil { + logging.Debugf("unable to run the %s task: %v : %s", constants.DaemonTaskName, err, stderr) + return err + } + return nil +} + +func checkIfOlderTask() error { + stdout, stderr, err := powershell.Execute(fmt.Sprintf(`(Get-ScheduledTask -TaskName "%s").Version`, constants.DaemonTaskName)) + if err != nil { + return fmt.Errorf("%s task is not running: %v : %s", constants.DaemonTaskName, err, stderr) + } + if strings.TrimSpace(stdout) != version.GetCRCVersion() { + return fmt.Errorf("expected %s task to be on version '%s' but got '%s'", constants.DaemonTaskName, version.GetCRCVersion(), stdout) + } + return nil +} diff --git a/pkg/crc/preflight/preflight_windows.go b/pkg/crc/preflight/preflight_windows.go index f3a01a121f..63157f8951 100644 --- a/pkg/crc/preflight/preflight_windows.go +++ b/pkg/crc/preflight/preflight_windows.go @@ -112,6 +112,29 @@ var vsockChecks = []Check{ }, } +var daemonTaskChecks = []Check{ + { + configKeySuffix: "check-daemon-task-install", + checkDescription: "Checking if daemon task is installed", + check: checkIfDaemonTaskInstalled, + fixDescription: "Installing the daemon task", + fix: fixDaemonTaskInstalled, + cleanupDescription: "Removing daemon task", + cleanup: removeDaemonTask, + + labels: labels{Os: Windows}, + }, + { + configKeySuffix: "check-daemon-task-running", + checkDescription: "Checking if daemon task is running", + check: checkIfDaemonTaskRunning, + fixDescription: "Running the daemon task", + fix: fixDaemonTaskRunning, + + labels: labels{Os: Windows}, + }, +} + var errReboot = errors.New("Please reboot your system and run 'crc setup' to complete the setup process") func username() string { @@ -157,6 +180,7 @@ func getChecks(bundlePath string, preset crcpreset.Preset) []Check { checks = append(checks, vsockChecks...) checks = append(checks, bundleCheck(bundlePath, preset)) checks = append(checks, genericCleanupChecks...) + checks = append(checks, daemonTaskChecks...) return checks } diff --git a/pkg/crc/preflight/preflight_windows_test.go b/pkg/crc/preflight/preflight_windows_test.go index b4c745aae7..37eadd1087 100644 --- a/pkg/crc/preflight/preflight_windows_test.go +++ b/pkg/crc/preflight/preflight_windows_test.go @@ -13,13 +13,13 @@ import ( func TestCountConfigurationOptions(t *testing.T) { cfg := config.New(config.NewEmptyInMemoryStorage()) RegisterSettings(cfg) - assert.Len(t, cfg.AllConfigs(), 10) + assert.Len(t, cfg.AllConfigs(), 12) } func TestCountPreflights(t *testing.T) { - assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 15) - assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 15) + assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17) + assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17) - assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16) - assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16) + assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18) + assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18) }