mirror of https://github.com/containers/podman.git
Merge pull request #13942 from baude/machinetests
Add podman machine test suite
This commit is contained in:
commit
05bdb4139a
2
Makefile
2
Makefile
|
|
@ -537,7 +537,7 @@ localunit: test/goecho/goecho test/version/version
|
||||||
UNIT=1 $(GOBIN)/ginkgo \
|
UNIT=1 $(GOBIN)/ginkgo \
|
||||||
-r \
|
-r \
|
||||||
$(TESTFLAGS) \
|
$(TESTFLAGS) \
|
||||||
--skipPackage test/e2e,pkg/apparmor,pkg/bindings,hack \
|
--skipPackage test/e2e,pkg/apparmor,pkg/bindings,hack,pkg/machine/e2e \
|
||||||
--cover \
|
--cover \
|
||||||
--covermode atomic \
|
--covermode atomic \
|
||||||
--coverprofile coverprofile \
|
--coverprofile coverprofile \
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,6 @@ func initMachine(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if finished, err := vm.Init(initOpts); err != nil || !finished {
|
if finished, err := vm.Init(initOpts); err != nil || !finished {
|
||||||
// Finished = true, err = nil - Success! Log a message with further instructions
|
// Finished = true, err = nil - Success! Log a message with further instructions
|
||||||
// Finished = false, err = nil - The installation is partially complete and podman should
|
// Finished = false, err = nil - The installation is partially complete and podman should
|
||||||
|
|
@ -144,7 +143,6 @@ func initMachine(cmd *cobra.Command, args []string) error {
|
||||||
// - a user has chosen to perform their own reboot
|
// - a user has chosen to perform their own reboot
|
||||||
// - reexec for limited admin operations, returning to parent
|
// - reexec for limited admin operations, returning to parent
|
||||||
// Finished = *, err != nil - Exit with an error message
|
// Finished = *, err != nil - Exit with an error message
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Machine init complete")
|
fmt.Println("Machine init complete")
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ BUILD_TAGS[abi]="${BUILD_TAGS[default]},!remoteclient"
|
||||||
BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,remoteclient"
|
BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,remoteclient"
|
||||||
|
|
||||||
declare -A SKIP_DIRS
|
declare -A SKIP_DIRS
|
||||||
SKIP_DIRS[abi]=""
|
SKIP_DIRS[abi]="pkg/machine/e2e"
|
||||||
# TODO: add "remote" build tag to pkg/api
|
# TODO: add "remote" build tag to pkg/api
|
||||||
SKIP_DIRS[tunnel]="pkg/api"
|
SKIP_DIRS[tunnel]="pkg/api,pkg/machine/e2e"
|
||||||
|
|
||||||
[[ $1 == run ]] && shift
|
[[ $1 == run ]] && shift
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
. "github.com/onsi/gomega/gexec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("run basic podman commands", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Basic ops", func() {
|
||||||
|
name := randomString(12)
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withNow()).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session).To(Exit(0))
|
||||||
|
|
||||||
|
bm := basicMachine{}
|
||||||
|
imgs, err := mb.setCmd(bm.withPodmanCommand([]string{"images", "-q"})).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(imgs).To(Exit(0))
|
||||||
|
Expect(len(imgs.outputToStringSlice())).To(Equal(0))
|
||||||
|
|
||||||
|
newImgs, err := mb.setCmd(bm.withPodmanCommand([]string{"pull", "quay.io/libpod/alpine_nginx"})).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(newImgs).To(Exit(0))
|
||||||
|
Expect(len(newImgs.outputToStringSlice())).To(Equal(1))
|
||||||
|
|
||||||
|
runAlp, err := mb.setCmd(bm.withPodmanCommand([]string{"run", "quay.io/libpod/alpine_nginx", "cat", "/etc/os-release"})).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(runAlp).To(Exit(0))
|
||||||
|
Expect(runAlp.outputToString()).To(ContainSubstring("Alpine Linux"))
|
||||||
|
|
||||||
|
rmCon, err := mb.setCmd(bm.withPodmanCommand([]string{"rm", "-a"})).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(rmCon).To(Exit(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
|
"github.com/containers/podman/v4/pkg/machine/qemu"
|
||||||
|
"github.com/containers/podman/v4/pkg/util"
|
||||||
|
. "github.com/onsi/ginkgo" //nolint:golint,stylecheck
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/onsi/gomega/gexec"
|
||||||
|
. "github.com/onsi/gomega/gexec" //nolint:golint,stylecheck
|
||||||
|
)
|
||||||
|
|
||||||
|
var originalHomeDir = os.Getenv("HOME")
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultTimeout time.Duration = 90 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type machineCommand interface {
|
||||||
|
buildCmd(m *machineTestBuilder) []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineTestBuilder interface {
|
||||||
|
setName(string) *MachineTestBuilder
|
||||||
|
setCmd(mc machineCommand) *MachineTestBuilder
|
||||||
|
setTimeout(duration time.Duration) *MachineTestBuilder
|
||||||
|
run() (*machineSession, error)
|
||||||
|
}
|
||||||
|
type machineSession struct {
|
||||||
|
*gexec.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
type machineTestBuilder struct {
|
||||||
|
cmd []string
|
||||||
|
imagePath string
|
||||||
|
name string
|
||||||
|
names []string
|
||||||
|
podmanBinary string
|
||||||
|
timeout time.Duration
|
||||||
|
}
|
||||||
|
type qemuMachineInspectInfo struct {
|
||||||
|
State machine.Status
|
||||||
|
VM qemu.MachineVM
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitWithTimeout waits for a command to complete for a given
|
||||||
|
// number of seconds
|
||||||
|
func (ms *machineSession) waitWithTimeout(timeout time.Duration) {
|
||||||
|
Eventually(ms, timeout).Should(Exit())
|
||||||
|
os.Stdout.Sync()
|
||||||
|
os.Stderr.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *machineSession) Bytes() []byte {
|
||||||
|
return []byte(ms.outputToString())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *machineSession) outputToStringSlice() []string {
|
||||||
|
var results []string
|
||||||
|
output := string(ms.Out.Contents())
|
||||||
|
for _, line := range strings.Split(output, "\n") {
|
||||||
|
if line != "" {
|
||||||
|
results = append(results, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputToString returns the output from a session in string form
|
||||||
|
func (ms *machineSession) outputToString() string {
|
||||||
|
if ms == nil || ms.Out == nil || ms.Out.Contents() == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(string(ms.Out.Contents()))
|
||||||
|
return strings.Join(fields, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMB constructor for machine test builders
|
||||||
|
func newMB() (*machineTestBuilder, error) {
|
||||||
|
mb := machineTestBuilder{
|
||||||
|
timeout: defaultTimeout,
|
||||||
|
}
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mb.podmanBinary = filepath.Join(cwd, "../../../bin/podman-remote")
|
||||||
|
if os.Getenv("PODMAN_BINARY") != "" {
|
||||||
|
mb.podmanBinary = os.Getenv("PODMAN_BINARY")
|
||||||
|
}
|
||||||
|
return &mb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setName sets the name of the virtuaql machine for the command
|
||||||
|
func (m *machineTestBuilder) setName(name string) *machineTestBuilder {
|
||||||
|
m.name = name
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCmd takes a machineCommand struct and assembles a cmd line
|
||||||
|
// representation of the podman machine command
|
||||||
|
func (m *machineTestBuilder) setCmd(mc machineCommand) *machineTestBuilder {
|
||||||
|
// If no name for the machine exists, we set a random name.
|
||||||
|
if !util.StringInSlice(m.name, m.names) {
|
||||||
|
if len(m.name) < 1 {
|
||||||
|
m.name = randomString(12)
|
||||||
|
}
|
||||||
|
m.names = append(m.names, m.name)
|
||||||
|
}
|
||||||
|
m.cmd = mc.buildCmd(m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineTestBuilder) setTimeout(timeout time.Duration) *machineTestBuilder {
|
||||||
|
m.timeout = timeout
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// toQemuInspectInfo is only for inspecting qemu machines. Other providers will need
|
||||||
|
// to make their own.
|
||||||
|
func (mb *machineTestBuilder) toQemuInspectInfo() ([]qemuMachineInspectInfo, int, error) {
|
||||||
|
args := []string{"machine", "inspect"}
|
||||||
|
args = append(args, mb.names...)
|
||||||
|
session, err := runWrapper(mb.podmanBinary, args, defaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, -1, err
|
||||||
|
}
|
||||||
|
mii := []qemuMachineInspectInfo{}
|
||||||
|
err = json.Unmarshal(session.Bytes(), &mii)
|
||||||
|
return mii, session.ExitCode(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineTestBuilder) run() (*machineSession, error) {
|
||||||
|
return runWrapper(m.podmanBinary, m.cmd, m.timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWrapper(podmanBinary string, cmdArgs []string, timeout time.Duration) (*machineSession, error) {
|
||||||
|
if len(os.Getenv("DEBUG")) > 0 {
|
||||||
|
cmdArgs = append([]string{"--log-level=debug"}, cmdArgs...)
|
||||||
|
}
|
||||||
|
fmt.Println(podmanBinary + " " + strings.Join(cmdArgs, " "))
|
||||||
|
c := exec.Command(podmanBinary, cmdArgs...)
|
||||||
|
session, err := Start(c, GinkgoWriter, GinkgoWriter)
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("Unable to start session: %q", err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ms := machineSession{session}
|
||||||
|
ms.waitWithTimeout(timeout)
|
||||||
|
fmt.Println("output:", ms.outputToString())
|
||||||
|
return &ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type basicMachine struct {
|
||||||
|
args []string
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s basicMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"-r"}
|
||||||
|
if len(s.args) > 0 {
|
||||||
|
cmd = append(cmd, s.args...)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *basicMachine) withPodmanCommand(args []string) *basicMachine {
|
||||||
|
s.args = args
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type initMachine struct {
|
||||||
|
/*
|
||||||
|
--cpus uint Number of CPUs (default 1)
|
||||||
|
--disk-size uint Disk size in GB (default 100)
|
||||||
|
--ignition-path string Path to ignition file
|
||||||
|
--image-path string Path to qcow image (default "testing")
|
||||||
|
-m, --memory uint Memory in MB (default 2048)
|
||||||
|
--now Start machine now
|
||||||
|
--rootful Whether this machine should prefer rootful container exectution
|
||||||
|
--timezone string Set timezone (default "local")
|
||||||
|
-v, --volume stringArray Volumes to mount, source:target
|
||||||
|
--volume-driver string Optional volume driver
|
||||||
|
|
||||||
|
*/
|
||||||
|
cpus *uint
|
||||||
|
diskSize *uint
|
||||||
|
ignitionPath string
|
||||||
|
imagePath string
|
||||||
|
memory *uint
|
||||||
|
now bool
|
||||||
|
timezone string
|
||||||
|
volumes []string
|
||||||
|
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "init"}
|
||||||
|
if i.cpus != nil {
|
||||||
|
cmd = append(cmd, "--cpus", strconv.Itoa(int(*i.cpus)))
|
||||||
|
}
|
||||||
|
if i.diskSize != nil {
|
||||||
|
cmd = append(cmd, "--disk-size", strconv.Itoa(int(*i.diskSize)))
|
||||||
|
}
|
||||||
|
if l := len(i.ignitionPath); l > 0 {
|
||||||
|
cmd = append(cmd, "--ignition-path", i.ignitionPath)
|
||||||
|
}
|
||||||
|
if l := len(i.imagePath); l > 0 {
|
||||||
|
cmd = append(cmd, "--image-path", i.imagePath)
|
||||||
|
}
|
||||||
|
if i.memory != nil {
|
||||||
|
cmd = append(cmd, "--memory", strconv.Itoa(int(*i.memory)))
|
||||||
|
}
|
||||||
|
if l := len(i.timezone); l > 0 {
|
||||||
|
cmd = append(cmd, "--timezone", i.timezone)
|
||||||
|
}
|
||||||
|
for _, v := range i.volumes {
|
||||||
|
cmd = append(cmd, "--volume", v)
|
||||||
|
}
|
||||||
|
if i.now {
|
||||||
|
cmd = append(cmd, "--now")
|
||||||
|
}
|
||||||
|
cmd = append(cmd, m.name)
|
||||||
|
i.cmd = cmd
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withCPUs(num uint) *initMachine {
|
||||||
|
i.cpus = &num
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
func (i *initMachine) withDiskSize(size uint) *initMachine {
|
||||||
|
i.diskSize = &size
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withIgnitionPath(path string) *initMachine {
|
||||||
|
i.ignitionPath = path
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withImagePath(path string) *initMachine {
|
||||||
|
i.imagePath = path
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withMemory(num uint) *initMachine {
|
||||||
|
i.memory = &num
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withNow() *initMachine {
|
||||||
|
i.now = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withTimezone(tz string) *initMachine {
|
||||||
|
i.timezone = tz
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withVolume(v string) *initMachine {
|
||||||
|
i.volumes = append(i.volumes, v)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type inspectMachine struct {
|
||||||
|
/*
|
||||||
|
--format string Format volume output using JSON or a Go template
|
||||||
|
*/
|
||||||
|
cmd []string
|
||||||
|
format string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *inspectMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "inspect"}
|
||||||
|
if len(i.format) > 0 {
|
||||||
|
cmd = append(cmd, "--format", i.format)
|
||||||
|
}
|
||||||
|
cmd = append(cmd, m.names...)
|
||||||
|
i.cmd = cmd
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *inspectMachine) withFormat(format string) *inspectMachine {
|
||||||
|
i.format = format
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type listMachine struct {
|
||||||
|
/*
|
||||||
|
--format string Format volume output using JSON or a Go template (default "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n")
|
||||||
|
--noheading Do not print headers
|
||||||
|
-q, --quiet Show only machine names
|
||||||
|
*/
|
||||||
|
|
||||||
|
format string
|
||||||
|
noHeading bool
|
||||||
|
quiet bool
|
||||||
|
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *listMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "list"}
|
||||||
|
if len(i.format) > 0 {
|
||||||
|
cmd = append(cmd, "--format", i.format)
|
||||||
|
}
|
||||||
|
if i.noHeading {
|
||||||
|
cmd = append(cmd, "--noheading")
|
||||||
|
}
|
||||||
|
if i.quiet {
|
||||||
|
cmd = append(cmd, "--quiet")
|
||||||
|
}
|
||||||
|
i.cmd = cmd
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *listMachine) withNoHeading() *listMachine {
|
||||||
|
i.noHeading = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *listMachine) withQuiet() *listMachine {
|
||||||
|
i.quiet = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *listMachine) withFormat(format string) *listMachine {
|
||||||
|
i.format = format
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type rmMachine struct {
|
||||||
|
/*
|
||||||
|
-f, --force Stop and do not prompt before rming
|
||||||
|
--save-ignition Do not delete ignition file
|
||||||
|
--save-image Do not delete the image file
|
||||||
|
--save-keys Do not delete SSH keys
|
||||||
|
|
||||||
|
*/
|
||||||
|
force bool
|
||||||
|
saveIgnition bool
|
||||||
|
saveImage bool
|
||||||
|
saveKeys bool
|
||||||
|
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *rmMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "rm"}
|
||||||
|
if i.force {
|
||||||
|
cmd = append(cmd, "--force")
|
||||||
|
}
|
||||||
|
if i.saveIgnition {
|
||||||
|
cmd = append(cmd, "--save-ignition")
|
||||||
|
}
|
||||||
|
if i.saveImage {
|
||||||
|
cmd = append(cmd, "--save-image")
|
||||||
|
}
|
||||||
|
if i.saveKeys {
|
||||||
|
cmd = append(cmd, "--save-keys")
|
||||||
|
}
|
||||||
|
cmd = append(cmd, m.name)
|
||||||
|
i.cmd = cmd
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *rmMachine) withForce() *rmMachine {
|
||||||
|
i.force = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *rmMachine) withSaveIgnition() *rmMachine {
|
||||||
|
i.saveIgnition = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *rmMachine) withSaveImage() *rmMachine {
|
||||||
|
i.saveImage = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *rmMachine) withSaveKeys() *rmMachine {
|
||||||
|
i.saveKeys = true
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type sshMachine struct {
|
||||||
|
/*
|
||||||
|
--username string Username to use when ssh-ing into the VM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
username string
|
||||||
|
sshCommand []string
|
||||||
|
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sshMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "ssh"}
|
||||||
|
if len(m.name) > 0 {
|
||||||
|
cmd = append(cmd, m.name)
|
||||||
|
}
|
||||||
|
if len(s.sshCommand) > 0 {
|
||||||
|
cmd = append(cmd, s.sshCommand...)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sshMachine) withUsername(name string) *sshMachine {
|
||||||
|
s.username = name
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sshMachine) withSSHComand(sshCommand []string) *sshMachine {
|
||||||
|
s.sshCommand = sshCommand
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type startMachine struct {
|
||||||
|
/*
|
||||||
|
No command line args other than a machine vm name (also not required)
|
||||||
|
*/
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s startMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "start"}
|
||||||
|
if len(m.name) > 0 {
|
||||||
|
cmd = append(cmd, m.name)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
type stopMachine struct {
|
||||||
|
/*
|
||||||
|
No command line args other than a machine vm name (also not required)
|
||||||
|
*/
|
||||||
|
cmd []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s stopMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
|
cmd := []string{"machine", "stop"}
|
||||||
|
if len(m.name) > 0 {
|
||||||
|
cmd = append(cmd, m.name)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
. "github.com/onsi/gomega/gexec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine init", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("bad init name", func() {
|
||||||
|
i := initMachine{}
|
||||||
|
reallyLongName := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
session, err := mb.setName(reallyLongName).setCmd(&i).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
It("simple init", func() {
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
inspectBefore, ec, err := mb.toQemuInspectInfo()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(ec).To(BeZero())
|
||||||
|
|
||||||
|
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)))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
It("simple init with start", func() {
|
||||||
|
i := initMachine{}
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
inspectBefore, ec, err := mb.toQemuInspectInfo()
|
||||||
|
Expect(ec).To(BeZero())
|
||||||
|
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]))
|
||||||
|
|
||||||
|
s := startMachine{}
|
||||||
|
ssession, err := mb.setCmd(s).setTimeout(time.Minute * 10).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(ssession).Should(Exit(0))
|
||||||
|
|
||||||
|
inspectAfter, ec, err := mb.toQemuInspectInfo()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(ec).To(BeZero())
|
||||||
|
Expect(len(inspectBefore)).To(BeNumerically(">", 0))
|
||||||
|
Expect(len(inspectAfter)).To(BeNumerically(">", 0))
|
||||||
|
Expect(inspectAfter[0].State).To(Equal(machine.Running))
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v4/pkg/machine/qemu"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine stop", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("inspect bad name", func() {
|
||||||
|
i := inspectMachine{}
|
||||||
|
reallyLongName := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
session, err := mb.setName(reallyLongName).setCmd(&i).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("inspect two machines", func() {
|
||||||
|
i := new(initMachine)
|
||||||
|
foo1, err := mb.setName("foo1").setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(foo1.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
ii := new(initMachine)
|
||||||
|
foo2, err := mb.setName("foo2").setCmd(ii.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(foo2.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
inspect := new(inspectMachine)
|
||||||
|
inspectSession, err := mb.setName("foo1").setCmd(inspect).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(inspectSession.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
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{}
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/buildah/util"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
. "github.com/onsi/gomega/gexec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine list", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("list machine", func() {
|
||||||
|
list := new(listMachine)
|
||||||
|
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
|
||||||
|
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session).To(Exit(0))
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
It("list machines with quiet", func() {
|
||||||
|
// Random names for machines to test list
|
||||||
|
name1 := randomString(12)
|
||||||
|
name2 := randomString(12)
|
||||||
|
|
||||||
|
list := new(listMachine)
|
||||||
|
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
|
||||||
|
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setName(name1).setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session).To(Exit(0))
|
||||||
|
|
||||||
|
session2, err := mb.setName(name2).setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session2).To(Exit(0))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
listNames := secondList.outputToStringSlice()
|
||||||
|
stripAsterisk(listNames)
|
||||||
|
Expect(util.StringInSlice(name1, listNames)).To(BeTrue())
|
||||||
|
Expect(util.StringInSlice(name2, listNames)).To(BeTrue())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
func stripAsterisk(sl []string) {
|
||||||
|
for idx, val := range sl {
|
||||||
|
sl[idx] = strings.TrimRight(val, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
url2 "net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultStream string = "podman-testing"
|
||||||
|
tmpDir string = "/var/tmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fqImageName string
|
||||||
|
suiteImageName string
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestLibpod ginkgo master function
|
||||||
|
func TestMachine(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Podman Machine tests")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = BeforeSuite(func() {
|
||||||
|
fcd, err := machine.GetFCOSDownload(defaultStream)
|
||||||
|
if err != nil {
|
||||||
|
Fail("unable to get virtual machine image")
|
||||||
|
}
|
||||||
|
suiteImageName = strings.TrimSuffix(path.Base(fcd.Location), ".xz")
|
||||||
|
fqImageName = filepath.Join(tmpDir, suiteImageName)
|
||||||
|
if _, err := os.Stat(fqImageName); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
getMe, err := url2.Parse(fcd.Location)
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("unable to create url for download: %q", err))
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
if err := machine.DownloadVMImage(getMe, fqImageName+".xz"); err != nil {
|
||||||
|
Fail(fmt.Sprintf("unable to download machine image: %q", err))
|
||||||
|
}
|
||||||
|
fmt.Println("Download took: ", time.Since(now).String())
|
||||||
|
if err := machine.Decompress(fqImageName+".xz", fqImageName); err != nil {
|
||||||
|
Fail(fmt.Sprintf("unable to decompress image file: %q", err))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Fail(fmt.Sprintf("unable to check for cache image: %q", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var _ = SynchronizedAfterSuite(func() {},
|
||||||
|
func() {
|
||||||
|
fmt.Println("After")
|
||||||
|
})
|
||||||
|
|
||||||
|
func setup() (string, *machineTestBuilder) {
|
||||||
|
homeDir, err := ioutil.TempDir("/var/tmp", "podman_test")
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to create home directory: %q", err))
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(homeDir, ".ssh"), 0700); err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to create ssh dir: %q", err))
|
||||||
|
}
|
||||||
|
sshConfig, err := os.Create(filepath.Join(homeDir, ".ssh", "config"))
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to create ssh config: %q", err))
|
||||||
|
}
|
||||||
|
if _, err := sshConfig.WriteString("IdentitiesOnly=yes"); err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to write ssh config: %q", err))
|
||||||
|
}
|
||||||
|
if err := sshConfig.Close(); err != nil {
|
||||||
|
Fail(fmt.Sprintf("unable to close ssh config file descriptor: %q", err))
|
||||||
|
}
|
||||||
|
if err := os.Setenv("HOME", homeDir); err != nil {
|
||||||
|
Fail("failed to set home dir")
|
||||||
|
}
|
||||||
|
if err := os.Unsetenv("SSH_AUTH_SOCK"); err != nil {
|
||||||
|
Fail("unable to unset SSH_AUTH_SOCK")
|
||||||
|
}
|
||||||
|
mb, err := newMB()
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to create machine test: %q", err))
|
||||||
|
}
|
||||||
|
f, err := os.Open(fqImageName)
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to open file %s: %q", fqImageName, err))
|
||||||
|
}
|
||||||
|
mb.imagePath = filepath.Join(homeDir, suiteImageName)
|
||||||
|
n, err := os.Create(mb.imagePath)
|
||||||
|
if err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to create file %s: %q", mb.imagePath, err))
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(n, f); err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to copy %ss to %s: %q", fqImageName, mb.imagePath, err))
|
||||||
|
}
|
||||||
|
return homeDir, mb
|
||||||
|
}
|
||||||
|
|
||||||
|
func teardown(origHomeDir string, testDir string, mb *machineTestBuilder) {
|
||||||
|
s := new(stopMachine)
|
||||||
|
for _, name := range mb.names {
|
||||||
|
if _, err := mb.setName(name).setCmd(s).run(); err != nil {
|
||||||
|
fmt.Printf("error occured rm'ing machine: %q\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(testDir); err != nil {
|
||||||
|
Fail(fmt.Sprintf("failed to remove test dir: %q", err))
|
||||||
|
}
|
||||||
|
// this needs to be last in teardown
|
||||||
|
if err := os.Setenv("HOME", origHomeDir); err != nil {
|
||||||
|
Fail("failed to set home dir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine rm", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("bad init name", func() {
|
||||||
|
i := rmMachine{}
|
||||||
|
reallyLongName := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
session, err := mb.setName(reallyLongName).setCmd(&i).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Remove machine", func() {
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
rm := rmMachine{}
|
||||||
|
_, err = mb.setCmd(rm.withForce()).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
|
// Inspecting a non-existent machine should fail
|
||||||
|
// which means it is gone
|
||||||
|
_, ec, err := mb.toQemuInspectInfo()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(ec).To(Equal(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Remove running machine", func() {
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath).withNow()).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
rm := new(rmMachine)
|
||||||
|
|
||||||
|
// Removing a running machine should fail
|
||||||
|
stop, err := mb.setCmd(rm).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(stop.ExitCode()).To(Equal(125))
|
||||||
|
|
||||||
|
// Removing again with force
|
||||||
|
stopAgain, err := mb.setCmd(rm.withForce()).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(stopAgain.ExitCode()).To(BeZero())
|
||||||
|
|
||||||
|
// Inspect to be dead sure
|
||||||
|
_, ec, err := mb.toQemuInspectInfo()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(ec).To(Equal(125))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine ssh", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("bad machine name", func() {
|
||||||
|
name := randomString(12)
|
||||||
|
ssh := sshMachine{}
|
||||||
|
session, err := mb.setName(name).setCmd(ssh).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(125))
|
||||||
|
// TODO seems like stderr is not being returned; re-enabled when fixed
|
||||||
|
//Expect(session.outputToString()).To(ContainSubstring("not exist"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("ssh to non-running machine", func() {
|
||||||
|
name := randomString(12)
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
ssh := sshMachine{}
|
||||||
|
sshSession, err := mb.setName(name).setCmd(ssh).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
// TODO seems like stderr is not being returned; re-enabled when fixed
|
||||||
|
//Expect(sshSession.outputToString()).To(ContainSubstring("is not running"))
|
||||||
|
Expect(sshSession.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("ssh to running machine and check os-type", func() {
|
||||||
|
name := randomString(12)
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withNow()).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
ssh := sshMachine{}
|
||||||
|
sshSession, err := mb.setName(name).setCmd(ssh.withSSHComand([]string{"cat", "/etc/os-release"})).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(sshSession.ExitCode()).To(Equal(0))
|
||||||
|
Expect(sshSession.outputToString()).To(ContainSubstring("Fedora CoreOS"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/podman/v4/pkg/machine"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine start", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("start simple machine", func() {
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
s := new(startMachine)
|
||||||
|
startSession, err := mb.setCmd(s).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(startSession.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
info, ec, err := mb.toQemuInspectInfo()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(ec).To(BeZero())
|
||||||
|
Expect(info[0].State).To(Equal(machine.Running))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("podman machine stop", func() {
|
||||||
|
var (
|
||||||
|
mb *machineTestBuilder
|
||||||
|
testDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
testDir, mb = setup()
|
||||||
|
})
|
||||||
|
AfterEach(func() {
|
||||||
|
teardown(originalHomeDir, testDir, mb)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("stop bad name", func() {
|
||||||
|
i := stopMachine{}
|
||||||
|
reallyLongName := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
session, err := mb.setName(reallyLongName).setCmd(&i).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Stop running machine", func() {
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setCmd(i.withImagePath(mb.imagePath).withNow()).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
stop := new(stopMachine)
|
||||||
|
// Removing a running machine should fail
|
||||||
|
stopSession, err := mb.setCmd(stop).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(stopSession.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
// Stopping it again should not result in an error
|
||||||
|
stopAgain, err := mb.setCmd(stop).run()
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(stopAgain.ExitCode()).To(BeZero())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -43,7 +43,7 @@ type FcosDownload struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload, error) {
|
func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload, error) {
|
||||||
info, err := getFCOSDownload(imageStream)
|
info, err := GetFCOSDownload(imageStream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ func (f FcosDownload) Get() *Download {
|
||||||
return &f.Download
|
return &f.Download
|
||||||
}
|
}
|
||||||
|
|
||||||
type fcosDownloadInfo struct {
|
type FcosDownloadInfo struct {
|
||||||
CompressionType string
|
CompressionType string
|
||||||
Location string
|
Location string
|
||||||
Release string
|
Release string
|
||||||
|
|
@ -139,7 +139,7 @@ func getStreamURL(streamType string) url2.URL {
|
||||||
|
|
||||||
// This should get Exported and stay put as it will apply to all fcos downloads
|
// 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
|
// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version
|
||||||
func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) { // nolint:staticcheck,unparam
|
func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { //nolint:staticcheck
|
||||||
var (
|
var (
|
||||||
fcosstable stream.Stream
|
fcosstable stream.Stream
|
||||||
altMeta release.Release
|
altMeta release.Release
|
||||||
|
|
@ -150,8 +150,8 @@ func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) { // nolint:
|
||||||
// fcos trees, we should remove it and re-release at least on
|
// fcos trees, we should remove it and re-release at least on
|
||||||
// macs.
|
// macs.
|
||||||
// TODO: remove when podman4.0 is in coreos
|
// TODO: remove when podman4.0 is in coreos
|
||||||
// nolint:staticcheck
|
|
||||||
imageStream = "podman-testing"
|
imageStream = "podman-testing" //nolint:staticcheck
|
||||||
|
|
||||||
switch imageStream {
|
switch imageStream {
|
||||||
case "podman-testing":
|
case "podman-testing":
|
||||||
|
|
@ -194,7 +194,7 @@ func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) { // nolint:
|
||||||
}
|
}
|
||||||
disk := qcow2.Disk
|
disk := qcow2.Disk
|
||||||
|
|
||||||
return &fcosDownloadInfo{
|
return &FcosDownloadInfo{
|
||||||
Location: disk.Location,
|
Location: disk.Location,
|
||||||
Sha256Sum: disk.Sha256,
|
Sha256Sum: disk.Sha256,
|
||||||
CompressionType: "xz",
|
CompressionType: "xz",
|
||||||
|
|
@ -228,7 +228,7 @@ func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) { // nolint:
|
||||||
if disk == nil {
|
if disk == nil {
|
||||||
return nil, fmt.Errorf("unable to pull VM image: no disk in stream")
|
return nil, fmt.Errorf("unable to pull VM image: no disk in stream")
|
||||||
}
|
}
|
||||||
return &fcosDownloadInfo{
|
return &FcosDownloadInfo{
|
||||||
Location: disk.Location,
|
Location: disk.Location,
|
||||||
Release: qemu.Release,
|
Release: qemu.Release,
|
||||||
Sha256Sum: disk.Sha256,
|
Sha256Sum: disk.Sha256,
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
vm.IgnitionFilePath = *ignitionFile
|
vm.IgnitionFilePath = *ignitionFile
|
||||||
|
|
||||||
imagePath, err := NewMachineFile(opts.ImagePath, nil)
|
imagePath, err := NewMachineFile(opts.ImagePath, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -373,7 +372,6 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
||||||
if err := v.writeConfig(); err != nil {
|
if err := v.writeConfig(); err != nil {
|
||||||
return false, fmt.Errorf("writing JSON file: %w", err)
|
return false, fmt.Errorf("writing JSON file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// User has provided ignition file so keygen
|
// User has provided ignition file so keygen
|
||||||
// will be skipped.
|
// will be skipped.
|
||||||
if len(opts.IgnitionPath) < 1 {
|
if len(opts.IgnitionPath) < 1 {
|
||||||
|
|
@ -387,7 +385,6 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
||||||
if err := v.prepare(); err != nil {
|
if err := v.prepare(); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
originalDiskSize, err := getDiskSize(v.getImageFile())
|
originalDiskSize, err := getDiskSize(v.getImageFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -514,17 +511,28 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
time.Sleep(wait)
|
time.Sleep(wait)
|
||||||
wait++
|
wait++
|
||||||
}
|
}
|
||||||
|
defer qemuSocketConn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := qemuSocketConn.(*net.UnixConn).File()
|
fd, err := qemuSocketConn.(*net.UnixConn).File()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
dnr, err := os.OpenFile("/dev/null", os.O_RDONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dnr.Close()
|
||||||
|
dnw, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dnw.Close()
|
||||||
|
|
||||||
attr := new(os.ProcAttr)
|
attr := new(os.ProcAttr)
|
||||||
files := []*os.File{os.Stdin, os.Stdout, os.Stderr, fd}
|
files := []*os.File{dnr, dnw, dnw, fd}
|
||||||
attr.Files = files
|
attr.Files = files
|
||||||
logrus.Debug(v.CmdLine)
|
logrus.Debug(v.CmdLine)
|
||||||
cmd := v.CmdLine
|
cmd := v.CmdLine
|
||||||
|
|
@ -552,7 +560,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
}
|
}
|
||||||
_, err = os.StartProcess(cmd[0], cmd, attr)
|
_, err = os.StartProcess(cmd[0], cmd, attr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrapf(err, "unable to execute %q", cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("Waiting for VM ...")
|
fmt.Println("Waiting for VM ...")
|
||||||
|
|
@ -575,11 +583,11 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
_, err = bufio.NewReader(conn).ReadString('\n')
|
_, err = bufio.NewReader(conn).ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.Mounts) > 0 {
|
if len(v.Mounts) > 0 {
|
||||||
state, err := v.State()
|
state, err := v.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -918,7 +926,7 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
|
||||||
sshDestination := username + "@localhost"
|
sshDestination := username + "@localhost"
|
||||||
port := strconv.Itoa(v.Port)
|
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"}
|
||||||
if len(opts.Args) > 0 {
|
if len(opts.Args) > 0 {
|
||||||
args = append(args, opts.Args...)
|
args = append(args, opts.Args...)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1085,9 +1093,19 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
attr := new(os.ProcAttr)
|
attr := new(os.ProcAttr)
|
||||||
// Pass on stdin, stdout, stderr
|
dnr, err := os.OpenFile("/dev/null", os.O_RDONLY, 0755)
|
||||||
files := []*os.File{os.Stdin, os.Stdout, os.Stderr}
|
if err != nil {
|
||||||
attr.Files = files
|
return "", noForwarding, err
|
||||||
|
}
|
||||||
|
dnw, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return "", noForwarding, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer dnr.Close()
|
||||||
|
defer dnw.Close()
|
||||||
|
|
||||||
|
attr.Files = []*os.File{dnr, dnw, dnw}
|
||||||
cmd := []string{binary}
|
cmd := []string{binary}
|
||||||
cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", v.QMPMonitor.Address.GetPath()), "-pid-file", v.PidFilePath.GetPath()}...)
|
cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", v.QMPMonitor.Address.GetPath()), "-pid-file", v.PidFilePath.GetPath()}...)
|
||||||
// Add the ssh port
|
// Add the ssh port
|
||||||
|
|
@ -1104,7 +1122,7 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
|
||||||
fmt.Println(cmd)
|
fmt.Println(cmd)
|
||||||
}
|
}
|
||||||
_, err = os.StartProcess(cmd[0], cmd, attr)
|
_, err = os.StartProcess(cmd[0], cmd, attr)
|
||||||
return forwardSock, state, err
|
return forwardSock, state, errors.Wrapf(err, "unable to execute: %q", cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwardingState) {
|
func (v *MachineVM) setupAPIForwarding(cmd []string) ([]string, string, apiForwardingState) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue