summaryrefslogtreecommitdiff
path: root/pkg/machine
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/machine')
-rw-r--r--pkg/machine/config_test.go3
-rw-r--r--pkg/machine/e2e/list_test.go23
-rw-r--r--pkg/machine/ignition.go4
-rw-r--r--pkg/machine/keys.go21
-rw-r--r--pkg/machine/qemu/config.go4
-rw-r--r--pkg/machine/qemu/config_test.go3
-rw-r--r--pkg/machine/qemu/machine.go152
-rw-r--r--pkg/machine/qemu/machine_test.go3
-rw-r--r--pkg/machine/wsl/machine.go102
9 files changed, 253 insertions, 62 deletions
diff --git a/pkg/machine/config_test.go b/pkg/machine/config_test.go
index d9fc5425e..ca08660b9 100644
--- a/pkg/machine/config_test.go
+++ b/pkg/machine/config_test.go
@@ -1,3 +1,6 @@
+//go:build amd64 || arm64
+// +build amd64 arm64
+
package machine
import (
diff --git a/pkg/machine/e2e/list_test.go b/pkg/machine/e2e/list_test.go
index 1c8c6ac81..e2121e7bf 100644
--- a/pkg/machine/e2e/list_test.go
+++ b/pkg/machine/e2e/list_test.go
@@ -29,7 +29,7 @@ var _ = Describe("podman machine list", func() {
firstList, err := mb.setCmd(list).run()
Expect(err).NotTo(HaveOccurred())
Expect(firstList).Should(Exit(0))
- Expect(len(firstList.outputToStringSlice())).To(Equal(1)) // just the header
+ Expect(firstList.outputToStringSlice()).To(HaveLen(1)) // just the header
i := new(initMachine)
session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
@@ -39,7 +39,7 @@ var _ = Describe("podman machine list", func() {
secondList, err := mb.setCmd(list).run()
Expect(err).NotTo(HaveOccurred())
Expect(secondList).To(Exit(0))
- Expect(len(secondList.outputToStringSlice())).To(Equal(2)) // one machine and the header
+ Expect(secondList.outputToStringSlice()).To(HaveLen(2)) // one machine and the header
})
It("list machines with quiet or noheading", func() {
@@ -51,12 +51,12 @@ var _ = Describe("podman machine list", func() {
firstList, err := mb.setCmd(list.withQuiet()).run()
Expect(err).NotTo(HaveOccurred())
Expect(firstList).Should(Exit(0))
- Expect(len(firstList.outputToStringSlice())).To(Equal(0)) // No header with quiet
+ Expect(firstList.outputToStringSlice()).To(HaveLen(0)) // No header with quiet
noheaderSession, err := mb.setCmd(list.withNoHeading()).run() // noheader
Expect(err).NotTo(HaveOccurred())
Expect(noheaderSession).Should(Exit(0))
- Expect(len(noheaderSession.outputToStringSlice())).To(Equal(0))
+ Expect(noheaderSession.outputToStringSlice()).To(HaveLen(0))
i := new(initMachine)
session, err := mb.setName(name1).setCmd(i.withImagePath(mb.imagePath)).run()
@@ -70,7 +70,7 @@ var _ = Describe("podman machine list", func() {
secondList, err := mb.setCmd(list.withQuiet()).run()
Expect(err).NotTo(HaveOccurred())
Expect(secondList).To(Exit(0))
- Expect(len(secondList.outputToStringSlice())).To(Equal(2)) // two machines, no header
+ Expect(secondList.outputToStringSlice()).To(HaveLen(2)) // two machines, no header
listNames := secondList.outputToStringSlice()
stripAsterisk(listNames)
@@ -116,10 +116,10 @@ var _ = Describe("podman machine list", func() {
// go format
list := new(listMachine)
- listSession, err := mb.setCmd(list.withFormat("{{.Name}}").withNoHeading()).run()
+ listSession, err := mb.setCmd(list.withFormat("{{.Name}}")).run()
Expect(err).NotTo(HaveOccurred())
Expect(listSession).To(Exit(0))
- Expect(len(listSession.outputToStringSlice())).To(Equal(1))
+ Expect(listSession.outputToStringSlice()).To(HaveLen(1))
listNames := listSession.outputToStringSlice()
stripAsterisk(listNames)
@@ -135,6 +135,15 @@ var _ = Describe("podman machine list", func() {
var listResponse []*machine.ListReporter
err = jsoniter.Unmarshal(listSession.Bytes(), &listResponse)
Expect(err).To(BeNil())
+
+ // table format includes the header
+ list = new(listMachine)
+ listSession3, err3 := mb.setCmd(list.withFormat("table {{.Name}}")).run()
+ Expect(err3).NotTo(HaveOccurred())
+ Expect(listSession3).To(Exit(0))
+ listNames3 := listSession3.outputToStringSlice()
+ Expect(listNames3).To(HaveLen(2))
+ Expect(listNames3).To(ContainSubstring("NAME"))
})
})
diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go
index 35a9a30cb..f4602cc95 100644
--- a/pkg/machine/ignition.go
+++ b/pkg/machine/ignition.go
@@ -93,7 +93,7 @@ func NewIgnitionFile(ign DynamicIgnition) error {
tz string
)
// local means the same as the host
- // lookup where it is pointing to on the host
+ // look up where it is pointing to on the host
if ign.TimeZone == "local" {
tz, err = getLocalTimeZone()
if err != nil {
@@ -348,7 +348,7 @@ Delegate=memory pids cpu io
},
})
- // Setup /etc/subuid and /etc/subgid
+ // Set up /etc/subuid and /etc/subgid
for _, sub := range []string{"/etc/subuid", "/etc/subgid"} {
files = append(files, File{
Node: Node{
diff --git a/pkg/machine/keys.go b/pkg/machine/keys.go
index 45d9801cc..463271427 100644
--- a/pkg/machine/keys.go
+++ b/pkg/machine/keys.go
@@ -4,14 +4,15 @@
package machine
import (
- "errors"
"fmt"
+ "io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
+ "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -51,7 +52,23 @@ func CreateSSHKeysPrefix(dir string, file string, passThru bool, skipExisting bo
// generatekeys creates an ed25519 set of keys
func generatekeys(writeLocation string) error {
args := append(append([]string{}, sshCommand[1:]...), writeLocation)
- return exec.Command(sshCommand[0], args...).Run()
+ cmd := exec.Command(sshCommand[0], args...)
+ stdErr, err := cmd.StderrPipe()
+ if err != nil {
+ return err
+ }
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ waitErr := cmd.Wait()
+ if waitErr == nil {
+ return nil
+ }
+ errMsg, err := io.ReadAll(stdErr)
+ if err != nil {
+ return fmt.Errorf("key generation failed, unable to read from stderr: %w", waitErr)
+ }
+ return fmt.Errorf("failed to generate keys: %s: %w", string(errMsg), waitErr)
}
// generatekeys creates an ed25519 set of keys
diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go
index 56c95e3b3..bada1af9b 100644
--- a/pkg/machine/qemu/config.go
+++ b/pkg/machine/qemu/config.go
@@ -72,8 +72,10 @@ type MachineVM struct {
Mounts []machine.Mount
// Name of VM
Name string
- // PidFilePath is the where the PID file lives
+ // PidFilePath is the where the Proxy PID file lives
PidFilePath machine.VMFile
+ // VMPidFilePath is the where the VM PID file lives
+ VMPidFilePath machine.VMFile
// QMPMonitor is the qemu monitor object for sending commands
QMPMonitor Monitor
// ReadySocket tells host when vm is booted
diff --git a/pkg/machine/qemu/config_test.go b/pkg/machine/qemu/config_test.go
index 4d96ec6e7..72cb3ed90 100644
--- a/pkg/machine/qemu/config_test.go
+++ b/pkg/machine/qemu/config_test.go
@@ -1,3 +1,6 @@
+//go:build (amd64 && !windows) || (arm64 && !windows)
+// +build amd64,!windows arm64,!windows
+
package qemu
import (
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 288b2eeb0..ca7947e34 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -8,6 +8,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
+ "errors"
"fmt"
"io/fs"
"io/ioutil"
@@ -30,8 +31,8 @@ import (
"github.com/containers/storage/pkg/homedir"
"github.com/digitalocean/go-qemu/qmp"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
)
var (
@@ -107,6 +108,9 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
if err != nil {
return nil, err
}
+ if err := vm.setPIDSocket(); err != nil {
+ return nil, err
+ }
cmd := []string{execPath}
// Add memory
cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...)
@@ -135,11 +139,9 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
"-device", "virtio-serial",
// qemu needs to establish the long name; other connections can use the symlink'd
"-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=" + vm.Name + "_ready",
- "-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...)
+ "-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0",
+ "-pidfile", vm.VMPidFilePath.GetPath()}...)
vm.CmdLine = cmd
- if err := vm.setPIDSocket(); err != nil {
- return nil, err
- }
return vm, nil
}
@@ -209,7 +211,7 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error {
vm.Rootful = old.Rootful
vm.UID = old.UID
- // Backup the original config file
+ // Back up the original config file
if err := os.Rename(configPath, configPath+".orig"); err != nil {
return err
}
@@ -432,12 +434,12 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
if v.Name != machine.DefaultMachineName {
suffix = " " + v.Name
}
- return setErrors, errors.Errorf("cannot change settings while the vm is running, run 'podman machine stop%s' first", suffix)
+ return setErrors, fmt.Errorf("cannot change settings while the vm is running, run 'podman machine stop%s' first", suffix)
}
if opts.Rootful != nil && v.Rootful != *opts.Rootful {
if err := v.setRootful(*opts.Rootful); err != nil {
- setErrors = append(setErrors, errors.Wrapf(err, "failed to set rootful option"))
+ setErrors = append(setErrors, fmt.Errorf("failed to set rootful option: %w", err))
} else {
v.Rootful = *opts.Rootful
}
@@ -455,7 +457,7 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
if opts.DiskSize != nil && v.DiskSize != *opts.DiskSize {
if err := v.resizeDisk(*opts.DiskSize, v.DiskSize); err != nil {
- setErrors = append(setErrors, errors.Wrapf(err, "failed to resize disk"))
+ setErrors = append(setErrors, fmt.Errorf("failed to resize disk: %w", err))
} else {
v.DiskSize = *opts.DiskSize
}
@@ -512,7 +514,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
forwardSock, forwardState, err := v.startHostNetworking()
if err != nil {
- return errors.Errorf("unable to start host networking: %q", err)
+ return fmt.Errorf("unable to start host networking: %q", err)
}
rtPath, err := getRuntimeDir()
@@ -580,7 +582,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
if !errors.Is(err, os.ErrNotExist) {
return err
}
- // lookup qemu again maybe the path was changed, https://github.com/containers/podman/issues/13394
+ // look up qemu again maybe the path was changed, https://github.com/containers/podman/issues/13394
cfg, err := config.Default()
if err != nil {
return err
@@ -591,7 +593,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
_, err = os.StartProcess(cmd[0], cmd, attr)
if err != nil {
- return errors.Wrapf(err, "unable to execute %q", cmd)
+ return fmt.Errorf("unable to execute %q: %w", cmd, err)
}
}
fmt.Println("Waiting for VM ...")
@@ -698,7 +700,7 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.Status, err
}
b, err := monitor.Run(input)
if err != nil {
- if errors.Cause(err) == os.ErrNotExist {
+ if errors.Is(err, os.ErrNotExist) {
return machine.Stopped, nil
}
return "", err
@@ -753,17 +755,17 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
if _, err := os.Stat(v.PidFilePath.GetPath()); os.IsNotExist(err) {
return nil
}
- pidString, err := v.PidFilePath.Read()
+ proxyPidString, err := v.PidFilePath.Read()
if err != nil {
return err
}
- pidNum, err := strconv.Atoi(string(pidString))
+ proxyPid, err := strconv.Atoi(string(proxyPidString))
if err != nil {
return err
}
- p, err := os.FindProcess(pidNum)
- if p == nil && err != nil {
+ proxyProc, err := os.FindProcess(proxyPid)
+ if proxyProc == nil && err != nil {
return err
}
@@ -772,7 +774,7 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
return err
}
// Kill the process
- if err := p.Kill(); err != nil {
+ if err := proxyProc.Kill(); err != nil {
return err
}
// Remove the pidfile
@@ -788,22 +790,50 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
// FIXME: this error should probably be returned
return nil //nolint: nilerr
}
-
disconnected = true
- waitInternal := 250 * time.Millisecond
- for i := 0; i < 5; i++ {
- state, err := v.State(false)
- if err != nil {
- return err
- }
- if state != machine.Running {
- break
+
+ if err := v.ReadySocket.Delete(); err != nil {
+ return err
+ }
+
+ if v.VMPidFilePath.GetPath() == "" {
+ // no vm pid file path means it's probably a machine created before we
+ // started using it, so we revert to the old way of waiting for the
+ // machine to stop
+ fmt.Println("Waiting for VM to stop running...")
+ waitInternal := 250 * time.Millisecond
+ for i := 0; i < 5; i++ {
+ state, err := v.State(false)
+ if err != nil {
+ return err
+ }
+ if state != machine.Running {
+ break
+ }
+ time.Sleep(waitInternal)
+ waitInternal *= 2
}
- time.Sleep(waitInternal)
- waitInternal *= 2
+ // after the machine stops running it normally takes about 1 second for the
+ // qemu VM to exit so we wait a bit to try to avoid issues
+ time.Sleep(2 * time.Second)
+ return nil
+ }
+
+ vmPidString, err := v.VMPidFilePath.Read()
+ if err != nil {
+ return err
+ }
+ vmPid, err := strconv.Atoi(strings.TrimSpace(string(vmPidString)))
+ if err != nil {
+ return err
+ }
+
+ fmt.Println("Waiting for VM to exit...")
+ for isProcessAlive(vmPid) {
+ time.Sleep(500 * time.Millisecond)
}
- return v.ReadySocket.Delete()
+ return nil
}
// NewQMPMonitor creates the monitor subsection of our vm
@@ -849,7 +879,7 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
}
if state == machine.Running {
if !opts.Force {
- return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name)
+ return "", nil, fmt.Errorf("running vm %q cannot be destroyed", v.Name)
}
err := v.Stop(v.Name, machine.StopOptions{})
if err != nil {
@@ -896,8 +926,11 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
// remove socket and pid file if any: warn at low priority if things fail
// Remove the pidfile
+ if err := v.VMPidFilePath.Delete(); err != nil {
+ logrus.Debugf("Error while removing VM pidfile: %v", err)
+ }
if err := v.PidFilePath.Delete(); err != nil {
- logrus.Debugf("Error while removing pidfile: %v", err)
+ logrus.Debugf("Error while removing proxy pidfile: %v", err)
}
// Remove socket
if err := v.QMPMonitor.Address.Delete(); err != nil {
@@ -930,7 +963,12 @@ func (v *MachineVM) State(bypass bool) (machine.Status, error) {
}
monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout)
if err != nil {
- // FIXME: this error should probably be returned
+ // If an improper cleanup was done and the socketmonitor was not deleted,
+ // it can appear as though the machine state is not stopped. Check for ECONNREFUSED
+ // almost assures us that the vm is stopped.
+ if errors.Is(err, syscall.ECONNREFUSED) {
+ return machine.Stopped, nil
+ }
return "", err
}
if err := monitor.Connect(); err != nil {
@@ -963,7 +1001,7 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
return err
}
if state != machine.Running {
- return errors.Errorf("vm %q is not running", v.Name)
+ return fmt.Errorf("vm %q is not running", v.Name)
}
username := opts.Username
@@ -975,7 +1013,7 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
port := strconv.Itoa(v.Port)
args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile=/dev/null",
- "-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR"}
+ "-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR", "-o", "SetEnv=LC_ALL="}
if len(opts.Args) > 0 {
args = append(args, opts.Args...)
} else {
@@ -1074,6 +1112,7 @@ func getVMInfos() ([]*machine.ListResponse, error) {
listEntry.RemoteUsername = vm.RemoteUsername
listEntry.IdentityPath = vm.IdentityPath
listEntry.CreatedAt = vm.Created
+ listEntry.Starting = vm.Starting
if listEntry.CreatedAt.IsZero() {
listEntry.CreatedAt = time.Now()
@@ -1087,6 +1126,7 @@ func getVMInfos() ([]*machine.ListResponse, error) {
if err != nil {
return err
}
+ listEntry.Running = state == machine.Running
if !vm.LastUp.IsZero() { // this means we have already written a time to the config
listEntry.LastUp = vm.LastUp
@@ -1097,12 +1137,6 @@ func getVMInfos() ([]*machine.ListResponse, error) {
return err
}
}
- switch state {
- case machine.Running:
- listEntry.Running = true
- case machine.Starting:
- listEntry.Starting = true
- }
listed = append(listed, listEntry)
}
@@ -1131,7 +1165,7 @@ func (p *Provider) IsValidVMName(name string) (bool, error) {
func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) {
vms, err := getVMInfos()
if err != nil {
- return false, "", errors.Wrap(err, "error checking VM active")
+ return false, "", fmt.Errorf("error checking VM active: %w", err)
}
for _, vm := range vms {
if vm.Running || vm.Starting {
@@ -1142,7 +1176,7 @@ func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) {
}
// startHostNetworking runs a binary on the host system that allows users
-// to setup port forwarding to the podman virtual machine
+// to set up port forwarding to the podman virtual machine
func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
cfg, err := config.Default()
if err != nil {
@@ -1183,7 +1217,10 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
fmt.Println(cmd)
}
_, err = os.StartProcess(cmd[0], cmd, attr)
- return forwardSock, state, errors.Wrapf(err, "unable to execute: %q", cmd)
+ if err != nil {
+ return "", 0, fmt.Errorf("unable to execute: %q: %w", cmd, err)
+ }
+ return forwardSock, state, nil
}
func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwardingState) {
@@ -1309,13 +1346,19 @@ func (v *MachineVM) setPIDSocket() error {
if !rootless.IsRootless() {
rtPath = "/run"
}
- pidFileName := fmt.Sprintf("%s.pid", v.Name)
socketDir := filepath.Join(rtPath, "podman")
- pidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, pidFileName), &pidFileName)
+ vmPidFileName := fmt.Sprintf("%s_vm.pid", v.Name)
+ proxyPidFileName := fmt.Sprintf("%s_proxy.pid", v.Name)
+ vmPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, vmPidFileName), &vmPidFileName)
+ if err != nil {
+ return err
+ }
+ proxyPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, proxyPidFileName), &proxyPidFileName)
if err != nil {
return err
}
- v.PidFilePath = *pidFilePath
+ v.VMPidFilePath = *vmPidFilePath
+ v.PidFilePath = *proxyPidFilePath
return nil
}
@@ -1446,7 +1489,7 @@ func (v *MachineVM) update() error {
b, err := v.ConfigPath.Read()
if err != nil {
if errors.Is(err, os.ErrNotExist) {
- return errors.Wrap(machine.ErrNoSuchVM, v.Name)
+ return fmt.Errorf("%v: %w", v.Name, machine.ErrNoSuchVM)
}
return err
}
@@ -1522,7 +1565,7 @@ func (v *MachineVM) resizeDisk(diskSize uint64, oldSize uint64) error {
// only if the virtualdisk size is less than
// the given disk size
if diskSize < oldSize {
- return errors.Errorf("new disk size must be larger than current disk size: %vGB", oldSize)
+ return fmt.Errorf("new disk size must be larger than current disk size: %vGB", oldSize)
}
// Find the qemu executable
@@ -1538,7 +1581,7 @@ func (v *MachineVM) resizeDisk(diskSize uint64, oldSize uint64) error {
resize.Stdout = os.Stdout
resize.Stderr = os.Stderr
if err := resize.Run(); err != nil {
- return errors.Errorf("resizing image: %q", err)
+ return fmt.Errorf("resizing image: %q", err)
}
return nil
@@ -1652,3 +1695,12 @@ func (p *Provider) RemoveAndCleanMachines() error {
}
return prevErr
}
+
+func isProcessAlive(pid int) bool {
+ err := unix.Kill(pid, syscall.Signal(0))
+ if err == nil || err == unix.EPERM {
+ return true
+ }
+
+ return false
+}
diff --git a/pkg/machine/qemu/machine_test.go b/pkg/machine/qemu/machine_test.go
index 62ca6068a..4c393d0f4 100644
--- a/pkg/machine/qemu/machine_test.go
+++ b/pkg/machine/qemu/machine_test.go
@@ -1,3 +1,6 @@
+//go:build (amd64 && !windows) || (arm64 && !windows)
+// +build amd64,!windows arm64,!windows
+
package qemu
import (
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 075f42cb2..04215d545 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -18,6 +18,7 @@ import (
"strings"
"time"
+ "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/homedir"
@@ -116,6 +117,43 @@ ln -fs /home/[USER]/.config/systemd/[USER]/linger-example.service \
/home/[USER]/.config/systemd/[USER]/default.target.wants/linger-example.service
`
+const proxyConfigSetup = `#!/bin/bash
+
+SYSTEMD_CONF=/etc/systemd/system.conf.d/default-env.conf
+ENVD_CONF=/etc/environment.d/default-env.conf
+PROFILE_CONF=/etc/profile.d/default-env.sh
+
+IFS="|"
+read proxies
+
+mkdir -p /etc/profile.d /etc/environment.d /etc/systemd/system.conf.d/
+rm -f $SYSTEMD_CONF
+for proxy in $proxies; do
+ output+="$proxy "
+done
+echo "[Manager]" >> $SYSTEMD_CONF
+echo -ne "DefaultEnvironment=" >> $SYSTEMD_CONF
+
+echo $output >> $SYSTEMD_CONF
+rm -f $ENVD_CONF
+for proxy in $proxies; do
+ echo "$proxy" >> $ENVD_CONF
+done
+rm -f $PROFILE_CONF
+for proxy in $proxies; do
+ echo "export $proxy" >> $PROFILE_CONF
+done
+`
+
+const proxyConfigAttempt = `if [ -f /usr/local/bin/proxyinit ]; \
+then /usr/local/bin/proxyinit; \
+else exit 42; \
+fi`
+
+const clearProxySettings = `rm -f /etc/systemd/system.conf.d/default-env.conf \
+ /etc/environment.d/default-env.conf \
+ /etc/profile.d/default-env.sh`
+
const wslInstallError = `Could not %s. See previous output for any potential failure details.
If you can not resolve the issue, and rerunning fails, try the "wsl --install" process
outlined in the following article:
@@ -300,6 +338,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
return cont, err
}
+ _ = setupWslProxyEnv()
homeDir := homedir.Get()
sshDir := filepath.Join(homeDir, ".ssh")
v.IdentityPath = filepath.Join(sshDir, v.Name)
@@ -526,6 +565,40 @@ func configureSystem(v *MachineVM, dist string) error {
return nil
}
+func configureProxy(dist string, useProxy bool) error {
+ if !useProxy {
+ _ = runCmdPassThrough("wsl", "-d", dist, "sh", "-c", clearProxySettings)
+ return nil
+ }
+ var content string
+ for i, key := range config.ProxyEnv {
+ if value, _ := os.LookupEnv(key); len(value) > 0 {
+ var suffix string
+ if i < (len(config.ProxyEnv) - 1) {
+ suffix = "|"
+ }
+ content = fmt.Sprintf("%s%s=\"%s\"%s", content, key, value, suffix)
+ }
+ }
+
+ if err := pipeCmdPassThrough("wsl", content, "-d", dist, "sh", "-c", proxyConfigAttempt); err != nil {
+ const failMessage = "Failure creating proxy configuration"
+ if exitErr, isExit := err.(*exec.ExitError); isExit && exitErr.ExitCode() != 42 {
+ return errors.Wrap(err, failMessage)
+ }
+
+ fmt.Println("Installing proxy support")
+ _ = pipeCmdPassThrough("wsl", proxyConfigSetup, "-d", dist, "sh", "-c",
+ "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit")
+
+ if err = pipeCmdPassThrough("wsl", content, "-d", dist, "/usr/local/bin/proxyinit"); err != nil {
+ return errors.Wrap(err, failMessage)
+ }
+ }
+
+ return nil
+}
+
func enableUserLinger(v *MachineVM, dist string) error {
lingerCmd := "mkdir -p /var/lib/systemd/linger; touch /var/lib/systemd/linger/" + v.RemoteUsername
if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", lingerCmd); err != nil {
@@ -555,6 +628,11 @@ func installScripts(dist string) error {
return errors.Wrap(err, "could not create bootstrap script for guest OS")
}
+ if err := pipeCmdPassThrough("wsl", proxyConfigSetup, "-d", dist, "sh", "-c",
+ "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit"); err != nil {
+ return errors.Wrap(err, "could not create proxyinit script for guest OS")
+ }
+
return nil
}
@@ -816,6 +894,26 @@ func pipeCmdPassThrough(name string, input string, arg ...string) error {
return cmd.Run()
}
+func setupWslProxyEnv() (hasProxy bool) {
+ current, _ := os.LookupEnv("WSLENV")
+ for _, key := range config.ProxyEnv {
+ if value, _ := os.LookupEnv(key); len(value) < 1 {
+ continue
+ }
+
+ hasProxy = true
+ delim := ""
+ if len(current) > 0 {
+ delim = ":"
+ }
+ current = fmt.Sprintf("%s%s%s/u", current, delim, key)
+ }
+ if hasProxy {
+ os.Setenv("WSLENV", current)
+ }
+ return
+}
+
func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
// If one setting fails to be applied, the others settings will not fail and still be applied.
// The setting(s) that failed to be applied will have its errors returned in setErrors
@@ -852,6 +950,10 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
dist := toDist(name)
+ useProxy := setupWslProxyEnv()
+ if err := configureProxy(dist, useProxy); err != nil {
+ return err
+ }
err := runCmdPassThrough("wsl", "-d", dist, "/root/bootstrap")
if err != nil {