Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Run 'crc daemon' as task for 'crc setup' #3088

Merged
merged 6 commits into from Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 0 additions & 9 deletions cmd/crc/cmd/daemon.go
Expand Up @@ -19,15 +19,13 @@ import (
"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/crc/pkg/crc/preflight"
"github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/gvisor-tap-vsock/pkg/virtualnetwork"
"github.com/docker/go-units"
"github.com/gorilla/handlers"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/util/exec"
)

var watchdog bool
Expand Down Expand Up @@ -56,13 +54,6 @@ var daemonCmd = &cobra.Command{
return errors.New("daemon is already running")
}

if err := preflight.StartPreflightChecks(config); err != nil {
return exec.CodeExitError{
Err: err,
Code: preflightFailedExitCode,
}
}

virtualNetworkConfig := types.Configuration{
Debug: false, // never log packets
CaptureFile: os.Getenv("CRC_DAEMON_PCAP_FILE"),
Expand Down
4 changes: 4 additions & 0 deletions packaging/windows/product.wxs.template
Expand Up @@ -83,6 +83,8 @@
<CustomAction Id="RemoveCrcGroup" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />
<SetProperty Action="CARemoveCrcUsersGroupRollback" Id="RemoveCrcGroupRollback" Value='"[System64Folder]cmd.exe" /c net localgroup crc-users /del' Before="RemoveCrcGroup" Sequence="execute"/>
<CustomAction Id="RemoveCrcGroupRollback" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="rollback" Impersonate="no" Return="ignore" />
<SetProperty Action="CARemoveCrcDaemonTask" Id="RemoveCrcDaemonTask" Value='"[System64Folder]cmd.exe" /c schtasks /delete /TN crcDaemon /F' Before='RemoveFiles' Sequence="execute"/>
<CustomAction Id="RemoveCrcDaemonTask" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />

<SetProperty Action="CAInstallHyperv" Id="InstallHyperv" Value='"[System64Folder]dism.exe" /online /enable-feature /featureName:microsoft-hyper-v-all /NoRestart /quiet' Before="InstallHyperv" Sequence="execute"/>
<CustomAction Id="InstallHyperv" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Impersonate="no" Return="ignore" />
Expand All @@ -98,6 +100,7 @@
<Custom Action="RemoveCrcGroup" After="RemoveFolders"> Installed AND NOT PATCH AND REMOVE~="ALL" AND NOT UPGRADINGPRODUCTCODE</Custom>
<Custom Action="InstallHyperv" Before="CreateCrcGroup"> NOT Installed AND NOT REMOVE~="ALL" AND NOT WIX_UPGRADE_DETECTED</Custom>
<Custom Action="RemoveCrcGroupRollback" After="CreateCrcGroup"> NOT Installed AND NOT REMOVE~="ALL" AND NOT WIX_UPGRADE_DETECTED</Custom>
<Custom Action="RemoveCrcDaemonTask" Before='RemoveFiles'>Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
<ScheduleReboot After="InstallFinalize"> NOT Installed AND NOT REMOVE~="ALL" AND NOT WIX_UPGRADE_DETECTED</ScheduleReboot>
</InstallExecuteSequence>
<Feature Id="DefaultFeature" Level="1">
Expand All @@ -117,6 +120,7 @@
<ProgressText Action="RemoveCrcGroup">Removing crc-users group</ProgressText>
<ProgressText Action="InstallHyperv">Installing Hyper-V</ProgressText>
<ProgressText Action="AddUserToHypervAdminGroup">Adding user: [LogonUser] to Hyper-V Administrators group</ProgressText>
<ProgressText Action="RemoveCrcDaemonTask">Removing crcDaemon task</ProgressText>
</UI>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
<!-- this should help to propagate env var changes -->
Expand Down
2 changes: 2 additions & 0 deletions pkg/crc/constants/constants_windows.go
Expand Up @@ -5,4 +5,6 @@ const (
PodmanRemoteExecutableName = "podman.exe"
TapSocketPath = ""
DaemonHTTPNamedPipe = `\\.\pipe\crc-http`
DaemonTaskName = "crcDaemon"
AdminHelperServiceName = "CodeReadyContainersAdminHelper"
gbraad marked this conversation as resolved.
Show resolved Hide resolved
)
11 changes: 11 additions & 0 deletions pkg/crc/preflight/preflight_checks_windows.go
Expand Up @@ -183,3 +183,14 @@ func removeCrcVM() (err error) {
logging.Debug("'crc' VM is removed")
return nil
}

func checkIfAdminHelperServiceRunning() error {
stdout, stderr, err := powershell.Execute(fmt.Sprintf("(Get-Service %s).Status", constants.AdminHelperServiceName))
if err != nil {
return fmt.Errorf("%s service is not present %v: %s", constants.AdminHelperServiceName, err, stderr)
}
if strings.TrimSpace(stdout) != "Running" {
return fmt.Errorf("%s service is not running", constants.AdminHelperServiceName)
}
return nil
}
159 changes: 159 additions & 0 deletions pkg/crc/preflight/preflight_daemon_task_check_windows.go
@@ -0,0 +1,159 @@
package preflight

import (
"bytes"
"encoding/xml"
"fmt"
"os"
"strings"

"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/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 = `<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>Run crc daemon as a task</Description>
<Version>%s</Version>
</RegistrationInfo>
<Settings>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<Hidden>true</Hidden>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<IdleSettings>
<Duration>PT10M</Duration>
<WaitTimeout>PT1H</WaitTimeout>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
</Settings>
<Triggers />
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-WindowStyle Hidden -Command %s</Arguments>
cfergeau marked this conversation as resolved.
Show resolved Hide resolved
</Exec>
</Actions>
</Task>
`
)

func genDaemonTaskInstallTemplate(crcVersion, daemonCommand string) (string, error) {
var escapedName bytes.Buffer
if err := xml.EscapeText(&escapedName, []byte(daemonCommand)); err != nil {
return "", err
}

return fmt.Sprintf(daemonTaskTemplate,
crcVersion,
escapedName.String(),
), nil
}

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
}

praveenkumar marked this conversation as resolved.
Show resolved Hide resolved
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)

taskContent, err := genDaemonTaskInstallTemplate(
version.GetCRCVersion(),
binPathWithArgs,
)
if err != nil {
return err
}

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 {
if isDaemonRunningWithReleasedVersion() {
return nil
}
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)
praveenkumar marked this conversation as resolved.
Show resolved Hide resolved
}
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
}

func isDaemonRunningWithReleasedVersion() bool {
ver, err := daemonclient.New().APIClient.Version()
if err != nil {
return false
}
if ver.CrcVersion != version.GetCRCVersion() {
logging.Debugf("Daemon is running with %s version but binary version is %s", ver.CrcVersion, version.GetCRCVersion())
return false
}
return true
}
41 changes: 39 additions & 2 deletions pkg/crc/preflight/preflight_windows.go
Expand Up @@ -83,14 +83,14 @@ var hypervPreflightChecks = []Check{
check: checkIfHyperVVirtualSwitchExists,
flags: StartUpOnly,

labels: labels{Os: Windows},
labels: labels{Os: Windows, NetworkMode: System},
},
{
cleanupDescription: "Removing dns server from interface",
cleanup: removeDNSServerAddress,
flags: CleanUpOnly,

labels: labels{Os: Windows},
labels: labels{Os: Windows, NetworkMode: System},
},
{
cleanupDescription: "Removing crc's virtual machine",
Expand All @@ -112,6 +112,41 @@ 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 adminHelperServiceCheks = []Check{
{
configKeySuffix: "check-admin-helper-service-running",
checkDescription: "Checking admin helper service is running",
check: checkIfAdminHelperServiceRunning,
fixDescription: "Make sure you installed the crc using installer",
flags: NoFix,

labels: labels{Os: Windows},
},
}

var errReboot = errors.New("Please reboot your system and run 'crc setup' to complete the setup process")

func username() string {
Expand Down Expand Up @@ -157,6 +192,8 @@ 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...)
checks = append(checks, adminHelperServiceCheks...)
return checks
}

Expand Down
10 changes: 5 additions & 5 deletions pkg/crc/preflight/preflight_windows_test.go
Expand Up @@ -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), 18)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18)

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), 17)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
}