summaryrefslogtreecommitdiff
path: root/cmd/podman/machine
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/machine')
-rw-r--r--cmd/podman/machine/info.go182
-rw-r--r--cmd/podman/machine/init.go28
-rw-r--r--cmd/podman/machine/inspect.go1
-rw-r--r--cmd/podman/machine/list.go52
-rw-r--r--cmd/podman/machine/machine.go20
-rw-r--r--cmd/podman/machine/machine_unix.go11
-rw-r--r--cmd/podman/machine/machine_windows.go9
-rw-r--r--cmd/podman/machine/rm.go1
-rw-r--r--cmd/podman/machine/set.go1
-rw-r--r--cmd/podman/machine/ssh.go17
-rw-r--r--cmd/podman/machine/start.go6
-rw-r--r--cmd/podman/machine/stop.go1
12 files changed, 263 insertions, 66 deletions
diff --git a/cmd/podman/machine/info.go b/cmd/podman/machine/info.go
new file mode 100644
index 000000000..9932027d8
--- /dev/null
+++ b/cmd/podman/machine/info.go
@@ -0,0 +1,182 @@
+//go:build amd64 || arm64
+// +build amd64 arm64
+
+package machine
+
+import (
+ "fmt"
+ "html/template"
+ "os"
+ "runtime"
+
+ "github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/report"
+ "github.com/containers/podman/v4/cmd/podman/common"
+ "github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/validate"
+ "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/podman/v4/pkg/machine"
+ "github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+)
+
+var infoDescription = `Display information pertaining to the machine host.`
+
+var (
+ infoCmd = &cobra.Command{
+ Use: "info [options]",
+ Short: "Display machine host info",
+ Long: infoDescription,
+ PersistentPreRunE: rootlessOnly,
+ RunE: info,
+ Args: validate.NoArgs,
+ ValidArgsFunction: completion.AutocompleteNone,
+ Example: `podman machine info`,
+ }
+)
+
+var (
+ inFormat string
+)
+
+// Info contains info on the machine host and version info
+type Info struct {
+ Host *HostInfo `json:"Host"`
+ Version define.Version `json:"Version"`
+}
+
+// HostInfo contains info on the machine host
+type HostInfo struct {
+ Arch string `json:"Arch"`
+ CurrentMachine string `json:"CurrentMachine"`
+ DefaultMachine string `json:"DefaultMachine"`
+ EventsDir string `json:"EventsDir"`
+ MachineConfigDir string `json:"MachineConfigDir"`
+ MachineImageDir string `json:"MachineImageDir"`
+ MachineState string `json:"MachineState"`
+ NumberOfMachines int `json:"NumberOfMachines"`
+ OS string `json:"OS"`
+ VMType string `json:"VMType"`
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: infoCmd,
+ Parent: machineCmd,
+ })
+
+ flags := infoCmd.Flags()
+ formatFlagName := "format"
+ flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template")
+ _ = infoCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&define.Info{}))
+}
+
+func info(cmd *cobra.Command, args []string) error {
+ info := Info{}
+ version, err := define.GetVersion()
+ if err != nil {
+ return fmt.Errorf("error getting version info %w", err)
+ }
+ info.Version = version
+
+ host, err := hostInfo()
+ if err != nil {
+ return err
+ }
+ info.Host = host
+
+ switch {
+ case report.IsJSON(inFormat):
+ b, err := json.MarshalIndent(info, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ case cmd.Flags().Changed("format"):
+ tmpl := template.New(cmd.Name()).Funcs(template.FuncMap(report.DefaultFuncs))
+ inFormat = report.NormalizeFormat(inFormat)
+ tmpl, err := tmpl.Parse(inFormat)
+ if err != nil {
+ return err
+ }
+ return tmpl.Execute(os.Stdout, info)
+ default:
+ b, err := yaml.Marshal(info)
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ }
+
+ return nil
+}
+
+func hostInfo() (*HostInfo, error) {
+ host := HostInfo{}
+
+ host.Arch = runtime.GOARCH
+ host.OS = runtime.GOOS
+
+ provider := GetSystemDefaultProvider()
+ var listOpts machine.ListOptions
+ listResponse, err := provider.List(listOpts)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machines %w", err)
+ }
+
+ host.NumberOfMachines = len(listResponse)
+
+ cfg, err := config.ReadCustomConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ // Default state of machine is stopped
+ host.MachineState = "Stopped"
+ for _, vm := range listResponse {
+ // Set default machine if found
+ if vm.Name == cfg.Engine.ActiveService {
+ host.DefaultMachine = vm.Name
+ }
+ // If machine is running or starting, it is automatically the current machine
+ if vm.Running {
+ host.CurrentMachine = vm.Name
+ host.MachineState = "Running"
+ } else if vm.Starting {
+ host.CurrentMachine = vm.Name
+ host.MachineState = "Starting"
+ }
+ }
+ // If no machines are starting or running, set current machine to default machine
+ // If no default machines are found, do not report a default machine or a state
+ if host.CurrentMachine == "" {
+ if host.DefaultMachine == "" {
+ host.MachineState = ""
+ } else {
+ host.CurrentMachine = host.DefaultMachine
+ }
+ }
+
+ host.VMType = provider.VMType()
+
+ dataDir, err := machine.GetDataDir(host.VMType)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machine image dir")
+ }
+ host.MachineImageDir = dataDir
+
+ confDir, err := machine.GetConfDir(host.VMType)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machine config dir %w", err)
+ }
+ host.MachineConfigDir = confDir
+
+ eventsDir, err := eventSockDir()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get events dir: %w", err)
+ }
+ host.EventsDir = eventsDir
+
+ return &host, nil
+}
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index 6c31f3531..def3334e8 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -20,21 +19,20 @@ var (
Use: "init [options] [NAME]",
Short: "Initialize a virtual machine",
Long: "initialize a virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: initMachine,
Args: cobra.MaximumNArgs(1),
Example: `podman machine init myvm`,
ValidArgsFunction: completion.AutocompleteNone,
}
-)
-var (
initOpts = machine.InitOptions{}
defaultMachineName = machine.DefaultMachineName
now bool
)
// maxMachineNameSize is set to thirty to limit huge machine names primarily
-// because macos has a much smaller file size limit.
+// because macOS has a much smaller file size limit.
const maxMachineNameSize = 30
func init() {
@@ -111,7 +109,6 @@ func init() {
flags.BoolVar(&initOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution")
}
-// TODO should we allow for a users to append to the qemu cmdline?
func initMachine(cmd *cobra.Command, args []string) error {
var (
err error
@@ -122,12 +119,12 @@ func initMachine(cmd *cobra.Command, args []string) error {
initOpts.Name = defaultMachineName
if len(args) > 0 {
if len(args[0]) > maxMachineNameSize {
- return errors.New("machine name must be 30 characters or less")
+ return fmt.Errorf("machine name %q must be %d characters or less", args[0], maxMachineNameSize)
}
initOpts.Name = args[0]
}
if _, err := provider.LoadVMByName(initOpts.Name); err == nil {
- return errors.Wrap(machine.ErrVMAlreadyExists, initOpts.Name)
+ return fmt.Errorf("%s: %w", initOpts.Name, machine.ErrVMAlreadyExists)
}
for idx, vol := range initOpts.Volumes {
initOpts.Volumes[idx] = os.ExpandEnv(vol)
@@ -150,17 +147,12 @@ func initMachine(cmd *cobra.Command, args []string) error {
fmt.Println("Machine init complete")
if now {
- err = vm.Start(initOpts.Name, machine.StartOptions{})
- if err == nil {
- fmt.Printf("Machine %q started successfully\n", initOpts.Name)
- newMachineEvent(events.Start, events.Event{Name: initOpts.Name})
- }
- } else {
- extra := ""
- if initOpts.Name != defaultMachineName {
- extra = " " + initOpts.Name
- }
- fmt.Printf("To start your machine run:\n\n\tpodman machine start%s\n\n", extra)
+ return start(cmd, args)
+ }
+ extra := ""
+ if initOpts.Name != defaultMachineName {
+ extra = " " + initOpts.Name
}
+ fmt.Printf("To start your machine run:\n\n\tpodman machine start%s\n\n", extra)
return err
}
diff --git a/cmd/podman/machine/inspect.go b/cmd/podman/machine/inspect.go
index 4600a2b6d..d69c382f2 100644
--- a/cmd/podman/machine/inspect.go
+++ b/cmd/podman/machine/inspect.go
@@ -20,6 +20,7 @@ var (
Use: "inspect [options] [MACHINE...]",
Short: "Inspect an existing machine",
Long: "Provide details on a managed virtual machine",
+ PersistentPreRunE: rootlessOnly,
RunE: inspect,
Example: `podman machine inspect myvm`,
ValidArgsFunction: autocompleteMachine,
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 5254d50cf..dd4a86697 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -4,6 +4,7 @@
package machine
import (
+ "fmt"
"os"
"sort"
"strconv"
@@ -15,9 +16,9 @@ import (
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
+ "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/machine"
"github.com/docker/go-units"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -27,6 +28,7 @@ var (
Aliases: []string{"ls"},
Short: "List machines",
Long: "List managed virtual machines.",
+ PersistentPreRunE: rootlessOnly,
RunE: list,
Args: validate.NoArgs,
ValidArgsFunction: completion.AutocompleteNone,
@@ -43,22 +45,6 @@ type listFlagType struct {
quiet bool
}
-type ListReporter struct {
- Name string
- Default bool
- Created string
- Running bool
- LastUp string
- Stream string
- VMType string
- CPUs uint64
- Memory string
- DiskSize string
- Port int
- RemoteUsername string
- IdentityPath string
-}
-
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: lsCmd,
@@ -68,7 +54,7 @@ func init() {
flags := lsCmd.Flags()
formatFlagName := "format"
flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n", "Format volume output using JSON or a Go template")
- _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&ListReporter{}))
+ _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ListReporter{}))
flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers")
flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Show only machine names")
}
@@ -87,7 +73,7 @@ func list(cmd *cobra.Command, args []string) error {
provider := GetSystemDefaultProvider()
listResponse, err = provider.List(opts)
if err != nil {
- return errors.Wrap(err, "error listing vms")
+ return fmt.Errorf("listing vms: %w", err)
}
// Sort by last run
@@ -121,8 +107,8 @@ func list(cmd *cobra.Command, args []string) error {
return outputTemplate(cmd, machineReporter)
}
-func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
- headers := report.Headers(ListReporter{}, map[string]string{
+func outputTemplate(cmd *cobra.Command, responses []*entities.ListReporter) error {
+ headers := report.Headers(entities.ListReporter{}, map[string]string{
"LastUp": "LAST UP",
"VmType": "VM TYPE",
"CPUs": "CPUS",
@@ -137,7 +123,7 @@ func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
switch {
case cmd.Flags().Changed("format"):
row = cmd.Flag("format").Value.String()
- listFlag.noHeading = !report.HasTable(row)
+ printHeader = report.HasTable(row)
row = report.NormalizeFormat(row)
default:
row = cmd.Flag("format").Value.String()
@@ -156,7 +142,7 @@ func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
defer w.Flush()
if printHeader {
if err := tmpl.Execute(w, headers); err != nil {
- return errors.Wrapf(err, "failed to write report column headers")
+ return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return tmpl.Execute(w, responses)
@@ -181,15 +167,15 @@ func streamName(imageStream string) string {
return imageStream
}
-func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
+func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
- machineResponses := make([]*ListReporter, 0, len(vms))
+ machineResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
- response := new(ListReporter)
+ response := new(entities.ListReporter)
response.Default = vm.Name == cfg.Engine.ActiveService
response.Name = vm.Name
response.Running = vm.Running
@@ -209,25 +195,29 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
return machineResponses, nil
}
-func toHumanFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
+func toHumanFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
- humanResponses := make([]*ListReporter, 0, len(vms))
+ humanResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
- response := new(ListReporter)
+ response := new(entities.ListReporter)
if vm.Name == cfg.Engine.ActiveService {
response.Name = vm.Name + "*"
response.Default = true
} else {
response.Name = vm.Name
}
- if vm.Running {
+ switch {
+ case vm.Running:
response.LastUp = "Currently running"
response.Running = true
- } else {
+ case vm.Starting:
+ response.LastUp = "Currently starting"
+ response.Starting = true
+ default:
response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago"
}
response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago"
diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go
index 5a8a06b9d..0618337cc 100644
--- a/cmd/podman/machine/machine.go
+++ b/cmd/podman/machine/machine.go
@@ -101,12 +101,6 @@ func resolveEventSock() ([]string, error) {
return []string{sock}, nil
}
- xdg, err := util.GetRuntimeDir()
- if err != nil {
- logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err)
- return nil, nil
- }
-
re := regexp.MustCompile(`machine_events.*\.sock`)
sockPaths := make([]string, 0)
fn := func(path string, info os.DirEntry, err error) error {
@@ -125,8 +119,12 @@ func resolveEventSock() ([]string, error) {
sockPaths = append(sockPaths, path)
return nil
}
+ sockDir, err := eventSockDir()
+ if err != nil {
+ logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err)
+ }
- if err := filepath.WalkDir(filepath.Join(xdg, "podman"), fn); err != nil {
+ if err := filepath.WalkDir(sockDir, fn); err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, nil
}
@@ -135,6 +133,14 @@ func resolveEventSock() ([]string, error) {
return sockPaths, nil
}
+func eventSockDir() (string, error) {
+ xdg, err := util.GetRuntimeDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(xdg, "podman"), nil
+}
+
func newMachineEvent(status events.Status, event events.Event) {
openEventSock.Do(initMachineEvents)
diff --git a/cmd/podman/machine/machine_unix.go b/cmd/podman/machine/machine_unix.go
index b56d081ec..a2d9b9d8e 100644
--- a/cmd/podman/machine/machine_unix.go
+++ b/cmd/podman/machine/machine_unix.go
@@ -4,9 +4,20 @@
package machine
import (
+ "fmt"
"os"
+
+ "github.com/containers/podman/v4/pkg/rootless"
+ "github.com/spf13/cobra"
)
func isUnixSocket(file os.DirEntry) bool {
return file.Type()&os.ModeSocket != 0
}
+
+func rootlessOnly(cmd *cobra.Command, args []string) error {
+ if !rootless.IsRootless() {
+ return fmt.Errorf("cannot run command %q as root", cmd.CommandPath())
+ }
+ return nil
+}
diff --git a/cmd/podman/machine/machine_windows.go b/cmd/podman/machine/machine_windows.go
index ffd5d8827..bf1240868 100644
--- a/cmd/podman/machine/machine_windows.go
+++ b/cmd/podman/machine/machine_windows.go
@@ -3,9 +3,18 @@ package machine
import (
"os"
"strings"
+
+ "github.com/spf13/cobra"
)
func isUnixSocket(file os.DirEntry) bool {
// Assume a socket on Windows, since sock mode is not supported yet https://github.com/golang/go/issues/33357
return !file.Type().IsDir() && strings.HasSuffix(file.Name(), ".sock")
}
+
+func rootlessOnly(cmd *cobra.Command, args []string) error {
+ // Rootless is not relevant on Windows. In the future rootless.IsRootless
+ // could be switched to return true on Windows, and other codepaths migrated
+
+ return nil
+}
diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go
index a6e66265c..362c9a7d3 100644
--- a/cmd/podman/machine/rm.go
+++ b/cmd/podman/machine/rm.go
@@ -20,6 +20,7 @@ var (
Use: "rm [options] [MACHINE]",
Short: "Remove an existing machine",
Long: "Remove a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: rm,
Args: cobra.MaximumNArgs(1),
Example: `podman machine rm myvm`,
diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go
index 5777882da..1b9e1b2bd 100644
--- a/cmd/podman/machine/set.go
+++ b/cmd/podman/machine/set.go
@@ -18,6 +18,7 @@ var (
Use: "set [options] [NAME]",
Short: "Sets a virtual machine setting",
Long: "Sets an updatable virtual machine setting",
+ PersistentPreRunE: rootlessOnly,
RunE: setMachine,
Args: cobra.MaximumNArgs(1),
Example: `podman machine set --rootful=false`,
diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go
index 4a86da67a..cb2f62f51 100644
--- a/cmd/podman/machine/ssh.go
+++ b/cmd/podman/machine/ssh.go
@@ -4,22 +4,24 @@
package machine
import (
+ "fmt"
"net/url"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
sshCmd = &cobra.Command{
- Use: "ssh [options] [NAME] [COMMAND [ARG ...]]",
- Short: "SSH into an existing machine",
- Long: "SSH into a managed virtual machine ",
- RunE: ssh,
+ Use: "ssh [options] [NAME] [COMMAND [ARG ...]]",
+ Short: "SSH into an existing machine",
+ Long: "SSH into a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
+ RunE: ssh,
Example: `podman machine ssh myvm
podman machine ssh myvm echo hello`,
ValidArgsFunction: autocompleteMachineSSH,
@@ -87,9 +89,10 @@ func ssh(cmd *cobra.Command, args []string) error {
vm, err = provider.LoadVMByName(vmName)
if err != nil {
- return errors.Wrapf(err, "vm %s not found", vmName)
+ return fmt.Errorf("vm %s not found: %w", vmName, err)
}
- return vm.SSH(vmName, sshOpts)
+ err = vm.SSH(vmName, sshOpts)
+ return utils.HandleOSExecError(err)
}
func remoteConnectionUsername() (string, error) {
diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go
index c9b99e63b..15a75522a 100644
--- a/cmd/podman/machine/start.go
+++ b/cmd/podman/machine/start.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/machine"
- "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -18,6 +17,7 @@ var (
Use: "start [MACHINE]",
Short: "Start an existing machine",
Long: "Start a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: start,
Args: cobra.MaximumNArgs(1),
Example: `podman machine start myvm`,
@@ -54,9 +54,9 @@ func start(_ *cobra.Command, args []string) error {
}
if active {
if vmName == activeName {
- return errors.Wrapf(machine.ErrVMAlreadyRunning, "cannot start VM %s", vmName)
+ return fmt.Errorf("cannot start VM %s: %w", vmName, machine.ErrVMAlreadyRunning)
}
- return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running", vmName, activeName)
+ return fmt.Errorf("cannot start VM %s. VM %s is currently running or starting: %w", vmName, activeName, machine.ErrMultipleActiveVM)
}
fmt.Printf("Starting machine %q\n", vmName)
if err := vm.Start(vmName, machine.StartOptions{}); err != nil {
diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go
index 993662792..ce87a44c4 100644
--- a/cmd/podman/machine/stop.go
+++ b/cmd/podman/machine/stop.go
@@ -17,6 +17,7 @@ var (
Use: "stop [MACHINE]",
Short: "Stop an existing machine",
Long: "Stop a managed virtual machine ",
+ PersistentPreRunE: rootlessOnly,
RunE: stop,
Args: cobra.MaximumNArgs(1),
Example: `podman machine stop myvm`,