From b5f54a9b23e8d9418700494da9aa78d8db354c43 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 15 Mar 2021 14:52:43 -0500 Subject: introduce podman machine podman machine allows podman to create, manage, and interact with a vm running some form of linux (default is fcos). podman is then configured to be able to interact with the vm automatically. while this is usable on linux, the real push is to get this working on both current apple architectures in macos. Ashley Cui contributed to this PR and was a great help. [NO TESTS NEEDED] Signed-off-by: baude --- pkg/machine/fcos.go | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 pkg/machine/fcos.go (limited to 'pkg/machine/fcos.go') diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go new file mode 100644 index 000000000..8bbad458f --- /dev/null +++ b/pkg/machine/fcos.go @@ -0,0 +1,162 @@ +package machine + +import ( + "crypto/sha256" + "io" + "io/ioutil" + url2 "net/url" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/containers/storage/pkg/archive" + + "github.com/sirupsen/logrus" + + digest "github.com/opencontainers/go-digest" +) + +// These should eventually be moved into machine/qemu as +// they are specific to running qemu +var ( + artifact string = "qemu" + Format string = "qcow2.xz" +) + +type FcosDownload struct { + Download +} + +func NewFcosDownloader(vmType, vmName string) (DistributionDownload, error) { + info, err := getFCOSDownload() + if err != nil { + return nil, err + } + urlSplit := strings.Split(info.Location, "/") + imageName := urlSplit[len(urlSplit)-1] + url, err := url2.Parse(info.Location) + if err != nil { + return nil, err + } + + dataDir, err := GetDataDir(vmType) + if err != nil { + return nil, err + } + + fcd := FcosDownload{ + Download: Download{ + Arch: getFcosArch(), + Artifact: artifact, + Format: Format, + ImageName: imageName, + LocalPath: filepath.Join(dataDir, imageName), + Sha256sum: info.Sha256Sum, + URL: url, + VMName: vmName, + }, + } + fcd.Download.LocalUncompressedFile = fcd.getLocalUncompressedName() + return fcd, nil +} + +func (f FcosDownload) getLocalUncompressedName() string { + uncompressedFilename := filepath.Join(filepath.Dir(f.LocalPath), f.VMName+"_"+f.ImageName) + return strings.TrimSuffix(uncompressedFilename, ".xz") +} + +func (f FcosDownload) DownloadImage() error { + // check if the latest image is already present + ok, err := UpdateAvailable(&f.Download) + if err != nil { + return err + } + if !ok { + if err := DownloadVMImage(f.URL, f.LocalPath); err != nil { + return err + } + } + uncompressedFileWriter, err := os.OpenFile(f.getLocalUncompressedName(), os.O_CREATE|os.O_RDWR, 0600) + if err != nil { + return err + } + sourceFile, err := ioutil.ReadFile(f.LocalPath) + if err != nil { + return err + } + compressionType := archive.DetectCompression(sourceFile) + f.CompressionType = compressionType.Extension() + + switch f.CompressionType { + case "tar.xz": + return decompressXZ(f.LocalPath, uncompressedFileWriter) + default: + // File seems to be uncompressed, make a copy + if err := copyFile(f.LocalPath, uncompressedFileWriter); err != nil { + return err + } + } + return nil +} + +func copyFile(src string, dest *os.File) error { + source, err := os.Open(src) + if err != nil { + return err + } + defer func() { + if err := source.Close(); err != nil { + logrus.Error(err) + } + }() + _, err = io.Copy(dest, source) + return err +} + +func (f FcosDownload) Get() *Download { + return &f.Download +} + +type fcosDownloadInfo struct { + CompressionType string + Location string + Release string + Sha256Sum string +} + +func UpdateAvailable(d *Download) (bool, error) { + // check the sha of the local image if it exists + // get the sha of the remote image + // == dont bother to pull + files, err := ioutil.ReadDir(filepath.Dir(d.LocalPath)) + if err != nil { + return false, err + } + for _, file := range files { + if filepath.Base(d.LocalPath) == file.Name() { + b, err := ioutil.ReadFile(d.LocalPath) + if err != nil { + return false, err + } + s := sha256.Sum256(b) + sum := digest.NewDigestFromBytes(digest.SHA256, s[:]) + if sum.Encoded() == d.Sha256sum { + return true, nil + } + } + } + return false, nil +} + +func getFcosArch() string { + var arch string + // TODO fill in more architectures + switch runtime.GOARCH { + case "arm64": + arch = "aarch64" + default: + arch = "x86_64" + } + return arch +} -- cgit v1.2.3-54-g00ecf From 4ab8a6f67eb9de0de40d478cb0cbec05b1b725c0 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 22 Mar 2021 13:29:25 -0500 Subject: Improvements for machine clean up ci failures and add appropriate arch,os exclusion tags Signed-off-by: baude --- cmd/podman/common/completion.go | 8 +++ cmd/podman/machine/create.go | 28 +++----- cmd/podman/machine/destroy.go | 91 ------------------------ cmd/podman/machine/machine.go | 4 +- cmd/podman/machine/machine_unsupported.go | 5 ++ cmd/podman/machine/remove.go | 88 +++++++++++++++++++++++ cmd/podman/machine/ssh.go | 9 +-- cmd/podman/machine/start.go | 2 + cmd/podman/machine/stop.go | 2 + docs/source/machine.rst | 2 +- docs/source/markdown/podman-machine-create.1.md | 6 +- docs/source/markdown/podman-machine-destroy.1.md | 65 ----------------- docs/source/markdown/podman-machine-remove.1.md | 65 +++++++++++++++++ docs/source/markdown/podman-machine-ssh.1.md | 10 +++ docs/source/markdown/podman-machine.1.md | 2 +- pkg/bindings/connection.go | 12 +--- pkg/machine/config.go | 37 +++------- pkg/machine/fcos.go | 4 +- pkg/machine/qemu/machine.go | 20 ++++-- pkg/machine/qemu/options_amd64.go | 26 ------- pkg/machine/qemu/options_arm64.go | 40 ----------- pkg/machine/qemu/options_darwin.go | 15 ++++ pkg/machine/qemu/options_darwin_amd64.go | 18 +++++ pkg/machine/qemu/options_darwin_arm64.go | 36 ++++++++++ pkg/machine/qemu/options_linux.go | 7 ++ pkg/machine/qemu/options_linux_amd64.go | 18 +++++ pkg/specgen/generate/ports.go | 6 +- pkg/specgen/ports.go | 26 ------- utils/ports.go | 26 +++++++ 29 files changed, 352 insertions(+), 326 deletions(-) delete mode 100644 cmd/podman/machine/destroy.go create mode 100644 cmd/podman/machine/machine_unsupported.go create mode 100644 cmd/podman/machine/remove.go delete mode 100644 docs/source/markdown/podman-machine-destroy.1.md create mode 100644 docs/source/markdown/podman-machine-remove.1.md delete mode 100644 pkg/machine/qemu/options_amd64.go delete mode 100644 pkg/machine/qemu/options_arm64.go create mode 100644 pkg/machine/qemu/options_darwin.go create mode 100644 pkg/machine/qemu/options_darwin_amd64.go create mode 100644 pkg/machine/qemu/options_darwin_arm64.go create mode 100644 pkg/machine/qemu/options_linux.go create mode 100644 pkg/machine/qemu/options_linux_amd64.go delete mode 100644 pkg/specgen/ports.go create mode 100644 utils/ports.go (limited to 'pkg/machine/fcos.go') diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index bc106263c..6bed5e0c6 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1092,3 +1092,11 @@ func AutocompleteVolumeFilters(cmd *cobra.Command, args []string, toComplete str } return completeKeyValues(toComplete, kv) } + +// AutocompleteMachineSSH - Autocomplete machine ssh command. +func AutocompleteMachineSSH(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveDefault +} diff --git a/cmd/podman/machine/create.go b/cmd/podman/machine/create.go index 04c5e9e65..1da34327a 100644 --- a/cmd/podman/machine/create.go +++ b/cmd/podman/machine/create.go @@ -1,3 +1,5 @@ +// +build amd64,linux amd64,darwin arm64,darwin + package machine import ( @@ -6,17 +8,16 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" - "github.com/pkg/errors" "github.com/spf13/cobra" ) var ( createCmd = &cobra.Command{ - Use: "create [options] NAME", + Use: "create [options] [NAME]", Short: "Create a vm", - Long: "Create a virtual machine for Podman to run on. Virtual machines are used to run Podman on Macs. ", + Long: "Create a virtual machine for Podman to run on. Virtual machines are used to run Podman.", RunE: create, - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), Example: `podman machine create myvm`, ValidArgsFunction: completion.AutocompleteNone, } @@ -32,7 +33,8 @@ type CreateCLIOptions struct { } var ( - createOpts = CreateCLIOptions{} + createOpts = CreateCLIOptions{} + defaultMachineName string = "podman-machine-default" ) func init() { @@ -59,14 +61,6 @@ func init() { ) _ = createCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) - deviceFlagName := "name" - flags.StringVar( - &createOpts.Name, - deviceFlagName, "", - "set vm name", - ) - _ = createCmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) - ImagePathFlagName := "image-path" flags.StringVar(&createOpts.ImagePath, ImagePathFlagName, "", "Path to qcow image") _ = createCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) @@ -78,9 +72,9 @@ func init() { // TODO should we allow for a users to append to the qemu cmdline? func create(cmd *cobra.Command, args []string) error { - // TODO add ability to create default, not name required - if len(createOpts.Name) < 1 { - return errors.New("required --name not provided") + createOpts.Name = defaultMachineName + if len(args) > 0 { + createOpts.Name = args[0] } vmOpts := machine.CreateOptions{ CPUS: createOpts.CPUS, @@ -95,8 +89,6 @@ func create(cmd *cobra.Command, args []string) error { err error ) switch vmType { - case "foobar": - // do nothing default: // qemu is the default vm, err = qemu.NewMachine(vmOpts) } diff --git a/cmd/podman/machine/destroy.go b/cmd/podman/machine/destroy.go deleted file mode 100644 index ab23d607d..000000000 --- a/cmd/podman/machine/destroy.go +++ /dev/null @@ -1,91 +0,0 @@ -package machine - -import ( - "bufio" - "fmt" - "os" - "strings" - - "github.com/containers/common/pkg/completion" - "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/machine" - "github.com/containers/podman/v3/pkg/machine/qemu" - "github.com/spf13/cobra" -) - -var ( - destroyCmd = &cobra.Command{ - Use: "destroy [options] NAME", - Short: "Destroy an existing machine", - Long: "Destroy an existing machine ", - RunE: destroy, - Args: cobra.ExactArgs(1), - Example: `podman machine destroy myvm`, - ValidArgsFunction: completion.AutocompleteNone, - } -) - -var ( - destoryOptions machine.DestroyOptions -) - -func init() { - registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: destroyCmd, - Parent: machineCmd, - }) - - flags := destroyCmd.Flags() - formatFlagName := "force" - flags.BoolVar(&destoryOptions.Force, formatFlagName, false, "Do not prompt before destroying") - _ = destroyCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) - - keysFlagName := "save-keys" - flags.BoolVar(&destoryOptions.SaveKeys, keysFlagName, false, "Do not delete SSH keys") - _ = destroyCmd.RegisterFlagCompletionFunc(keysFlagName, common.AutocompleteJSONFormat) - - ignitionFlagName := "save-ignition" - flags.BoolVar(&destoryOptions.SaveIgnition, ignitionFlagName, false, "Do not delete ignition file") - _ = destroyCmd.RegisterFlagCompletionFunc(ignitionFlagName, common.AutocompleteJSONFormat) - - imageFlagName := "save-image" - flags.BoolVar(&destoryOptions.SaveImage, imageFlagName, false, "Do not delete the image file") - _ = destroyCmd.RegisterFlagCompletionFunc(imageFlagName, common.AutocompleteJSONFormat) -} - -func destroy(cmd *cobra.Command, args []string) error { - var ( - err error - vm machine.VM - vmType string - ) - switch vmType { - default: - vm, err = qemu.LoadVMByName(args[0]) - } - if err != nil { - return err - } - confirmationMessage, doIt, err := vm.Destroy(args[0], machine.DestroyOptions{}) - if err != nil { - return err - } - - if !destoryOptions.Force { - // Warn user - fmt.Println(confirmationMessage) - reader := bufio.NewReader(os.Stdin) - fmt.Print("Are you sure you want to continue? [y/N] ") - answer, err := reader.ReadString('\n') - if err != nil { - return err - } - if strings.ToLower(answer)[0] != 'y' { - return nil - } - } - return doIt() -} diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go index ce5a3b889..ed284ee10 100644 --- a/cmd/podman/machine/machine.go +++ b/cmd/podman/machine/machine.go @@ -1,3 +1,5 @@ +// +build amd64,linux amd64,darwin arm64,darwin + package machine import ( @@ -15,7 +17,7 @@ var ( machineCmd = &cobra.Command{ Use: "machine", Short: "Manage a virtual machine", - Long: "Manage a virtual machine. Virtual machines are used to run Podman on Macs.", + Long: "Manage a virtual machine. Virtual machines are used to run Podman.", PersistentPreRunE: noOp, PersistentPostRunE: noOp, RunE: validate.SubCommandExists, diff --git a/cmd/podman/machine/machine_unsupported.go b/cmd/podman/machine/machine_unsupported.go new file mode 100644 index 000000000..cb1636419 --- /dev/null +++ b/cmd/podman/machine/machine_unsupported.go @@ -0,0 +1,5 @@ +// +build !amd64 arm64,linux amd64,windows + +package machine + +func init() {} diff --git a/cmd/podman/machine/remove.go b/cmd/podman/machine/remove.go new file mode 100644 index 000000000..f6ce9e326 --- /dev/null +++ b/cmd/podman/machine/remove.go @@ -0,0 +1,88 @@ +// +build amd64,linux amd64,darwin arm64,darwin + +package machine + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/machine" + "github.com/containers/podman/v3/pkg/machine/qemu" + "github.com/spf13/cobra" +) + +var ( + removeCmd = &cobra.Command{ + Use: "remove [options] NAME", + Short: "Remove an existing machine", + Long: "Remove an existing machine ", + RunE: remove, + Args: cobra.ExactArgs(1), + Example: `podman machine remove myvm`, + ValidArgsFunction: completion.AutocompleteNone, + } +) + +var ( + destoryOptions machine.RemoveOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: removeCmd, + Parent: machineCmd, + }) + + flags := removeCmd.Flags() + formatFlagName := "force" + flags.BoolVar(&destoryOptions.Force, formatFlagName, false, "Do not prompt before removeing") + + keysFlagName := "save-keys" + flags.BoolVar(&destoryOptions.SaveKeys, keysFlagName, false, "Do not delete SSH keys") + + ignitionFlagName := "save-ignition" + flags.BoolVar(&destoryOptions.SaveIgnition, ignitionFlagName, false, "Do not delete ignition file") + + imageFlagName := "save-image" + flags.BoolVar(&destoryOptions.SaveImage, imageFlagName, false, "Do not delete the image file") +} + +func remove(cmd *cobra.Command, args []string) error { + var ( + err error + vm machine.VM + vmType string + ) + switch vmType { + default: + vm, err = qemu.LoadVMByName(args[0]) + } + if err != nil { + return err + } + confirmationMessage, doIt, err := vm.Remove(args[0], machine.RemoveOptions{}) + if err != nil { + return err + } + + if !destoryOptions.Force { + // Warn user + fmt.Println(confirmationMessage) + reader := bufio.NewReader(os.Stdin) + fmt.Print("Are you sure you want to continue? [y/N] ") + answer, err := reader.ReadString('\n') + if err != nil { + return err + } + if strings.ToLower(answer)[0] != 'y' { + return nil + } + } + return doIt() +} diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go index 32483f731..a7111a195 100644 --- a/cmd/podman/machine/ssh.go +++ b/cmd/podman/machine/ssh.go @@ -1,7 +1,9 @@ +// +build amd64,linux amd64,darwin arm64,darwin + package machine import ( - "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" @@ -14,13 +16,13 @@ var ( sshCmd = &cobra.Command{ Use: "ssh [options] NAME [COMMAND [ARG ...]]", Short: "SSH into a virtual machine", - Long: "SSH into a podman-managed virtual machine ", + Long: "SSH into a virtual machine ", RunE: ssh, Args: cobra.MinimumNArgs(1), Example: `podman machine ssh myvm podman machine ssh -e myvm echo hello`, - ValidArgsFunction: completion.AutocompleteNone, + ValidArgsFunction: common.AutocompleteMachineSSH, } ) @@ -38,7 +40,6 @@ func init() { flags := sshCmd.Flags() executeFlagName := "execute" flags.BoolVarP(&sshOpts.Execute, executeFlagName, "e", false, "Execute command from args") - _ = sshCmd.RegisterFlagCompletionFunc(executeFlagName, completion.AutocompleteDefault) } func ssh(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go index 762639358..44ade2850 100644 --- a/cmd/podman/machine/start.go +++ b/cmd/podman/machine/start.go @@ -1,3 +1,5 @@ +// +build amd64,linux amd64,darwin arm64,darwin + package machine import ( diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go index b6585c296..35fd4ff95 100644 --- a/cmd/podman/machine/stop.go +++ b/cmd/podman/machine/stop.go @@ -1,3 +1,5 @@ +// +build amd64,linux amd64,darwin arm64,darwin + package machine import ( diff --git a/docs/source/machine.rst b/docs/source/machine.rst index aefceb3a5..cf7f72cce 100644 --- a/docs/source/machine.rst +++ b/docs/source/machine.rst @@ -3,7 +3,7 @@ Machine :doc:`create ` Create a new virtual machine -:doc:`destroy ` Destroy a virtual machine +:doc:`remove ` Remove a virtual machine :doc:`ssh ` SSH into a virtual machine :doc:`start ` Start a virtual machine :doc:`stop ` Stop a virtual machine diff --git a/docs/source/markdown/podman-machine-create.1.md b/docs/source/markdown/podman-machine-create.1.md index 1e199bb77..eb498f2dd 100644 --- a/docs/source/markdown/podman-machine-create.1.md +++ b/docs/source/markdown/podman-machine-create.1.md @@ -4,7 +4,7 @@ podman\-machine\-create - Create a new virtual machine ## SYNOPSIS -**podman machine create** [*options*] *name* +**podman machine create** [*options*] [*name*] ## DESCRIPTION @@ -34,10 +34,6 @@ Fully qualified path of the uncompressed image file Memory (in MB). -#### **--name** - -Name to assign to the VM - #### **--help** Print usage statement. diff --git a/docs/source/markdown/podman-machine-destroy.1.md b/docs/source/markdown/podman-machine-destroy.1.md deleted file mode 100644 index 7c5421418..000000000 --- a/docs/source/markdown/podman-machine-destroy.1.md +++ /dev/null @@ -1,65 +0,0 @@ -% podman-machine-destroy(1) - -## NAME -podman\-machine\-destroy - Destroy a virtual machine - -## SYNOPSIS -**podman machine destroy** [*options*] *name* - -## DESCRIPTION - -Destroy a virtual machine and its related files. What is actually deleted -depends on the virtual machine type. For all virtual machines, the generated -SSH keys and the podman system connection are deleted. The ignition files -generated for that VM are also destroyed as is its image file on the filesystem. - -Users get a display of what will be deleted and are required to confirm unless the option `--force` -is used. - - -## OPTIONS - -#### **--help** - -Print usage statement. - -#### **--force** - -Delete without confirmation - -#### **--save-ignition** - -Do not delete the generated ignition file - -#### **--save-image** - -Do not delete the VM image - -#### **--save-keys** - -Do not delete the SSH keys for the VM. The system connection is always -deleted. - -## EXAMPLES - -Destroy a VM named "test1" - -``` -$ podman machine destroy test1 - -The following files will be deleted: - -/home/user/.ssh/test1 -/home/user/.ssh/test1.pub -/home/user/.config/containers/podman/machine/qemu/test1.ign -/home/user/.local/share/containers/podman/machine/qemu/test1_fedora-coreos-33.20210315.1.0-qemu.x86_64.qcow2 -/home/user/.config/containers/podman/machine/qemu/test1.json - -Are you sure you want to continue? [y/N] y -``` - -## SEE ALSO -podman-machine (1) - -## HISTORY -March 2021, Originally compiled by Ashley Cui diff --git a/docs/source/markdown/podman-machine-remove.1.md b/docs/source/markdown/podman-machine-remove.1.md new file mode 100644 index 000000000..07763741d --- /dev/null +++ b/docs/source/markdown/podman-machine-remove.1.md @@ -0,0 +1,65 @@ +% podman-machine-remove(1) + +## NAME +podman\-machine\-remove - Remove a virtual machine + +## SYNOPSIS +**podman machine remove** [*options*] *name* + +## DESCRIPTION + +Remove a virtual machine and its related files. What is actually deleted +depends on the virtual machine type. For all virtual machines, the generated +SSH keys and the podman system connection are deleted. The ignition files +generated for that VM are also removeed as is its image file on the filesystem. + +Users get a display of what will be deleted and are required to confirm unless the option `--force` +is used. + + +## OPTIONS + +#### **--help** + +Print usage statement. + +#### **--force** + +Delete without confirmation + +#### **--save-ignition** + +Do not delete the generated ignition file + +#### **--save-image** + +Do not delete the VM image + +#### **--save-keys** + +Do not delete the SSH keys for the VM. The system connection is always +deleted. + +## EXAMPLES + +Remove a VM named "test1" + +``` +$ podman machine remove test1 + +The following files will be deleted: + +/home/user/.ssh/test1 +/home/user/.ssh/test1.pub +/home/user/.config/containers/podman/machine/qemu/test1.ign +/home/user/.local/share/containers/podman/machine/qemu/test1_fedora-coreos-33.20210315.1.0-qemu.x86_64.qcow2 +/home/user/.config/containers/podman/machine/qemu/test1.json + +Are you sure you want to continue? [y/N] y +``` + +## SEE ALSO +podman-machine (1) + +## HISTORY +March 2021, Originally compiled by Ashley Cui diff --git a/docs/source/markdown/podman-machine-ssh.1.md b/docs/source/markdown/podman-machine-ssh.1.md index ed35a38e4..bcecd1010 100644 --- a/docs/source/markdown/podman-machine-ssh.1.md +++ b/docs/source/markdown/podman-machine-ssh.1.md @@ -16,16 +16,26 @@ tied to the Linux kernel. ## OPTIONS +#### **--execute**, **-e** + +Execute the given command on the VM + #### **--help** Print usage statement. ## EXAMPLES +To get an interactive session with a VM called `myvm`: ``` $ podman machine ssh myvm ``` +To run a command on a VM called `myvm`: +``` +$ podman machine ssh -e myvm -- rpm -q podman +``` + ## SEE ALSO podman-machine (1) diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md index b31d8f788..52f212cdd 100644 --- a/docs/source/markdown/podman-machine.1.md +++ b/docs/source/markdown/podman-machine.1.md @@ -14,7 +14,7 @@ podman\-machine - Manage Podman's virtual machine | Command | Man Page | Description | | ------- | ------------------------------------------------------- | ----------------------------- | | create | [podman-machine-create(1)](podman-machine-create.1.md) | Create a new virtual machine | -| destroy | [podman-machine-destroy(1)](podman-machine-destroy.1.md)| Destroy a virtual machine | +| remove | [podman-machine-destroy(1)](podman-machine-remove.1.md)| Remove a virtual machine | | ssh | [podman-machine-ssh.1.md(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | | start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | | stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 148b592e9..21a8e7a8b 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -22,21 +22,11 @@ import ( "golang.org/x/crypto/ssh/agent" ) -/* - WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING - WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING - - DO NOT MERGE WITHOUT REVERTING THE HACK BELOW - - WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING - WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING -*/ var ( BasePath = &url.URL{ Scheme: "http", Host: "d", - //Path: "/v" + version.APIVersion[version.Libpod][version.CurrentAPI].String() + "/libpod", - Path: "/v3.0.0/libpod", + Path: "/v" + version.APIVersion[version.Libpod][version.CurrentAPI].String() + "/libpod", } ) diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 2a70b8ff7..242401ab4 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -50,7 +50,7 @@ type StartOptions struct{} type StopOptions struct{} -type DestroyOptions struct { +type RemoveOptions struct { Force bool SaveKeys bool SaveImage bool @@ -59,7 +59,7 @@ type DestroyOptions struct { type VM interface { Create(opts CreateOptions) error - Destroy(name string, opts DestroyOptions) (string, func() error, error) + Remove(name string, opts RemoveOptions) (string, func() error, error) SSH(name string, opts SSHOptions) error Start(name string, opts StartOptions) error Stop(name string, opts StopOptions) error @@ -70,33 +70,18 @@ type DistributionDownload interface { Get() *Download } -// TODO is this even needed? -type TestVM struct{} - -func (vm *TestVM) Create(opts CreateOptions) error { - return nil -} - -func (vm *TestVM) Start(name string, opts StartOptions) error { - return nil -} -func (vm *TestVM) Stop(name string, opts StopOptions) error { - return nil -} - func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url.URL { userInfo := url.User(userName) uri := url.URL{ - Scheme: "ssh", - Opaque: "", - User: userInfo, - Host: host, - Path: path, - RawPath: "", - ForceQuery: false, - RawQuery: "", - Fragment: "", - RawFragment: "", + Scheme: "ssh", + Opaque: "", + User: userInfo, + Host: host, + Path: path, + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", } if len(port) > 0 { uri.Host = net.JoinHostPort(uri.Hostname(), port) diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go index 8bbad458f..0c6a2485e 100644 --- a/pkg/machine/fcos.go +++ b/pkg/machine/fcos.go @@ -11,10 +11,8 @@ import ( "strings" "github.com/containers/storage/pkg/archive" - - "github.com/sirupsen/logrus" - digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" ) // These should eventually be moved into machine/qemu as diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 30d96ce08..504b64bd5 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -10,8 +10,9 @@ import ( "strconv" "time" + "github.com/containers/podman/v3/utils" + "github.com/containers/podman/v3/pkg/machine" - "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/storage/pkg/homedir" "github.com/digitalocean/go-qemu/qmp" "github.com/pkg/errors" @@ -56,7 +57,7 @@ func NewMachine(opts machine.CreateOptions) (machine.VM, error) { } // Add a random port for ssh - port, err := specgen.GetRandomPort() + port, err := utils.GetRandomPort() if err != nil { return nil, err } @@ -170,7 +171,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { attr := new(os.ProcAttr) files := []*os.File{os.Stdin, os.Stdout, os.Stderr} attr.Files = files - fmt.Print(v.CmdLine) + logrus.Debug(v.CmdLine) _, err = os.StartProcess(v.CmdLine[0], v.CmdLine, attr) return err } @@ -211,22 +212,29 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { // NewQMPMonitor creates the monitor subsection of our vm func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) { - rtDir, err := getDataDir() + rtDir, err := getSocketDir() if err != nil { return Monitor{}, err } + rtDir = filepath.Join(rtDir, "podman") + if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) { + // TODO 0644 is fine on linux but macos is weird + if err := os.MkdirAll(rtDir, 0755); err != nil { + return Monitor{}, err + } + } if timeout == 0 { timeout = defaultQMPTimeout } monitor := Monitor{ Network: network, - Address: filepath.Join(rtDir, "podman", "qmp_"+name+".sock"), + Address: filepath.Join(rtDir, "qmp_"+name+".sock"), Timeout: timeout, } return monitor, nil } -func (v *MachineVM) Destroy(name string, opts machine.DestroyOptions) (string, func() error, error) { +func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, func() error, error) { var ( files []string ) diff --git a/pkg/machine/qemu/options_amd64.go b/pkg/machine/qemu/options_amd64.go deleted file mode 100644 index 85a2c4b3e..000000000 --- a/pkg/machine/qemu/options_amd64.go +++ /dev/null @@ -1,26 +0,0 @@ -package qemu - -import ( - "github.com/containers/podman/v3/pkg/util" -) - -var ( - QemuCommand = "qemu-kvm" -) - -func (v *MachineVM) addArchOptions() []string { - opts := []string{"-cpu", "host"} - return opts -} - -func (v *MachineVM) prepare() error { - return nil -} - -func (v *MachineVM) archRemovalFiles() []string { - return []string{} -} - -func getDataDir() (string, error) { - return util.GetRuntimeDir() -} diff --git a/pkg/machine/qemu/options_arm64.go b/pkg/machine/qemu/options_arm64.go deleted file mode 100644 index c5b0ea16b..000000000 --- a/pkg/machine/qemu/options_arm64.go +++ /dev/null @@ -1,40 +0,0 @@ -package qemu - -import ( - "os/exec" - "path/filepath" -) - -var ( - QemuCommand = "qemu-system-aarch64" -) - -func (v *MachineVM) addArchOptions() []string { - ovmfDir := getOvmfDir(v.ImagePath, v.Name) - opts := []string{ - "-accel", "hvf", - "-cpu", "cortex-a57", - "-M", "virt,highmem=off", - "-drive", "file=/usr/local/share/qemu/edk2-aarch64-code.fd,if=pflash,format=raw,readonly=on", - "-drive", "file=" + ovmfDir + ",if=pflash,format=raw"} - return opts -} - -func (v *MachineVM) prepare() error { - ovmfDir := getOvmfDir(v.ImagePath, v.Name) - cmd := []string{"dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} - return exec.Command(cmd[0], cmd[1:]...).Run() -} - -func (v *MachineVM) archRemovalFiles() []string { - ovmDir := getOvmfDir(v.ImagePath, v.Name) - return []string{ovmDir} -} - -func getDataDir() (string, error) { - return "/tmp", nil -} - -func getOvmfDir(imagePath, vmName string) string { - return filepath.Join(filepath.Dir(imagePath), vmName+"_ovmf_vars.fd") -} diff --git a/pkg/machine/qemu/options_darwin.go b/pkg/machine/qemu/options_darwin.go new file mode 100644 index 000000000..46ccf24cb --- /dev/null +++ b/pkg/machine/qemu/options_darwin.go @@ -0,0 +1,15 @@ +package qemu + +import ( + "os" + + "github.com/pkg/errors" +) + +func getSocketDir() (string, error) { + tmpDir, ok := os.LookupEnv("TMPDIR") + if !ok { + return "", errors.New("unable to resolve TMPDIR") + } + return tmpDir, nil +} diff --git a/pkg/machine/qemu/options_darwin_amd64.go b/pkg/machine/qemu/options_darwin_amd64.go new file mode 100644 index 000000000..69f7982b2 --- /dev/null +++ b/pkg/machine/qemu/options_darwin_amd64.go @@ -0,0 +1,18 @@ +package qemu + +var ( + QemuCommand = "qemu-system-x86_64" +) + +func (v *MachineVM) addArchOptions() []string { + opts := []string{"-cpu", "host"} + return opts +} + +func (v *MachineVM) prepare() error { + return nil +} + +func (v *MachineVM) archRemovalFiles() []string { + return []string{} +} diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go new file mode 100644 index 000000000..7513b3048 --- /dev/null +++ b/pkg/machine/qemu/options_darwin_arm64.go @@ -0,0 +1,36 @@ +package qemu + +import ( + "os/exec" + "path/filepath" +) + +var ( + QemuCommand = "qemu-system-aarch64" +) + +func (v *MachineVM) addArchOptions() []string { + ovmfDir := getOvmfDir(v.ImagePath, v.Name) + opts := []string{ + "-accel", "hvf", + "-cpu", "cortex-a57", + "-M", "virt,highmem=off", + "-drive", "file=/usr/local/share/qemu/edk2-aarch64-code.fd,if=pflash,format=raw,readonly=on", + "-drive", "file=" + ovmfDir + ",if=pflash,format=raw"} + return opts +} + +func (v *MachineVM) prepare() error { + ovmfDir := getOvmfDir(v.ImagePath, v.Name) + cmd := []string{"dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} + return exec.Command(cmd[0], cmd[1:]...).Run() +} + +func (v *MachineVM) archRemovalFiles() []string { + ovmDir := getOvmfDir(v.ImagePath, v.Name) + return []string{ovmDir} +} + +func getOvmfDir(imagePath, vmName string) string { + return filepath.Join(filepath.Dir(imagePath), vmName+"_ovmf_vars.fd") +} diff --git a/pkg/machine/qemu/options_linux.go b/pkg/machine/qemu/options_linux.go new file mode 100644 index 000000000..0a2e40d8f --- /dev/null +++ b/pkg/machine/qemu/options_linux.go @@ -0,0 +1,7 @@ +package qemu + +import "github.com/containers/podman/v3/pkg/util" + +func getSocketDir() (string, error) { + return util.GetRuntimeDir() +} diff --git a/pkg/machine/qemu/options_linux_amd64.go b/pkg/machine/qemu/options_linux_amd64.go new file mode 100644 index 000000000..cc0a4bab2 --- /dev/null +++ b/pkg/machine/qemu/options_linux_amd64.go @@ -0,0 +1,18 @@ +package qemu + +var ( + QemuCommand = "qemu-kvm" +) + +func (v *MachineVM) addArchOptions() []string { + opts := []string{"-cpu", "host"} + return opts +} + +func (v *MachineVM) prepare() error { + return nil +} + +func (v *MachineVM) archRemovalFiles() []string { + return []string{} +} diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index d5d779c8f..678e36a70 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -6,6 +6,8 @@ import ( "strconv" "strings" + "github.com/containers/podman/v3/utils" + "github.com/containers/podman/v3/libpod/image" "github.com/containers/podman/v3/pkg/specgen" "github.com/cri-o/ocicni/pkg/ocicni" @@ -218,7 +220,7 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, // Only get a random candidate for single entries or the start // of a range. Otherwise we just increment the candidate. if !tmp.isInRange || tmp.startOfRange { - candidate, err = specgen.GetRandomPort() + candidate, err = utils.GetRandomPort() if err != nil { return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort) } @@ -344,7 +346,7 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, img *imag for hostPort == 0 && tries > 0 { // We can't select a specific protocol, which is // unfortunate for the UDP case. - candidate, err := specgen.GetRandomPort() + candidate, err := utils.GetRandomPort() if err != nil { return nil, err } diff --git a/pkg/specgen/ports.go b/pkg/specgen/ports.go deleted file mode 100644 index 940b2a564..000000000 --- a/pkg/specgen/ports.go +++ /dev/null @@ -1,26 +0,0 @@ -package specgen - -import ( - "net" - "strconv" - - "github.com/pkg/errors" -) - -// Find a random, open port on the host -func GetRandomPort() (int, error) { - l, err := net.Listen("tcp", ":0") - if err != nil { - return 0, errors.Wrapf(err, "unable to get free TCP port") - } - defer l.Close() - _, randomPort, err := net.SplitHostPort(l.Addr().String()) - if err != nil { - return 0, errors.Wrapf(err, "unable to determine free port") - } - rp, err := strconv.Atoi(randomPort) - if err != nil { - return 0, errors.Wrapf(err, "unable to convert random port to int") - } - return rp, nil -} diff --git a/utils/ports.go b/utils/ports.go new file mode 100644 index 000000000..0a4f67dcc --- /dev/null +++ b/utils/ports.go @@ -0,0 +1,26 @@ +package utils + +import ( + "net" + "strconv" + + "github.com/pkg/errors" +) + +// Find a random, open port on the host +func GetRandomPort() (int, error) { + l, err := net.Listen("tcp", ":0") + if err != nil { + return 0, errors.Wrapf(err, "unable to get free TCP port") + } + defer l.Close() + _, randomPort, err := net.SplitHostPort(l.Addr().String()) + if err != nil { + return 0, errors.Wrapf(err, "unable to determine free port") + } + rp, err := strconv.Atoi(randomPort) + if err != nil { + return 0, errors.Wrapf(err, "unable to convert random port to int") + } + return rp, nil +} -- cgit v1.2.3-54-g00ecf