summaryrefslogtreecommitdiff
path: root/pkg/machine
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/machine')
-rw-r--r--pkg/machine/config.go50
-rw-r--r--pkg/machine/config_test.go3
-rw-r--r--pkg/machine/connection.go4
-rw-r--r--pkg/machine/e2e/config.go21
-rw-r--r--pkg/machine/e2e/config_info.go20
-rw-r--r--pkg/machine/e2e/info_test.go58
-rw-r--r--pkg/machine/e2e/init_test.go19
-rw-r--r--pkg/machine/e2e/inspect_test.go22
-rw-r--r--pkg/machine/e2e/list_test.go34
-rw-r--r--pkg/machine/e2e/machine_test.go9
-rw-r--r--pkg/machine/e2e/set_test.go13
-rw-r--r--pkg/machine/e2e/ssh_test.go7
-rw-r--r--pkg/machine/fcos.go12
-rw-r--r--pkg/machine/fedora.go6
-rw-r--r--pkg/machine/ignition.go4
-rw-r--r--pkg/machine/keys.go30
-rw-r--r--pkg/machine/qemu/config.go4
-rw-r--r--pkg/machine/qemu/config_test.go3
-rw-r--r--pkg/machine/qemu/machine.go259
-rw-r--r--pkg/machine/qemu/machine_test.go3
-rw-r--r--pkg/machine/qemu/options_darwin_arm64.go23
-rw-r--r--pkg/machine/wsl/machine.go203
-rw-r--r--pkg/machine/wsl/util_windows.go30
23 files changed, 609 insertions, 228 deletions
diff --git a/pkg/machine/config.go b/pkg/machine/config.go
index d34776714..29cd7bc00 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -4,7 +4,7 @@
package machine
import (
- errors2 "errors"
+ "errors"
"io/ioutil"
"net"
"net/url"
@@ -13,7 +13,6 @@ import (
"time"
"github.com/containers/storage/pkg/homedir"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -42,7 +41,9 @@ const (
// Running indicates the qemu vm is running.
Running Status = "running"
// Stopped indicates the vm has stopped.
- Stopped Status = "stopped"
+ Stopped Status = "stopped"
+ // Starting indicated the vm is in the process of starting
+ Starting Status = "starting"
DefaultMachineName string = "podman-machine-default"
)
@@ -53,6 +54,7 @@ type Provider interface {
IsValidVMName(name string) (bool, error)
CheckExclusiveActiveVM() (bool, string, error)
RemoveAndCleanMachines() error
+ VMType() string
}
type RemoteConnectionType string
@@ -62,7 +64,7 @@ var (
DefaultIgnitionUserName = "core"
ErrNoSuchVM = errors.New("VM does not exist")
ErrVMAlreadyExists = errors.New("VM already exists")
- ErrVMAlreadyRunning = errors.New("VM already running")
+ ErrVMAlreadyRunning = errors.New("VM already running or starting")
ErrMultipleActiveVM = errors.New("only one VM can be active at a time")
ForwarderBinaryName = "gvproxy"
)
@@ -88,6 +90,7 @@ type ListResponse struct {
CreatedAt time.Time
LastUp time.Time
Running bool
+ Starting bool
Stream string
VMType string
CPUs uint64
@@ -138,14 +141,15 @@ type DistributionDownload interface {
Get() *Download
}
type InspectInfo struct {
- ConfigPath VMFile
- Created time.Time
- Image ImageConfig
- LastUp time.Time
- Name string
- Resources ResourceConfig
- SSHConfig SSHConfig
- State Status
+ ConfigPath VMFile
+ ConnectionInfo ConnectionConfig
+ Created time.Time
+ Image ImageConfig
+ LastUp time.Time
+ Name string
+ Resources ResourceConfig
+ SSHConfig SSHConfig
+ State Status
}
func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url.URL {
@@ -251,11 +255,11 @@ func (m *VMFile) GetPath() string {
// the actual path
func (m *VMFile) Delete() error {
if m.Symlink != nil {
- if err := os.Remove(*m.Symlink); err != nil && !errors2.Is(err, os.ErrNotExist) {
+ if err := os.Remove(*m.Symlink); err != nil && !errors.Is(err, os.ErrNotExist) {
logrus.Errorf("unable to remove symlink %q", *m.Symlink)
}
}
- if err := os.Remove(m.Path); err != nil && !errors2.Is(err, os.ErrNotExist) {
+ if err := os.Remove(m.Path); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
return nil
@@ -269,14 +273,14 @@ func (m *VMFile) Read() ([]byte, error) {
// NewMachineFile is a constructor for VMFile
func NewMachineFile(path string, symlink *string) (*VMFile, error) {
if len(path) < 1 {
- return nil, errors2.New("invalid machine file path")
+ return nil, errors.New("invalid machine file path")
}
if symlink != nil && len(*symlink) < 1 {
- return nil, errors2.New("invalid symlink path")
+ return nil, errors.New("invalid symlink path")
}
mf := VMFile{Path: path}
if symlink != nil && len(path) > maxSocketPathLength {
- if err := mf.makeSymlink(symlink); err != nil && !errors2.Is(err, os.ErrExist) {
+ if err := mf.makeSymlink(symlink); err != nil && !errors.Is(err, os.ErrExist) {
return nil, err
}
}
@@ -286,13 +290,13 @@ func NewMachineFile(path string, symlink *string) (*VMFile, error) {
// makeSymlink for macOS creates a symlink in $HOME/.podman/
// for a machinefile like a socket
func (m *VMFile) makeSymlink(symlink *string) error {
- homedir, err := os.UserHomeDir()
+ homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
- sl := filepath.Join(homedir, ".podman", *symlink)
+ sl := filepath.Join(homeDir, ".podman", *symlink)
// make the symlink dir and throw away if it already exists
- if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors2.Is(err, os.ErrNotExist) {
+ if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
m.Symlink = &sl
@@ -335,3 +339,9 @@ type SSHConfig struct {
// RemoteUsername of the vm user
RemoteUsername string
}
+
+// ConnectionConfig contains connections like sockets, etc.
+type ConnectionConfig struct {
+ // PodmanSocket is the exported podman service socket
+ PodmanSocket *VMFile `json:"PodmanSocket"`
+}
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/connection.go b/pkg/machine/connection.go
index 841b2afa6..6ff761a92 100644
--- a/pkg/machine/connection.go
+++ b/pkg/machine/connection.go
@@ -4,10 +4,10 @@
package machine
import (
+ "errors"
"fmt"
"github.com/containers/common/pkg/config"
- "github.com/pkg/errors"
)
func AddConnection(uri fmt.Stringer, name, identity string, isDefault bool) error {
@@ -72,7 +72,7 @@ func RemoveConnection(name string) error {
if _, ok := cfg.Engine.ServiceDestinations[name]; ok {
delete(cfg.Engine.ServiceDestinations, name)
} else {
- return errors.Errorf("unable to find connection named %q", name)
+ return fmt.Errorf("unable to find connection named %q", name)
}
return cfg.Write()
}
diff --git a/pkg/machine/e2e/config.go b/pkg/machine/e2e/config.go
index c17b840d3..b3fe74b0c 100644
--- a/pkg/machine/e2e/config.go
+++ b/pkg/machine/e2e/config.go
@@ -3,7 +3,6 @@ package e2e
import (
"encoding/json"
"fmt"
- "math/rand"
"os"
"os/exec"
"path/filepath"
@@ -13,6 +12,7 @@ import (
"github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/qemu"
"github.com/containers/podman/v4/pkg/util"
+ "github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo" //nolint:golint,stylecheck
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -85,6 +85,14 @@ func (ms *machineSession) outputToString() string {
return strings.Join(fields, " ")
}
+// errorToString returns the error output from a session in string form
+func (ms *machineSession) errorToString() string {
+ if ms == nil || ms.Err == nil || ms.Err.Contents() == nil {
+ return ""
+ }
+ return string(ms.Err.Contents())
+}
+
// newMB constructor for machine test builders
func newMB() (*machineTestBuilder, error) {
mb := machineTestBuilder{
@@ -128,14 +136,14 @@ func (m *machineTestBuilder) setTimeout(timeout time.Duration) *machineTestBuild
// toQemuInspectInfo is only for inspecting qemu machines. Other providers will need
// to make their own.
-func (mb *machineTestBuilder) toQemuInspectInfo() ([]qemuMachineInspectInfo, int, error) {
+func (mb *machineTestBuilder) toQemuInspectInfo() ([]machine.InspectInfo, int, error) {
args := []string{"machine", "inspect"}
args = append(args, mb.names...)
session, err := runWrapper(mb.podmanBinary, args, defaultTimeout, true)
if err != nil {
return nil, -1, err
}
- mii := []qemuMachineInspectInfo{}
+ mii := []machine.InspectInfo{}
err = json.Unmarshal(session.Bytes(), &mii)
return mii, session.ExitCode(), err
}
@@ -171,10 +179,5 @@ func (m *machineTestBuilder) init() {}
// randomString returns a string of given length composed of random characters
func randomString(n int) string {
- var randomLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
- b := make([]rune, n)
- for i := range b {
- b[i] = randomLetters[rand.Intn(len(randomLetters))]
- }
- return string(b)
+ return stringid.GenerateRandomID()[0:12]
}
diff --git a/pkg/machine/e2e/config_info.go b/pkg/machine/e2e/config_info.go
new file mode 100644
index 000000000..410c7e518
--- /dev/null
+++ b/pkg/machine/e2e/config_info.go
@@ -0,0 +1,20 @@
+package e2e
+
+type infoMachine struct {
+ format string
+ cmd []string
+}
+
+func (i *infoMachine) buildCmd(m *machineTestBuilder) []string {
+ cmd := []string{"machine", "info"}
+ if len(i.format) > 0 {
+ cmd = append(cmd, "--format", i.format)
+ }
+ i.cmd = cmd
+ return cmd
+}
+
+func (i *infoMachine) withFormat(format string) *infoMachine {
+ i.format = format
+ return i
+}
diff --git a/pkg/machine/e2e/info_test.go b/pkg/machine/e2e/info_test.go
new file mode 100644
index 000000000..eeabb78af
--- /dev/null
+++ b/pkg/machine/e2e/info_test.go
@@ -0,0 +1,58 @@
+package e2e
+
+import (
+ "github.com/containers/podman/v4/cmd/podman/machine"
+ jsoniter "github.com/json-iterator/go"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("podman machine info", func() {
+ var (
+ mb *machineTestBuilder
+ testDir string
+ )
+
+ BeforeEach(func() {
+ testDir, mb = setup()
+ })
+ AfterEach(func() {
+ teardown(originalHomeDir, testDir, mb)
+ })
+
+ It("machine info", func() {
+ info := new(infoMachine)
+ infoSession, err := mb.setCmd(info).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+
+ // Verify go template works and check for no running machines
+ info = new(infoMachine)
+ infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+ Expect(infoSession.outputToString()).To(Equal("0"))
+
+ // Create a machine and check if info has been updated
+ i := new(initMachine)
+ initSession, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
+ Expect(err).To(BeNil())
+ Expect(initSession).To(Exit(0))
+
+ info = new(infoMachine)
+ infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+ Expect(infoSession.outputToString()).To(Equal("1"))
+
+ // Check if json is in correct format
+ infoSession, err = mb.setCmd(info.withFormat("json")).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+
+ infoReport := &machine.Info{}
+ err = jsoniter.Unmarshal(infoSession.Bytes(), infoReport)
+ Expect(err).To(BeNil())
+ })
+})
diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go
index 6949eb0af..40f140cae 100644
--- a/pkg/machine/e2e/init_test.go
+++ b/pkg/machine/e2e/init_test.go
@@ -3,6 +3,7 @@ package e2e
import (
"io/ioutil"
"os"
+ "runtime"
"time"
"github.com/containers/podman/v4/pkg/machine"
@@ -44,9 +45,9 @@ var _ = Describe("podman machine init", func() {
Expect(len(inspectBefore)).To(BeNumerically(">", 0))
testMachine := inspectBefore[0]
- Expect(testMachine.VM.Name).To(Equal(mb.names[0]))
- Expect(testMachine.VM.CPUs).To(Equal(uint64(1)))
- Expect(testMachine.VM.Memory).To(Equal(uint64(2048)))
+ Expect(testMachine.Name).To(Equal(mb.names[0]))
+ Expect(testMachine.Resources.CPUs).To(Equal(uint64(1)))
+ Expect(testMachine.Resources.Memory).To(Equal(uint64(2048)))
})
@@ -61,7 +62,7 @@ var _ = Describe("podman machine init", func() {
Expect(len(inspectBefore)).To(BeNumerically(">", 0))
Expect(err).To(BeNil())
Expect(len(inspectBefore)).To(BeNumerically(">", 0))
- Expect(inspectBefore[0].VM.Name).To(Equal(mb.names[0]))
+ Expect(inspectBefore[0].Name).To(Equal(mb.names[0]))
s := startMachine{}
ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run()
@@ -104,7 +105,15 @@ var _ = Describe("podman machine init", func() {
memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run()
Expect(err).To(BeNil())
Expect(memorySession).To(Exit(0))
- Expect(memorySession.outputToString()).To(ContainSubstring("3824"))
+ switch runtime.GOOS {
+ // os's handle memory differently
+ case "linux":
+ Expect(memorySession.outputToString()).To(ContainSubstring("3821"))
+ case "darwin":
+ Expect(memorySession.outputToString()).To(ContainSubstring("3824"))
+ default:
+ // add windows when testing on that platform
+ }
sshTimezone := sshMachine{}
timezoneSession, err := mb.setName(name).setCmd(sshTimezone.withSSHComand([]string{"date"})).run()
diff --git a/pkg/machine/e2e/inspect_test.go b/pkg/machine/e2e/inspect_test.go
index 2c9de5664..93fb8cc2b 100644
--- a/pkg/machine/e2e/inspect_test.go
+++ b/pkg/machine/e2e/inspect_test.go
@@ -1,10 +1,9 @@
package e2e
import (
- "encoding/json"
+ "strings"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/containers/podman/v4/pkg/machine/qemu"
jsoniter "github.com/json-iterator/go"
. "github.com/onsi/ginkgo"
@@ -50,24 +49,6 @@ var _ = Describe("podman machine stop", func() {
Expect(err).To(BeNil())
Expect(inspectSession).To(Exit(0))
Expect(inspectSession.Bytes()).To(ContainSubstring("foo1"))
-
- type fakeInfos struct {
- Status string
- VM qemu.MachineVM
- }
- infos := make([]fakeInfos, 0, 2)
- err = json.Unmarshal(inspectSession.Bytes(), &infos)
- Expect(err).ToNot(HaveOccurred())
- Expect(len(infos)).To(Equal(2))
-
- // rm := new(rmMachine)
- // // Must manually clean up due to multiple names
- // for _, name := range []string{"foo1", "foo2"} {
- // mb.setName(name).setCmd(rm.withForce()).run()
- // mb.names = []string{}
- // }
- // mb.names = []string{}
-
})
It("inspect with go format", func() {
@@ -86,6 +67,7 @@ var _ = Describe("podman machine stop", func() {
var inspectInfo []machine.InspectInfo
err = jsoniter.Unmarshal(inspectSession.Bytes(), &inspectInfo)
Expect(err).To(BeNil())
+ Expect(strings.HasSuffix(inspectInfo[0].ConnectionInfo.PodmanSocket.GetPath(), "podman.sock"))
inspect := new(inspectMachine)
inspect = inspect.withFormat("{{.Name}}")
diff --git a/pkg/machine/e2e/list_test.go b/pkg/machine/e2e/list_test.go
index 0bc867047..fb855c61e 100644
--- a/pkg/machine/e2e/list_test.go
+++ b/pkg/machine/e2e/list_test.go
@@ -2,9 +2,10 @@ package e2e
import (
"strings"
+ "time"
- "github.com/containers/buildah/util"
- "github.com/containers/podman/v4/cmd/podman/machine"
+ "github.com/containers/common/pkg/util"
+ "github.com/containers/podman/v4/pkg/domain/entities"
jsoniter "github.com/json-iterator/go"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -29,7 +30,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 +40,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 +52,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 +71,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)
@@ -87,7 +88,7 @@ var _ = Describe("podman machine list", func() {
startSession, err := mb.setCmd(s).runWithoutWait()
Expect(err).To(BeNil())
l := new(listMachine)
- for { // needs to be infinite because we need to check if running when inspect returns to avoid race conditions.
+ for i := 0; i < 30; i++ {
listSession, err := mb.setCmd(l).run()
Expect(listSession).To(Exit(0))
Expect(err).To(BeNil())
@@ -96,6 +97,7 @@ var _ = Describe("podman machine list", func() {
} else {
break
}
+ time.Sleep(3 * time.Second)
}
Expect(startSession).To(Exit(0))
listSession, err := mb.setCmd(l).run()
@@ -116,10 +118,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)
@@ -128,13 +130,21 @@ var _ = Describe("podman machine list", func() {
// --format json
list2 := new(listMachine)
list2 = list2.withFormat("json")
- listSession2, err := mb.setName("foo1").setCmd(list2).run()
+ listSession2, err := mb.setCmd(list2).run()
Expect(err).To(BeNil())
Expect(listSession2).To(Exit(0))
- var listResponse []*machine.ListReporter
+ var listResponse []*entities.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))
})
})
diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go
index 657014b05..7b063937d 100644
--- a/pkg/machine/e2e/machine_test.go
+++ b/pkg/machine/e2e/machine_test.go
@@ -22,7 +22,7 @@ func TestMain(m *testing.M) {
}
const (
- defaultStream string = "podman-testing"
+ defaultStream string = "testing"
)
var (
@@ -97,6 +97,9 @@ func setup() (string, *machineTestBuilder) {
if err := os.Setenv("HOME", homeDir); err != nil {
Fail("failed to set home dir")
}
+ if err := os.Setenv("XDG_RUNTIME_DIR", homeDir); err != nil {
+ Fail("failed to set xdg_runtime dir")
+ }
if err := os.Unsetenv("SSH_AUTH_SOCK"); err != nil {
Fail("unable to unset SSH_AUTH_SOCK")
}
@@ -120,9 +123,9 @@ func setup() (string, *machineTestBuilder) {
}
func teardown(origHomeDir string, testDir string, mb *machineTestBuilder) {
- s := new(stopMachine)
+ r := new(rmMachine)
for _, name := range mb.names {
- if _, err := mb.setName(name).setCmd(s).run(); err != nil {
+ if _, err := mb.setName(name).setCmd(r.withForce()).run(); err != nil {
fmt.Printf("error occurred rm'ing machine: %q\n", err)
}
}
diff --git a/pkg/machine/e2e/set_test.go b/pkg/machine/e2e/set_test.go
index 15215a44d..80cb89488 100644
--- a/pkg/machine/e2e/set_test.go
+++ b/pkg/machine/e2e/set_test.go
@@ -1,6 +1,8 @@
package e2e
import (
+ "runtime"
+
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
@@ -57,8 +59,15 @@ var _ = Describe("podman machine set", func() {
memorySession, err := mb.setName(name).setCmd(sshMemory.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run()
Expect(err).To(BeNil())
Expect(memorySession).To(Exit(0))
- Expect(memorySession.outputToString()).To(ContainSubstring("3824"))
-
+ switch runtime.GOOS {
+ // it seems macos and linux handle memory differently
+ case "linux":
+ Expect(memorySession.outputToString()).To(ContainSubstring("3821"))
+ case "darwin":
+ Expect(memorySession.outputToString()).To(ContainSubstring("3824"))
+ default:
+ // windows can go here if we ever run tests there
+ }
// Setting a running machine results in 125
runner, err := mb.setName(name).setCmd(set.withCPUs(4)).run()
Expect(err).To(BeNil())
diff --git a/pkg/machine/e2e/ssh_test.go b/pkg/machine/e2e/ssh_test.go
index 155d39a64..9ee31ac26 100644
--- a/pkg/machine/e2e/ssh_test.go
+++ b/pkg/machine/e2e/ssh_test.go
@@ -56,5 +56,12 @@ var _ = Describe("podman machine ssh", func() {
Expect(err).To(BeNil())
Expect(sshSession).To(Exit(0))
Expect(sshSession.outputToString()).To(ContainSubstring("Fedora CoreOS"))
+
+ // keep exit code
+ sshSession, err = mb.setName(name).setCmd(ssh.withSSHComand([]string{"false"})).run()
+ Expect(err).To(BeNil())
+ Expect(sshSession).To(Exit(1))
+ Expect(sshSession.outputToString()).To(Equal(""))
+ Expect(sshSession.errorToString()).To(Equal(""))
})
})
diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go
index df58b8a1e..4ccb99e96 100644
--- a/pkg/machine/fcos.go
+++ b/pkg/machine/fcos.go
@@ -17,7 +17,6 @@ import (
"github.com/coreos/stream-metadata-go/fedoracoreos"
"github.com/coreos/stream-metadata-go/release"
"github.com/coreos/stream-metadata-go/stream"
- "github.com/pkg/errors"
digest "github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
@@ -139,20 +138,13 @@ func getStreamURL(streamType string) url2.URL {
// This should get Exported and stay put as it will apply to all fcos downloads
// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version
-func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { //nolint:staticcheck
+func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) {
var (
fcosstable stream.Stream
altMeta release.Release
streamType string
)
- // This is being hard set to testing. Once podman4 is in the
- // fcos trees, we should remove it and re-release at least on
- // macs.
- // TODO: remove when podman4.0 is in coreos
-
- imageStream = "podman-testing" //nolint:staticcheck
-
switch imageStream {
case "podman-testing":
streamType = "podman-testing"
@@ -163,7 +155,7 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { //nolint:s
case "stable":
streamType = fedoracoreos.StreamStable
default:
- return nil, errors.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream)
+ return nil, fmt.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream)
}
streamurl := getStreamURL(streamType)
resp, err := http.Get(streamurl.String())
diff --git a/pkg/machine/fedora.go b/pkg/machine/fedora.go
index 14a173da6..46e450418 100644
--- a/pkg/machine/fedora.go
+++ b/pkg/machine/fedora.go
@@ -4,6 +4,7 @@
package machine
import (
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -13,7 +14,6 @@ import (
"path/filepath"
"regexp"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -107,12 +107,12 @@ func getFedoraDownload(releaseStream string) (string, *url.URL, int64, error) {
newLocation := rawURL + file
downloadURL, err := url.Parse(newLocation)
if err != nil {
- return "", nil, -1, errors.Wrapf(err, "invalid URL generated from discovered Fedora file: %s", newLocation)
+ return "", nil, -1, fmt.Errorf("invalid URL generated from discovered Fedora file: %s: %w", newLocation, err)
}
resp, err := http.Head(newLocation)
if err != nil {
- return "", nil, -1, errors.Wrapf(err, "head request failed: %s", newLocation)
+ return "", nil, -1, fmt.Errorf("head request failed: %s: %w", newLocation, err)
}
_ = resp.Body.Close()
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 15c1f73d8..0c7d7114e 100644
--- a/pkg/machine/keys.go
+++ b/pkg/machine/keys.go
@@ -6,6 +6,7 @@ package machine
import (
"errors"
"fmt"
+ "io"
"io/ioutil"
"os"
"os/exec"
@@ -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
@@ -59,7 +76,16 @@ func generatekeysPrefix(dir string, file string, passThru bool, prefix ...string
args := append([]string{}, prefix[1:]...)
args = append(args, sshCommand...)
args = append(args, file)
- cmd := exec.Command(prefix[0], args...)
+
+ binary, err := exec.LookPath(prefix[0])
+ if err != nil {
+ return err
+ }
+ binary, err = filepath.Abs(binary)
+ if err != nil {
+ return err
+ }
+ cmd := exec.Command(binary, args...)
cmd.Dir = dir
if passThru {
cmd.Stdin = os.Stdin
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 6e36b0886..6134e69e1 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -5,9 +5,11 @@ package qemu
import (
"bufio"
+ "bytes"
"context"
"encoding/base64"
"encoding/json"
+ "errors"
"fmt"
"io/fs"
"io/ioutil"
@@ -16,9 +18,11 @@ import (
"net/url"
"os"
"os/exec"
+ "os/signal"
"path/filepath"
"strconv"
"strings"
+ "syscall"
"time"
"github.com/containers/common/pkg/config"
@@ -28,8 +32,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 (
@@ -105,6 +109,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))}...)
@@ -132,12 +139,12 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
cmd = append(cmd, []string{
"-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"}...)
+ // Note both id and chardev start with an extra "a" because qemu requires that it
+ // starts with an letter but users can also use numbers
+ "-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=a" + vm.Name + "_ready",
+ "-device", "virtserialport,chardev=a" + 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
}
@@ -207,7 +214,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
}
@@ -314,6 +321,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
source := paths[0]
target := source
readonly := false
+ securityModel := "mapped-xattr"
if len(paths) > 1 {
target = paths[1]
}
@@ -321,18 +329,20 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
options := paths[2]
volopts := strings.Split(options, ",")
for _, o := range volopts {
- switch o {
- case "rw":
+ switch {
+ case o == "rw":
readonly = false
- case "ro":
+ case o == "ro":
readonly = true
+ case strings.HasPrefix(o, "security_model="):
+ securityModel = strings.Split(o, "=")[1]
default:
fmt.Printf("Unknown option: %s\n", o)
}
}
}
if volumeType == VolumeTypeVirtfs {
- virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=mapped-xattr", source, tag)
+ virtfsOptions := fmt.Sprintf("local,path=%s,mount_tag=%s,security_model=%s", source, tag, securityModel)
if readonly {
virtfsOptions += ",readonly"
}
@@ -430,12 +440,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
}
@@ -453,7 +463,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
}
@@ -484,19 +494,33 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
if err := v.writeConfig(); err != nil {
return fmt.Errorf("writing JSON file: %w", err)
}
- defer func() {
+ doneStarting := func() {
v.Starting = false
if err := v.writeConfig(); err != nil {
logrus.Errorf("Writing JSON file: %v", err)
}
+ }
+ defer doneStarting()
+
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+ go func() {
+ _, ok := <-c
+ if !ok {
+ return
+ }
+ doneStarting()
+ os.Exit(1)
}()
+ defer close(c)
+
if v.isIncompatible() {
logrus.Errorf("machine %q is incompatible with this release of podman and needs to be recreated, starting for recovery only", v.Name)
}
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()
@@ -550,34 +574,46 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
files := []*os.File{dnr, dnw, dnw, fd}
attr.Files = files
logrus.Debug(v.CmdLine)
- cmd := v.CmdLine
+ cmdLine := v.CmdLine
// Disable graphic window when not in debug mode
// Done in start, so we're not suck with the debug level we used on init
if !logrus.IsLevelEnabled(logrus.DebugLevel) {
- cmd = append(cmd, "-display", "none")
+ cmdLine = append(cmdLine, "-display", "none")
}
- _, err = os.StartProcess(v.CmdLine[0], cmd, attr)
+ stderrBuf := &bytes.Buffer{}
+
+ cmd := &exec.Cmd{
+ Args: cmdLine,
+ Path: cmdLine[0],
+ Stdin: dnr,
+ Stdout: dnw,
+ Stderr: stderrBuf,
+ ExtraFiles: []*os.File{fd},
+ }
+ err = cmd.Start()
if err != nil {
// check if qemu was not found
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
}
- cmd[0], err = cfg.FindHelperBinary(QemuCommand, true)
+ cmdLine[0], err = cfg.FindHelperBinary(QemuCommand, true)
if err != nil {
return err
}
- _, err = os.StartProcess(cmd[0], cmd, attr)
+ cmd.Path = cmdLine[0]
+ err = cmd.Start()
if err != nil {
- return errors.Wrapf(err, "unable to execute %q", cmd)
+ return fmt.Errorf("unable to execute %q: %w", cmd, err)
}
}
+ defer cmd.Process.Release() //nolint:errcheck
fmt.Println("Waiting for VM ...")
socketPath, err := getRuntimeDir()
if err != nil {
@@ -592,6 +628,16 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
if err == nil {
break
}
+ // check if qemu is still alive
+ var status syscall.WaitStatus
+ pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
+ if err != nil {
+ return fmt.Errorf("failed to read qemu process status: %w", err)
+ }
+ if pid > 0 {
+ // child exited
+ return fmt.Errorf("qemu exited unexpectedly with exit code %d, stderr: %s", status.ExitStatus(), stderrBuf.String())
+ }
time.Sleep(wait)
wait++
}
@@ -682,7 +728,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
@@ -737,17 +783,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
}
@@ -756,7 +802,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
@@ -770,24 +816,52 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
if err := qmpMonitor.Disconnect(); err != nil {
// FIXME: this error should probably be returned
- return nil // nolint: nilerr
+ 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
}
- return v.ReadySocket.Delete()
+ fmt.Println("Waiting for VM to exit...")
+ for isProcessAlive(vmPid) {
+ time.Sleep(500 * time.Millisecond)
+ }
+
+ return nil
}
// NewQMPMonitor creates the monitor subsection of our vm
@@ -831,8 +905,14 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func()
if err != nil {
return "", nil, err
}
- if state == machine.Running && !opts.Force {
- return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name)
+ if state == machine.Running {
+ if !opts.Force {
+ return "", nil, fmt.Errorf("running vm %q cannot be destroyed", v.Name)
+ }
+ err := v.Stop(v.Name, machine.StopOptions{})
+ if err != nil {
+ return "", nil, err
+ }
}
// Collect all the files that need to be destroyed
@@ -874,8 +954,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 {
@@ -904,11 +987,16 @@ func (v *MachineVM) State(bypass bool) (machine.Status, error) {
}
// Check if we can dial it
if v.Starting && !bypass {
- return "", nil
+ return machine.Starting, nil
}
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 {
@@ -941,7 +1029,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
@@ -952,7 +1040,8 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
sshDestination := username + "@localhost"
port := strconv.Itoa(v.Port)
- args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no"}
+ args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile=/dev/null",
+ "-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR", "-o", "SetEnv=LC_ALL="}
if len(opts.Args) > 0 {
args = append(args, opts.Args...)
} else {
@@ -1051,6 +1140,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()
@@ -1064,6 +1154,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
@@ -1074,9 +1165,6 @@ func getVMInfos() ([]*machine.ListResponse, error) {
return err
}
}
- if state == machine.Running {
- listEntry.Running = true
- }
listed = append(listed, listEntry)
}
@@ -1105,10 +1193,10 @@ 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 {
+ if vm.Running || vm.Starting {
return true, vm.Name, nil
}
}
@@ -1116,7 +1204,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 {
@@ -1157,7 +1245,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) {
@@ -1283,13 +1374,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
}
@@ -1420,7 +1517,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
}
@@ -1471,16 +1568,22 @@ func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
if err != nil {
return nil, err
}
-
+ connInfo := new(machine.ConnectionConfig)
+ podmanSocket, err := v.forwardSocketPath()
+ if err != nil {
+ return nil, err
+ }
+ connInfo.PodmanSocket = podmanSocket
return &machine.InspectInfo{
- ConfigPath: v.ConfigPath,
- Created: v.Created,
- Image: v.ImageConfig,
- LastUp: v.LastUp,
- Name: v.Name,
- Resources: v.ResourceConfig,
- SSHConfig: v.SSHConfig,
- State: state,
+ ConfigPath: v.ConfigPath,
+ ConnectionInfo: *connInfo,
+ Created: v.Created,
+ Image: v.ImageConfig,
+ LastUp: v.LastUp,
+ Name: v.Name,
+ Resources: v.ResourceConfig,
+ SSHConfig: v.SSHConfig,
+ State: state,
}, nil
}
@@ -1490,7 +1593,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
@@ -1506,7 +1609,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
@@ -1545,7 +1648,7 @@ func (v *MachineVM) editCmdLine(flag string, value string) {
}
}
-// RemoveAndCleanMachines removes all machine and cleans up any other files associatied with podman machine
+// RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine
func (p *Provider) RemoveAndCleanMachines() error {
var (
vm machine.VM
@@ -1620,3 +1723,15 @@ 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
+}
+
+func (p *Provider) VMType() string {
+ return vmtype
+}
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/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go
index 4c954af00..d75237938 100644
--- a/pkg/machine/qemu/options_darwin_arm64.go
+++ b/pkg/machine/qemu/options_darwin_arm64.go
@@ -4,6 +4,8 @@ import (
"os"
"os/exec"
"path/filepath"
+
+ "github.com/containers/common/pkg/config"
)
var (
@@ -15,8 +17,8 @@ func (v *MachineVM) addArchOptions() []string {
opts := []string{
"-accel", "hvf",
"-accel", "tcg",
- "-cpu", "cortex-a57",
- "-M", "virt,highmem=off",
+ "-cpu", "host",
+ "-M", "virt,highmem=on",
"-drive", "file=" + getEdk2CodeFd("edk2-aarch64-code.fd") + ",if=pflash,format=raw,readonly=on",
"-drive", "file=" + ovmfDir + ",if=pflash,format=raw"}
return opts
@@ -38,6 +40,22 @@ func getOvmfDir(imagePath, vmName string) string {
}
/*
+ * When QEmu is installed in a non-default location in the system
+ * we can use the qemu-system-* binary path to figure the install
+ * location for Qemu and use it to look for edk2-code-fd
+ */
+func getEdk2CodeFdPathFromQemuBinaryPath() string {
+ cfg, err := config.Default()
+ if err == nil {
+ execPath, err := cfg.FindHelperBinary(QemuCommand, true)
+ if err == nil {
+ return filepath.Clean(filepath.Join(filepath.Dir(execPath), "..", "share", "qemu"))
+ }
+ }
+ return ""
+}
+
+/*
* QEmu can be installed in multiple locations on MacOS, especially on
* Apple Silicon systems. A build from source will likely install it in
* /usr/local/bin, whereas Homebrew package management standard is to
@@ -45,6 +63,7 @@ func getOvmfDir(imagePath, vmName string) string {
*/
func getEdk2CodeFd(name string) string {
dirs := []string{
+ getEdk2CodeFdPathFromQemuBinaryPath(),
"/opt/homebrew/opt/podman/libexec/share/qemu",
"/usr/local/share/qemu",
"/opt/homebrew/share/qemu",
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 0b2874baf..450d8b877 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -6,6 +6,7 @@ package wsl
import (
"bufio"
"encoding/json"
+ "errors"
"fmt"
"io"
"io/fs"
@@ -18,10 +19,10 @@ 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"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
@@ -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:
@@ -239,7 +277,7 @@ func readAndMigrate(configPath string, name string) (*MachineVM, error) {
b, err := os.ReadFile(configPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
- return nil, errors.Wrap(machine.ErrNoSuchVM, name)
+ return nil, fmt.Errorf("%v: %w", name, machine.ErrNoSuchVM)
}
return vm, err
}
@@ -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)
@@ -368,7 +407,7 @@ func (v *MachineVM) writeConfig() error {
return err
}
if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil {
- return errors.Wrap(err, "could not write machine json config")
+ return fmt.Errorf("could not write machine json config: %w", err)
}
return nil
@@ -406,38 +445,38 @@ func provisionWSLDist(v *MachineVM) (string, error) {
distDir := filepath.Join(vmDataDir, "wsldist")
distTarget := filepath.Join(distDir, v.Name)
if err := os.MkdirAll(distDir, 0755); err != nil {
- return "", errors.Wrap(err, "could not create wsldist directory")
+ return "", fmt.Errorf("could not create wsldist directory: %w", err)
}
dist := toDist(v.Name)
fmt.Println("Importing operating system into WSL (this may take 5+ minutes on a new WSL install)...")
if err = runCmdPassThrough("wsl", "--import", dist, distTarget, v.ImagePath); err != nil {
- return "", errors.Wrap(err, "WSL import of guest OS failed")
+ return "", fmt.Errorf("the WSL import of guest OS failed: %w", err)
}
fmt.Println("Installing packages (this will take awhile)...")
if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "upgrade", "-y"); err != nil {
- return "", errors.Wrap(err, "package upgrade on guest OS failed")
+ return "", fmt.Errorf("package upgrade on guest OS failed: %w", err)
}
fmt.Println("Enabling Copr")
if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install", "-y", "'dnf-command(copr)'"); err != nil {
- return "", errors.Wrap(err, "enabling copr failed")
+ return "", fmt.Errorf("enabling copr failed: %w", err)
}
fmt.Println("Enabling podman4 repo")
if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "-y", "copr", "enable", "rhcontainerbot/podman4"); err != nil {
- return "", errors.Wrap(err, "enabling copr failed")
+ return "", fmt.Errorf("enabling copr failed: %w", err)
}
if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install",
"podman", "podman-docker", "openssh-server", "procps-ng", "-y"); err != nil {
- return "", errors.Wrap(err, "package installation on guest OS failed")
+ return "", fmt.Errorf("package installation on guest OS failed: %w", err)
}
// Fixes newuidmap
if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "reinstall", "shadow-utils", "-y"); err != nil {
- return "", errors.Wrap(err, "package reinstallation of shadow-utils on guest OS failed")
+ return "", fmt.Errorf("package reinstallation of shadow-utils on guest OS failed: %w", err)
}
// Windows 11 (NT Version = 10, Build 22000) generates harmless but scary messages on every
@@ -456,28 +495,28 @@ func createKeys(v *MachineVM, dist string, sshDir string) error {
user := v.RemoteUsername
if err := os.MkdirAll(sshDir, 0700); err != nil {
- return errors.Wrap(err, "could not create ssh directory")
+ return fmt.Errorf("could not create ssh directory: %w", err)
}
if err := runCmdPassThrough("wsl", "--terminate", dist); err != nil {
- return errors.Wrap(err, "could not cycle WSL dist")
+ return fmt.Errorf("could not cycle WSL dist: %w", err)
}
key, err := machine.CreateSSHKeysPrefix(sshDir, v.Name, true, true, "wsl", "-d", dist)
if err != nil {
- return errors.Wrap(err, "could not create ssh keys")
+ return fmt.Errorf("could not create ssh keys: %w", err)
}
if err := pipeCmdPassThrough("wsl", key+"\n", "-d", dist, "sh", "-c", "mkdir -p /root/.ssh;"+
"cat >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys"); err != nil {
- return errors.Wrap(err, "could not create root authorized keys on guest OS")
+ return fmt.Errorf("could not create root authorized keys on guest OS: %w", err)
}
userAuthCmd := withUser("mkdir -p /home/[USER]/.ssh;"+
"cat >> /home/[USER]/.ssh/authorized_keys; chown -R [USER]:[USER] /home/[USER]/.ssh;"+
"chmod 600 /home/[USER]/.ssh/authorized_keys", user)
if err := pipeCmdPassThrough("wsl", key+"\n", "-d", dist, "sh", "-c", userAuthCmd); err != nil {
- return errors.Wrapf(err, "could not create '%s' authorized keys on guest OS", v.RemoteUsername)
+ return fmt.Errorf("could not create '%s' authorized keys on guest OS: %w", v.RemoteUsername, err)
}
return nil
@@ -486,25 +525,25 @@ func createKeys(v *MachineVM, dist string, sshDir string) error {
func configureSystem(v *MachineVM, dist string) error {
user := v.RemoteUsername
if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", fmt.Sprintf(appendPort, v.Port, v.Port)); err != nil {
- return errors.Wrap(err, "could not configure SSH port for guest OS")
+ return fmt.Errorf("could not configure SSH port for guest OS: %w", err)
}
if err := pipeCmdPassThrough("wsl", withUser(configServices, user), "-d", dist, "sh"); err != nil {
- return errors.Wrap(err, "could not configure systemd settings for guest OS")
+ return fmt.Errorf("could not configure systemd settings for guest OS: %w", err)
}
if err := pipeCmdPassThrough("wsl", sudoers, "-d", dist, "sh", "-c", "cat >> /etc/sudoers"); err != nil {
- return errors.Wrap(err, "could not add wheel to sudoers")
+ return fmt.Errorf("could not add wheel to sudoers: %w", err)
}
if err := pipeCmdPassThrough("wsl", overrideSysusers, "-d", dist, "sh", "-c",
"cat > /etc/systemd/system/systemd-sysusers.service.d/override.conf"); err != nil {
- return errors.Wrap(err, "could not generate systemd-sysusers override for guest OS")
+ return fmt.Errorf("could not generate systemd-sysusers override for guest OS: %w", err)
}
lingerCmd := withUser("cat > /home/[USER]/.config/systemd/[USER]/linger-example.service", user)
if err := pipeCmdPassThrough("wsl", lingerService, "-d", dist, "sh", "-c", lingerCmd); err != nil {
- return errors.Wrap(err, "could not generate linger service for guest OS")
+ return fmt.Errorf("could not generate linger service for guest OS: %w", err)
}
if err := enableUserLinger(v, dist); err != nil {
@@ -512,15 +551,49 @@ func configureSystem(v *MachineVM, dist string) error {
}
if err := pipeCmdPassThrough("wsl", withUser(lingerSetup, user), "-d", dist, "sh"); err != nil {
- return errors.Wrap(err, "could not configure systemd settomgs for guest OS")
+ return fmt.Errorf("could not configure systemd settomgs for guest OS: %w", err)
}
if err := pipeCmdPassThrough("wsl", containersConf, "-d", dist, "sh", "-c", "cat > /etc/containers/containers.conf"); err != nil {
- return errors.Wrap(err, "could not create containers.conf for guest OS")
+ return fmt.Errorf("could not create containers.conf for guest OS: %w", err)
}
if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", "echo wsl > /etc/containers/podman-machine"); err != nil {
- return errors.Wrap(err, "could not create podman-machine file for guest OS")
+ return fmt.Errorf("could not create podman-machine file for guest OS: %w", err)
+ }
+
+ 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 fmt.Errorf("%v: %w", failMessage, err)
+ }
+
+ 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 fmt.Errorf("%v: %w", failMessage, err)
+ }
}
return nil
@@ -529,7 +602,7 @@ func configureSystem(v *MachineVM, dist string) error {
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 {
- return errors.Wrap(err, "could not enable linger for remote user on guest OS")
+ return fmt.Errorf("could not enable linger for remote user on guest OS: %w", err)
}
return nil
@@ -538,21 +611,26 @@ func enableUserLinger(v *MachineVM, dist string) error {
func installScripts(dist string) error {
if err := pipeCmdPassThrough("wsl", enterns, "-d", dist, "sh", "-c",
"cat > /usr/local/bin/enterns; chmod 755 /usr/local/bin/enterns"); err != nil {
- return errors.Wrap(err, "could not create enterns script for guest OS")
+ return fmt.Errorf("could not create enterns script for guest OS: %w", err)
}
if err := pipeCmdPassThrough("wsl", profile, "-d", dist, "sh", "-c",
"cat > /etc/profile.d/enterns.sh"); err != nil {
- return errors.Wrap(err, "could not create motd profile script for guest OS")
+ return fmt.Errorf("could not create motd profile script for guest OS: %w", err)
}
if err := pipeCmdPassThrough("wsl", wslmotd, "-d", dist, "sh", "-c", "cat > /etc/wslmotd"); err != nil {
- return errors.Wrap(err, "could not create a WSL MOTD for guest OS")
+ return fmt.Errorf("could not create a WSL MOTD for guest OS: %w", err)
}
if err := pipeCmdPassThrough("wsl", bootstrap, "-d", dist, "sh", "-c",
"cat > /root/bootstrap; chmod 755 /root/bootstrap"); err != nil {
- return errors.Wrap(err, "could not create bootstrap script for guest OS")
+ return fmt.Errorf("could not create bootstrap script for guest OS: %w", err)
+ }
+
+ if err := pipeCmdPassThrough("wsl", proxyConfigSetup, "-d", dist, "sh", "-c",
+ "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit"); err != nil {
+ return fmt.Errorf("could not create proxyinit script for guest OS: %w", err)
}
return nil
@@ -595,10 +673,10 @@ func checkAndInstallWSL(opts machine.InitOptions) (bool, error) {
func attemptFeatureInstall(opts machine.InitOptions, admin bool) error {
if !winVersionAtLeast(10, 0, 18362) {
- return errors.Errorf("Your version of Windows does not support WSL. Update to Windows 10 Build 19041 or later")
+ return errors.New("your version of Windows does not support WSL. Update to Windows 10 Build 19041 or later")
} else if !winVersionAtLeast(10, 0, 19041) {
fmt.Fprint(os.Stderr, wslOldVersion)
- return errors.Errorf("WSL can not be automatically installed")
+ return errors.New("the WSL can not be automatically installed")
}
message := "WSL is not installed on this system, installing it.\n\n"
@@ -612,7 +690,7 @@ func attemptFeatureInstall(opts machine.InitOptions, admin bool) error {
"If you prefer, you may abort now, and perform a manual installation using the \"wsl --install\" command."
if !opts.ReExec && MessageBox(message, "Podman Machine", false) != 1 {
- return errors.Errorf("WSL installation aborted")
+ return errors.New("the WSL installation aborted")
}
if !opts.ReExec && !admin {
@@ -648,12 +726,12 @@ func installWsl() error {
defer log.Close()
if err := runCmdPassThroughTee(log, "dism", "/online", "/enable-feature",
"/featurename:Microsoft-Windows-Subsystem-Linux", "/all", "/norestart"); isMsiError(err) {
- return errors.Wrap(err, "could not enable WSL Feature")
+ return fmt.Errorf("could not enable WSL Feature: %w", err)
}
if err = runCmdPassThroughTee(log, "dism", "/online", "/enable-feature",
"/featurename:VirtualMachinePlatform", "/all", "/norestart"); isMsiError(err) {
- return errors.Wrap(err, "could not enable Virtual Machine Feature")
+ return fmt.Errorf("could not enable Virtual Machine Feature: %w", err)
}
log.Close()
@@ -687,7 +765,7 @@ func installWslKernel() error {
}
if err != nil {
- return errors.Wrap(err, "could not install WSL Kernel")
+ return fmt.Errorf("could not install WSL Kernel: %w", err)
}
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
@@ -824,23 +922,23 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
if opts.Rootful != nil && v.Rootful != *opts.Rootful {
err := v.setRootful(*opts.Rootful)
if err != nil {
- setErrors = append(setErrors, errors.Wrapf(err, "error setting rootful option"))
+ setErrors = append(setErrors, fmt.Errorf("error setting rootful option: %w", err))
} else {
v.Rootful = *opts.Rootful
}
}
if opts.CPUs != nil {
- setErrors = append(setErrors, errors.Errorf("changing CPUs not suppored for WSL machines"))
+ setErrors = append(setErrors, errors.New("changing CPUs not supported for WSL machines"))
}
if opts.Memory != nil {
- setErrors = append(setErrors, errors.Errorf("changing memory not suppored for WSL machines"))
+ setErrors = append(setErrors, errors.New("changing memory not supported for WSL machines"))
}
if opts.DiskSize != nil {
- setErrors = append(setErrors, errors.Errorf("changing Disk Size not suppored for WSL machines"))
+ setErrors = append(setErrors, errors.New("changing Disk Size not supported for WSL machines"))
}
return setErrors, v.writeConfig()
@@ -848,14 +946,18 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) {
func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
if v.isRunning() {
- return errors.Errorf("%q is already running", name)
+ return fmt.Errorf("%q is already running", name)
}
dist := toDist(name)
+ useProxy := setupWslProxyEnv()
+ if err := configureProxy(dist, useProxy); err != nil {
+ return err
+ }
err := runCmdPassThrough("wsl", "-d", dist, "/root/bootstrap")
if err != nil {
- return errors.Wrap(err, "WSL bootstrap script failed")
+ return fmt.Errorf("the WSL bootstrap script failed: %w", err)
}
if !v.Rootful {
@@ -897,7 +999,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
func launchWinProxy(v *MachineVM) (bool, string, error) {
machinePipe := toDist(v.Name)
if !pipeAvailable(machinePipe) {
- return false, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe)
+ return false, "", fmt.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe)
}
globalName := false
@@ -945,7 +1047,7 @@ func launchWinProxy(v *MachineVM) (bool, string, error) {
return globalName, pipePrefix + waitPipe, waitPipeExists(waitPipe, 30, func() error {
active, exitCode := getProcessState(cmd.Process.Pid)
if !active {
- return errors.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode)
+ return fmt.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode)
}
return nil
@@ -1083,7 +1185,7 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
}
if !wsl || !sysd {
- return errors.Errorf("%q is not running", v.Name)
+ return fmt.Errorf("%q is not running", v.Name)
}
_, _, _ = v.updateTimeStamps(true)
@@ -1095,12 +1197,12 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
cmd := exec.Command("wsl", "-d", dist, "sh")
cmd.Stdin = strings.NewReader(waitTerm)
if err = cmd.Start(); err != nil {
- return errors.Wrap(err, "Error executing wait command")
+ return fmt.Errorf("executing wait command: %w", err)
}
exitCmd := exec.Command("wsl", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
if err = exitCmd.Run(); err != nil {
- return errors.Wrap(err, "Error stopping sysd")
+ return fmt.Errorf("stopping sysd: %w", err)
}
if err = cmd.Wait(); err != nil {
@@ -1181,7 +1283,7 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
var files []string
if v.isRunning() {
- return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name)
+ return "", nil, fmt.Errorf("running vm %q cannot be destroyed", v.Name)
}
// Collect all the files that need to be destroyed
@@ -1253,7 +1355,7 @@ func (v *MachineVM) isRunning() bool {
// Added ssh function to VM interface: pkg/machine/config/go : line 58
func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
if !v.isRunning() {
- return errors.Errorf("vm %q is not running.", v.Name)
+ return fmt.Errorf("vm %q is not running.", v.Name)
}
username := opts.Username
@@ -1312,6 +1414,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
listEntry.RemoteUsername = vm.RemoteUsername
listEntry.Port = vm.Port
listEntry.IdentityPath = vm.IdentityPath
+ listEntry.Starting = false
running := vm.isRunning()
listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running)
@@ -1477,7 +1580,7 @@ func (v *MachineVM) getResources() (resources machine.ResourceConfig) {
return
}
-// RemoveAndCleanMachines removes all machine and cleans up any other files associatied with podman machine
+// RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine
func (p *Provider) RemoveAndCleanMachines() error {
var (
vm machine.VM
@@ -1552,3 +1655,7 @@ func (p *Provider) RemoveAndCleanMachines() error {
}
return prevErr
}
+
+func (p *Provider) VMType() string {
+ return vmtype
+}
diff --git a/pkg/machine/wsl/util_windows.go b/pkg/machine/wsl/util_windows.go
index b5c28e015..43f54fdd4 100644
--- a/pkg/machine/wsl/util_windows.go
+++ b/pkg/machine/wsl/util_windows.go
@@ -2,6 +2,7 @@ package wsl
import (
"encoding/base64"
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -11,7 +12,6 @@ import (
"unicode/utf16"
"unsafe"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
@@ -157,9 +157,9 @@ func relaunchElevatedWait() error {
case syscall.WAIT_OBJECT_0:
break
case syscall.WAIT_FAILED:
- return errors.Wrap(err, "could not wait for process, failed")
+ return fmt.Errorf("could not wait for process, failed: %w", err)
default:
- return errors.Errorf("could not wait for process, unknown error")
+ return errors.New("could not wait for process, unknown error")
}
var code uint32
if err := syscall.GetExitCodeProcess(handle, &code); err != nil {
@@ -174,7 +174,7 @@ func relaunchElevatedWait() error {
func wrapMaybe(err error, message string) error {
if err != nil {
- return errors.Wrap(err, message)
+ return fmt.Errorf("%v: %w", message, err)
}
return errors.New(message)
@@ -182,10 +182,10 @@ func wrapMaybe(err error, message string) error {
func wrapMaybef(err error, format string, args ...interface{}) error {
if err != nil {
- return errors.Wrapf(err, format, args...)
+ return fmt.Errorf(format+": %w", append(args, err)...)
}
- return errors.Errorf(format, args...)
+ return fmt.Errorf(format, args...)
}
func reboot() error {
@@ -202,14 +202,14 @@ func reboot() error {
dataDir, err := homedir.GetDataHome()
if err != nil {
- return errors.Wrap(err, "could not determine data directory")
+ return fmt.Errorf("could not determine data directory: %w", err)
}
if err := os.MkdirAll(dataDir, 0755); err != nil {
- return errors.Wrap(err, "could not create data directory")
+ return fmt.Errorf("could not create data directory: %w", err)
}
commFile := filepath.Join(dataDir, "podman-relaunch.dat")
if err := ioutil.WriteFile(commFile, []byte(encoded), 0600); err != nil {
- return errors.Wrap(err, "could not serialize command state")
+ return fmt.Errorf("could not serialize command state: %w", err)
}
command := fmt.Sprintf(pShellLaunch, commFile)
@@ -244,7 +244,7 @@ func reboot() error {
procExit := user32.NewProc("ExitWindowsEx")
if ret, _, err := procExit.Call(EWX_REBOOT|EWX_RESTARTAPPS|EWX_FORCEIFHUNG,
SHTDN_REASON_MAJOR_APPLICATION|SHTDN_REASON_MINOR_INSTALLATION|SHTDN_REASON_FLAG_PLANNED); ret != 1 {
- return errors.Wrap(err, "reboot failed")
+ return fmt.Errorf("reboot failed: %w", err)
}
return nil
@@ -262,19 +262,19 @@ func obtainShutdownPrivilege() error {
var hToken uintptr
if ret, _, err := OpenProcessToken.Call(uintptr(proc), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, uintptr(unsafe.Pointer(&hToken))); ret != 1 {
- return errors.Wrap(err, "error opening process token")
+ return fmt.Errorf("error opening process token: %w", err)
}
var privs TokenPrivileges
if ret, _, err := LookupPrivilegeValue.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeShutdownName))), uintptr(unsafe.Pointer(&(privs.privileges[0].luid)))); ret != 1 {
- return errors.Wrap(err, "error looking up shutdown privilege")
+ return fmt.Errorf("error looking up shutdown privilege: %w", err)
}
privs.privilegeCount = 1
privs.privileges[0].attributes = SE_PRIVILEGE_ENABLED
if ret, _, err := AdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&privs)), 0, uintptr(0), 0); ret != 1 {
- return errors.Wrap(err, "error enabling shutdown privilege on token")
+ return fmt.Errorf("error enabling shutdown privilege on token: %w", err)
}
return nil
@@ -295,13 +295,13 @@ func getProcessState(pid int) (active bool, exitCode int) {
func addRunOnceRegistryEntry(command string) error {
k, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\RunOnce`, registry.WRITE)
if err != nil {
- return errors.Wrap(err, "could not open RunOnce registry entry")
+ return fmt.Errorf("could not open RunOnce registry entry: %w", err)
}
defer k.Close()
if err := k.SetExpandStringValue("podman-machine", command); err != nil {
- return errors.Wrap(err, "could not open RunOnce registry entry")
+ return fmt.Errorf("could not open RunOnce registry entry: %w", err)
}
return nil