WSL commands execution refactoring

Introduced a new function to encapsulate the code to execute WSL
commands.

Signed-off-by: Mario Loriedo <mario.loriedo@gmail.com>
This commit is contained in:
Mario Loriedo
2025-07-02 09:43:12 +02:00
parent 2a6f190e8f
commit 9fbe2fffe9
5 changed files with 56 additions and 40 deletions

View File

@@ -48,7 +48,7 @@ func installWslKernel() error {
)
backoff := 500 * time.Millisecond
for i := 1; i < 6; i++ {
err = wutil.SilentExec("wsl", "--update")
err = wutil.SilentExec("--update")
if err == nil {
break
}

View File

@@ -20,6 +20,7 @@ import (
"github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/ignition"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/containers/podman/v5/pkg/machine/wsl/wutil"
"github.com/containers/podman/v5/utils"
"github.com/containers/storage/pkg/homedir"
"github.com/sirupsen/logrus"
@@ -100,7 +101,8 @@ func provisionWSLDist(name string, imagePath string, prompt string) (string, err
// 1. Wsl/Service/RegisterDistro/CreateVm/HCS/ERROR_NOT_SUPPORTED
// 2. Wsl/Service/RegisterDistro/CreateVm/HCS/HCS_E_SERVICE_NOT_AVAILABLE
cmdOutput := &bytes.Buffer{}
err = runCmdPassThroughTee(cmdOutput, "wsl", "--import", dist, distTarget, imagePath, "--version", "2")
cmd := wutil.NewWSLCommand("--import", dist, distTarget, imagePath, "--version", "2")
err = runCmdPassThroughTee(cmdOutput, cmd)
decoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()
decoded, _, decodeErr := transform.Bytes(decoder, cmdOutput.Bytes())
if decodeErr != nil {
@@ -373,13 +375,15 @@ func installWsl() error {
return err
}
defer log.Close()
if err := runCmdPassThroughTee(log, "dism", "/online", "/enable-feature",
"/featurename:Microsoft-Windows-Subsystem-Linux", "/all", "/norestart"); isMsiError(err) {
cmd := exec.Command("dism", "/online", "/enable-feature",
"/featurename:Microsoft-Windows-Subsystem-Linux", "/all", "/norestart")
if err := runCmdPassThroughTee(log, cmd); isMsiError(err) {
return fmt.Errorf("could not enable WSL Feature: %w", err)
}
if err = runCmdPassThroughTee(log, "dism", "/online", "/enable-feature",
"/featurename:VirtualMachinePlatform", "/all", "/norestart"); isMsiError(err) {
cmd = exec.Command("dism", "/online", "/enable-feature",
"/featurename:VirtualMachinePlatform", "/all", "/norestart")
if err = runCmdPassThroughTee(log, cmd); isMsiError(err) {
return fmt.Errorf("could not enable Virtual Machine Feature: %w", err)
}
@@ -466,50 +470,49 @@ func withUser(s string, user string) string {
func wslInvoke(dist string, arg ...string) error {
newArgs := []string{"-u", "root", "-d", dist}
newArgs = append(newArgs, arg...)
return runCmdPassThrough("wsl", newArgs...)
cmd := wutil.NewWSLCommand(newArgs...)
return runCmdPassThrough(cmd)
}
func wslPipe(input string, dist string, arg ...string) error {
newArgs := []string{"-u", "root", "-d", dist}
newArgs = append(newArgs, arg...)
return pipeCmdPassThrough("wsl", input, newArgs...)
cmd := wutil.NewWSLCommand(newArgs...)
return pipeCmdPassThrough(cmd, input)
}
func runCmdPassThrough(name string, arg ...string) error {
logrus.Debugf("Running command: %s %v", name, arg)
cmd := exec.Command(name, arg...)
func runCmdPassThrough(cmd *exec.Cmd) error {
logrus.Debugf("Running command: %s %v", cmd.Path, cmd.Args)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("command %s %v failed: %w", name, arg, err)
return fmt.Errorf("command %s %v failed: %w", cmd.Path, cmd.Args, err)
}
return nil
}
func runCmdPassThroughTee(out io.Writer, name string, arg ...string) error {
logrus.Debugf("Running command: %s %v", name, arg)
func runCmdPassThroughTee(out io.Writer, cmd *exec.Cmd) error {
logrus.Debugf("Running command: %s %v", cmd.Path, cmd.Args)
// TODO - Perhaps improve this with a conpty pseudo console so that
// dism installer text bars mirror console behavior (redraw)
cmd := exec.Command(name, arg...)
cmd.Stdin = os.Stdin
cmd.Stdout = io.MultiWriter(os.Stdout, out)
cmd.Stderr = io.MultiWriter(os.Stderr, out)
if err := cmd.Run(); isMsiError(err) {
return fmt.Errorf("command %s %v failed: %w", name, arg, err)
return fmt.Errorf("command %s %v failed: %w", cmd.Path, cmd.Args, err)
}
return nil
}
func pipeCmdPassThrough(name string, input string, arg ...string) error {
logrus.Debugf("Running command: %s %v", name, arg)
cmd := exec.Command(name, arg...)
func pipeCmdPassThrough(cmd *exec.Cmd, input string) error {
logrus.Debugf("Running command: %s %v", cmd.Path, cmd.Args)
cmd.Stdin = strings.NewReader(input)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("command %s %v failed: %w", name, arg, err)
return fmt.Errorf("command %s %v failed: %w", cmd.Path, cmd.Args, err)
}
return nil
}
@@ -569,7 +572,7 @@ func getAllWSLDistros(running bool) (map[string]struct{}, error) {
if running {
args = append(args, "--running")
}
cmd := exec.Command("wsl", args...)
cmd := wutil.NewWSLCommand(args...)
out, err := cmd.StdoutPipe()
if err != nil {
return nil, err
@@ -598,7 +601,7 @@ func getAllWSLDistros(running bool) (map[string]struct{}, error) {
}
func isSystemdRunning(dist string) (bool, error) {
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "sh")
cmd := wutil.NewWSLCommand("-u", "root", "-d", dist, "sh")
cmd.Stdin = strings.NewReader(sysdpid + "\necho $SYSDPID\n")
out, err := cmd.StdoutPipe()
if err != nil {
@@ -621,26 +624,26 @@ func isSystemdRunning(dist string) (bool, error) {
err = cmd.Wait()
if err != nil {
return false, fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args, err, strings.TrimSpace(stderr.String()))
return false, fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args[1:], err, strings.TrimSpace(stderr.String()))
}
return result, nil
}
func terminateDist(dist string) error {
cmd := exec.Command("wsl", "--terminate", dist)
cmd := wutil.NewWSLCommand("--terminate", dist)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args, err, strings.TrimSpace(string(out)))
return fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args[1:], err, strings.TrimSpace(string(out)))
}
return nil
}
func unregisterDist(dist string) error {
cmd := exec.Command("wsl", "--unregister", dist)
cmd := wutil.NewWSLCommand("--unregister", dist)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args, err, strings.TrimSpace(string(out)))
return fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args[1:], err, strings.TrimSpace(string(out)))
}
return nil
}
@@ -685,13 +688,14 @@ func getCPUs(name string) (uint64, error) {
if run, _ := isWSLRunning(dist); !run {
return 0, nil
}
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "nproc")
cmd := wutil.NewWSLCommand("-u", "root", "-d", dist, "nproc")
out, err := cmd.StdoutPipe()
if err != nil {
return 0, err
}
stderr := &bytes.Buffer{}
cmd.Stderr = stderr
cmd.Env = []string{"WSL_UTF8=1"}
if err = cmd.Start(); err != nil {
return 0, err
}
@@ -702,7 +706,7 @@ func getCPUs(name string) (uint64, error) {
}
err = cmd.Wait()
if err != nil {
return 0, fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args, err, strings.TrimSpace(strings.TrimSpace(stderr.String())))
return 0, fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args[1:], err, strings.TrimSpace(strings.TrimSpace(stderr.String())))
}
ret, err := strconv.Atoi(result)
@@ -715,7 +719,7 @@ func getMem(name string) (strongunits.MiB, error) {
if run, _ := isWSLRunning(dist); !run {
return 0, nil
}
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "cat", "/proc/meminfo")
cmd := wutil.NewWSLCommand("-u", "root", "-d", dist, "cat", "/proc/meminfo")
out, err := cmd.StdoutPipe()
if err != nil {
return 0, err
@@ -746,7 +750,7 @@ func getMem(name string) (strongunits.MiB, error) {
}
err = cmd.Wait()
if err != nil {
return 0, fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args, err, strings.TrimSpace(stderr.String()))
return 0, fmt.Errorf("command %s %v failed: %w (%s)", cmd.Path, cmd.Args[1:], err, strings.TrimSpace(stderr.String()))
}
return strongunits.MiB(total - available), err

View File

@@ -7,10 +7,10 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/wsl/wutil"
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/podman/v5/pkg/machine"
@@ -108,7 +108,8 @@ func (w WSLStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() error,
// below if we wanted to hard error on the wsl unregister
// of the vm
wslRemoveFunc := func() error {
if err := runCmdPassThrough("wsl", "--unregister", env.WithPodmanPrefix(mc.Name)); err != nil {
cmd := wutil.NewWSLCommand("--unregister", env.WithPodmanPrefix(mc.Name))
if err := runCmdPassThrough(cmd); err != nil {
return err
}
return nil
@@ -253,7 +254,7 @@ func (w WSLStubber) StopVM(mc *vmconfigs.MachineConfig, hardStop bool) error {
fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
}
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "sh")
cmd := wutil.NewWSLCommand("-u", "root", "-d", dist, "sh")
cmd.Stdin = strings.NewReader(waitTerm)
out := &bytes.Buffer{}
cmd.Stderr = out
@@ -263,7 +264,7 @@ func (w WSLStubber) StopVM(mc *vmconfigs.MachineConfig, hardStop bool) error {
return fmt.Errorf("executing wait command: %w", err)
}
exitCmd := exec.Command("wsl", "-u", "root", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
exitCmd := wutil.NewWSLCommand("-u", "root", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
if err = exitCmd.Run(); err != nil {
return fmt.Errorf("stopping systemd: %w", err)
}

View File

@@ -29,8 +29,13 @@ type wslStatus struct {
wslFeatureEnabled bool
}
func NewWSLCommand(arg ...string) *exec.Cmd {
cmd := exec.Command("wsl", arg...)
return cmd
}
func SilentExec(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd := NewWSLCommand(args...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
cmd.Stdout = nil
cmd.Stderr = nil
@@ -40,8 +45,8 @@ func SilentExec(command string, args ...string) error {
return nil
}
func SilentExecCmd(command string, args ...string) *exec.Cmd {
cmd := exec.Command(command, args...)
func SilentExecCmd(args ...string) *exec.Cmd {
cmd := NewWSLCommand(args...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
return cmd
}
@@ -53,7 +58,7 @@ func parseWSLStatus() wslStatus {
vmpFeatureEnabled: false,
wslFeatureEnabled: false,
}
cmd := SilentExecCmd("wsl", "--status")
cmd := SilentExecCmd("--status")
out, err := cmd.StdoutPipe()
cmd.Stderr = nil
if err != nil {
@@ -79,7 +84,7 @@ func IsWSLInstalled() bool {
}
func IsWSLStoreVersionInstalled() bool {
cmd := SilentExecCmd("wsl", "--version")
cmd := SilentExecCmd("--version")
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {

View File

@@ -144,3 +144,9 @@ func TestMatchOutputLine(t *testing.T) {
})
}
}
func TestNewWSLCommand(t *testing.T) {
cmd := NewWSLCommand("--status")
assert.Contains(t, cmd.Path, "wsl")
assert.Equal(t, []string{"--status"}, cmd.Args[1:])
}