diff --git a/pkg/configurer/common.go b/pkg/configurer/common.go index e991dc6a..acc50ee8 100644 --- a/pkg/configurer/common.go +++ b/pkg/configurer/common.go @@ -10,6 +10,10 @@ import ( "github.com/k0sproject/rig/os" ) +type rebootable interface { + Reboot() error +} + type DockerConfigurer struct{} // GetDockerInfo gets docker info from the host. diff --git a/pkg/configurer/installer.go b/pkg/configurer/installer.go index a1f977cc..fd9efb49 100644 --- a/pkg/configurer/installer.go +++ b/pkg/configurer/installer.go @@ -24,10 +24,6 @@ func GetInstaller(source string) (string, error) { return path, nil } - if path == "" { - return "", fmt.Errorf("%w; skipping failed installer download", ErrInstallerDownloadFailed) - } - path, getErr := downloadInstaller(source) if getErr != nil { return "", fmt.Errorf("%w, installer download failed; %s", ErrInstallerDownloadFailed, getErr.Error()) diff --git a/pkg/configurer/windows.go b/pkg/configurer/windows.go index 1fd5299f..4c92715a 100644 --- a/pkg/configurer/windows.go +++ b/pkg/configurer/windows.go @@ -39,10 +39,6 @@ func (c WindowsConfigurer) MCRConfigPath() string { return `C:\ProgramData\Docker\config\daemon.json` } -type rebootable interface { - Reboot() error -} - var errRebootRequired = fmt.Errorf("reboot required") // InstallMCRLicense for license install.. @@ -88,23 +84,46 @@ func (c WindowsConfigurer) InstallMCR(h os.Host, engineConfig commonconfig.MCRCo log.Infof("%s: running installer", h) output, err := h.ExecOutput(installCommand) + + needsReboot := false if err != nil { - return fmt.Errorf("failed to run MCR installer: %w", err) + if isExitCode3010(err) { + needsReboot = true + } else { + return fmt.Errorf("failed to run MCR installer: %w", err) + } } - if strings.Contains(output, "Your machine needs to be rebooted") { - log.Warnf("%s: host needs to be rebooted", h) - if rh, ok := h.(rebootable); ok { - if err := rh.Reboot(); err != nil { - return fmt.Errorf("%s: failed to reboot host: %w", h, err) - } + if !needsReboot && strings.Contains(output, "Your machine needs to be rebooted") { + needsReboot = true + } + + if needsReboot { + log.Warnf("%s: host needs to be rebooted after MCR install", h) + rh, ok := h.(rebootable) + if !ok { + return fmt.Errorf("%s: %w: host does not support reboot", h, errRebootRequired) + } + if err := rh.Reboot(); err != nil { + return fmt.Errorf("%s: failed to reboot host: %w", h, err) + } + // Machine is back up. Delete the ONSTART scheduled task so it does not + // trigger another reboot on subsequent startups. + if err := h.Exec(`schtasks /delete /tn "RigReboot" /f`); err != nil { + log.Warnf("%s: failed to clean up RigReboot task: %s", h, err) } - return fmt.Errorf("%s: %w: host isn't rebootable", h, errRebootRequired) + return nil } return nil } +// isExitCode3010 checks if the error is a command failure with Windows exit +// code 3010 (ERROR_SUCCESS_REBOOT_REQUIRED). +func isExitCode3010(err error) bool { + return err != nil && strings.Contains(err.Error(), "non-zero exit code: 3010") +} + // UninstallMCR uninstalls docker-ee engine // This relies on using the http://get.mirantis.com/install.ps1 script with the '-Uninstall' option, and some cleanup as per // https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-docker/configure-docker-daemon#how-to-uninstall-docker diff --git a/pkg/docker/image.go b/pkg/docker/image.go index d9275c0e..312764ea 100644 --- a/pkg/docker/image.go +++ b/pkg/docker/image.go @@ -95,7 +95,6 @@ func (i *Image) Exist(h *mkeconfig.Host) bool { // PullImages pulls multiple images parallelly by using a worker pool. func PullImages(h *mkeconfig.Host, images []*Image) error { wp := workerpool.New(5) - defer wp.StopWait() var mutex sync.Mutex var lastError error @@ -103,20 +102,20 @@ func PullImages(h *mkeconfig.Host, images []*Image) error { for _, image := range images { i := image // So we can safely pass i forward to pool without it getting mutated wp.Submit(func() { - mutex.Lock() - defer mutex.Unlock() - if lastError != nil { - return - } - - err := i.Pull(h) - if err != nil { + if err := i.Pull(h); err != nil { mutex.Lock() - lastError = err + if lastError == nil { + lastError = err + } + mutex.Unlock() } }) } + // Wait for all workers to complete before reading lastError. + // A deferred StopWait() would let the return expression evaluate + // before workers finish, potentially returning nil on a real error. + wp.StopWait() return lastError }