Skip to content

Commit 9e0871e

Browse files
committed
tests: Add e2e tests
This initial set of tests covers the basic use case of running and removing VMs/disk images. Signed-off-by: Chris Kyrouac <[email protected]>
1 parent 74172ae commit 9e0871e

File tree

12 files changed

+732
-3
lines changed

12 files changed

+732
-3
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ jobs:
2121
go install github.com/onsi/ginkgo/v2/ginkgo@latest
2222
make GOOPTS=-buildvcs=false
2323
export PATH=$PATH:$HOME/go/bin
24-
make test
24+
make integration_tests

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/bin
2+
test/e2e/e2e.test

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ all: out_dir
88
out_dir:
99
mkdir -p $(output_dir)
1010

11-
test:
12-
ginkgo -tags $(build_tags) ./...
11+
integration_tests:
12+
ginkgo run -tags $(build_tags) --skip-package test ./...
13+
14+
# !! These tests will modify your system's resources. See note in e2e_test.go. !!
15+
e2e_test: all
16+
ginkgo -tags $(build_tags) ./test/...
1317

1418
clean:
1519
rm -f $(output_dir)/*

test/e2e/e2e_test.go

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
package e2e_test
2+
3+
// ****************************************************************************
4+
// These are end-to-end tests that run the podman-bootc binary.
5+
// A rootful podman machine is assumed to already be running.
6+
// The tests interact directly with libvirt (on linux), qemu (on darwin),
7+
// podman-bootc cache dirs, and podman images and containers.
8+
//
9+
// Running these tests will create/delete VMs, pull/remove podman images
10+
// and containers, and remove the entire podman-bootc cache dir.
11+
//
12+
// These tests depend on the quay.io/ckyrouac/podman-bootc-test image
13+
// which is built from the Containerfiles in the test/resources directory.
14+
// ****************************************************************************
15+
16+
import (
17+
"encoding/json"
18+
"os"
19+
"path/filepath"
20+
"sync"
21+
"testing"
22+
23+
"gitlab.com/bootc-org/podman-bootc/pkg/config"
24+
"gitlab.com/bootc-org/podman-bootc/test/e2e"
25+
26+
. "github.com/onsi/ginkgo/v2"
27+
. "github.com/onsi/gomega"
28+
)
29+
30+
func TestPodmanBootcE2E(t *testing.T) {
31+
RegisterFailHandler(Fail)
32+
RunSpecs(t, "End to End Test Suite")
33+
}
34+
35+
var _ = BeforeSuite(func() {
36+
err := e2e.Cleanup()
37+
Expect(err).To(Not(HaveOccurred()))
38+
})
39+
40+
var _ = AfterSuite(func() {
41+
err := e2e.Cleanup()
42+
Expect(err).To(Not(HaveOccurred()))
43+
})
44+
45+
var _ = Describe("E2E", func() {
46+
Context("Run with no args from a fresh install", Ordered, func() {
47+
// Create the disk/VM once to avoid the overhead of creating it for each test
48+
var vm *e2e.TestVM
49+
50+
BeforeAll(func() {
51+
var err error
52+
vm, err = e2e.BootVM(e2e.BaseImage)
53+
Expect(err).To(Not(HaveOccurred()))
54+
})
55+
56+
It("should pull the container image", func() {
57+
imagesListOutput, _, err := e2e.RunPodman("images", e2e.BaseImage, "--format", "json")
58+
Expect(err).To(Not(HaveOccurred()))
59+
imagesList := []map[string]interface{}{}
60+
json.Unmarshal([]byte(imagesListOutput), &imagesList)
61+
Expect(imagesList).To(HaveLen(1))
62+
})
63+
64+
It("should create a bootc disk image", func() {
65+
vmDirs, err := e2e.ListCacheDirs()
66+
Expect(err).To(Not(HaveOccurred()))
67+
Expect(vmDirs).To(HaveLen(1))
68+
69+
_, err = os.Stat(filepath.Join(vmDirs[0], config.DiskImage))
70+
Expect(err).To(Not(HaveOccurred()))
71+
})
72+
73+
It("should create a new virtual machine", func() {
74+
vmExists, err := e2e.VMExists(vm.Id)
75+
Expect(err).To(Not(HaveOccurred()))
76+
Expect(vmExists).To(BeTrue())
77+
})
78+
79+
It("should start an ssh session into the VM", func() {
80+
// Send a command to the VM and check the output
81+
vm.SendCommand("echo 'hello'", "hello")
82+
Expect(vm.StdOut[len(vm.StdOut)-1]).To(ContainSubstring("hello"))
83+
})
84+
85+
It("should keep the VM running after the initial ssh session is closed", func() {
86+
vm.StdIn.Close() // this closes the ssh session
87+
88+
vmIsRunning, err := e2e.VMIsRunning(vm.Id)
89+
Expect(err).To(Not(HaveOccurred()))
90+
Expect(vmIsRunning).To(BeTrue())
91+
})
92+
93+
It("should open a new ssh session into the VM via the ssh cmd", func() {
94+
_, _, err := e2e.RunPodmanBootc("ssh", vm.Id) //TODO: test the output, send a command
95+
Expect(err).To(Not(HaveOccurred()))
96+
})
97+
98+
It("Should delete the VM and persist the disk image when calling stop", func() {
99+
_, _, err := e2e.RunPodmanBootc("stop", vm.Id)
100+
Expect(err).To(Not(HaveOccurred()))
101+
102+
//qemu doesn't immediately stop the VM, so we need to wait for it to stop
103+
Eventually(func() bool {
104+
vmExists, err := e2e.VMExists(vm.Id)
105+
Expect(err).To(Not(HaveOccurred()))
106+
return vmExists
107+
}).Should(BeFalse())
108+
109+
vmDirs, err := e2e.ListCacheDirs()
110+
Expect(err).To(Not(HaveOccurred()))
111+
112+
_, err = os.Stat(filepath.Join(vmDirs[0], config.DiskImage))
113+
Expect(err).To(Not(HaveOccurred()))
114+
})
115+
116+
It("Should remove the disk image when calling rm", func() {
117+
_, _, err := e2e.RunPodmanBootc("rm", vm.Id)
118+
Expect(err).To(Not(HaveOccurred()))
119+
120+
vmDirs, err := e2e.ListCacheDirs()
121+
Expect(err).To(Not(HaveOccurred()))
122+
123+
Expect(vmDirs).To(HaveLen(0))
124+
})
125+
126+
It("Should recreate the disk and VM when calling run", func() {
127+
var err error
128+
vm, err = e2e.BootVM(e2e.BaseImage)
129+
Expect(err).To(Not(HaveOccurred()))
130+
131+
vmDirs, err := e2e.ListCacheDirs()
132+
Expect(err).To(Not(HaveOccurred()))
133+
Expect(vmDirs).To(HaveLen(1))
134+
135+
vmExists, err := e2e.VMExists(vm.Id)
136+
Expect(err).To(Not(HaveOccurred()))
137+
Expect(vmExists).To(BeTrue())
138+
})
139+
140+
It("Should prevent removing a VM with an active SSH session", func() {
141+
_, _, err := e2e.RunPodmanBootc("rm", "-f", vm.Id)
142+
Expect(err).To(HaveOccurred())
143+
144+
Eventually(func() int {
145+
vmDirs, err := e2e.ListCacheDirs()
146+
Expect(err).To(Not(HaveOccurred()))
147+
return len(vmDirs)
148+
}).Should(Equal(1))
149+
})
150+
151+
It("Should remove the cache directory when calling rm -f while VM is running", func() {
152+
// the SSH connection needs to be closed before attempting rm -f
153+
err := vm.StdIn.Close()
154+
Expect(err).To(Not(HaveOccurred()))
155+
156+
_, _, err = e2e.RunPodmanBootc("rm", "-f", vm.Id)
157+
Expect(err).To(Not(HaveOccurred()))
158+
159+
Eventually(func() int {
160+
vmDirs, err := e2e.ListCacheDirs()
161+
Expect(err).To(Not(HaveOccurred()))
162+
return len(vmDirs)
163+
}).Should(Equal(0))
164+
})
165+
166+
AfterAll(func() {
167+
vm.StdIn.Close()
168+
e2e.Cleanup()
169+
})
170+
})
171+
172+
Context("Multiple VMs exist", Ordered, func() {
173+
var activeVM *e2e.TestVM
174+
var inactiveVM *e2e.TestVM
175+
var stoppedVM *e2e.TestVM
176+
177+
BeforeAll(func() {
178+
var err error
179+
errors := make(chan error)
180+
181+
var wg sync.WaitGroup
182+
wg.Add(1)
183+
go func() {
184+
// create an "active" VM
185+
// running with an active SSH session
186+
println("**** STARTING ACTIVE VM")
187+
activeVM, err = e2e.BootVM(e2e.TestImageTwo)
188+
if err != nil {
189+
errors <- err
190+
}
191+
wg.Done()
192+
}()
193+
194+
wg.Add(1)
195+
go func() {
196+
// create an "inactive" VM
197+
// running with no active SSH session
198+
inactiveVM, err = e2e.BootVM(e2e.TestImageOne)
199+
if err != nil {
200+
errors <- err
201+
}
202+
err = inactiveVM.StdIn.Close()
203+
if err != nil {
204+
errors <- err
205+
}
206+
wg.Done()
207+
}()
208+
209+
wg.Add(1)
210+
go func() {
211+
// create a "stopped" VM
212+
// VM does not exist but the VM directory containing the cached disk image does
213+
stoppedVM, err = e2e.BootVM(e2e.BaseImage)
214+
if err != nil {
215+
errors <- err
216+
}
217+
err = stoppedVM.StdIn.Close() //ssh needs to be closed before stopping the VM
218+
if err != nil {
219+
errors <- err
220+
}
221+
_, _, err = e2e.RunPodmanBootc("stop", stoppedVM.Id)
222+
if err != nil {
223+
errors <- err
224+
}
225+
wg.Done()
226+
}()
227+
228+
wg.Wait()
229+
close(errors)
230+
231+
if err := <-errors; err != nil {
232+
Fail(err.Error())
233+
}
234+
235+
// validate there are 3 vm directories
236+
vmDirs, err := e2e.ListCacheDirs()
237+
Expect(err).To(Not(HaveOccurred()))
238+
Expect(vmDirs).To(HaveLen(3))
239+
})
240+
241+
It("Should list multiple VMs", func() {
242+
stdout, _, err := e2e.RunPodmanBootc("list")
243+
Expect(err).To(Not(HaveOccurred()))
244+
245+
listOutput := e2e.ParseListOutput(stdout)
246+
Expect(listOutput).To(HaveLen(3))
247+
Expect(listOutput).To(ContainElement(e2e.ListEntry{
248+
Id: activeVM.Id,
249+
Repo: e2e.TestImageTwo,
250+
Running: "true",
251+
}))
252+
253+
Expect(listOutput).To(ContainElement(e2e.ListEntry{
254+
Id: inactiveVM.Id,
255+
Repo: e2e.TestImageOne,
256+
Running: "true",
257+
}))
258+
259+
Expect(listOutput).To(ContainElement(e2e.ListEntry{
260+
Id: stoppedVM.Id,
261+
Repo: e2e.BaseImage,
262+
Running: "false",
263+
}))
264+
})
265+
266+
It("Should remove all inactive VMs and caches when calling rm -f --all", func() {
267+
_, _, err := e2e.RunPodmanBootc("rm", "-f", "--all")
268+
Expect(err).To(Not(HaveOccurred()))
269+
270+
stdout, _, err := e2e.RunPodmanBootc("list")
271+
Expect(err).To(Not(HaveOccurred()))
272+
273+
// should keep the active VM that has an ssh session open
274+
Expect(stdout).To(ContainSubstring(activeVM.Id))
275+
276+
// should remove the other VMs
277+
Expect(stdout).To(Not(ContainSubstring(stoppedVM.Id)))
278+
Expect(stdout).To(Not(ContainSubstring(inactiveVM.Id)))
279+
280+
vmDirs, err := e2e.ListCacheDirs()
281+
Expect(err).To(Not(HaveOccurred()))
282+
Expect(vmDirs).To(HaveLen(1))
283+
Expect(vmDirs[0]).To(ContainSubstring(activeVM.Id))
284+
})
285+
286+
It("Should no-op and return successfully when rm -f --all with no VMs", func() {
287+
//cleanup the remaining active VM first
288+
err := activeVM.StdIn.Close()
289+
Expect(err).To(Not(HaveOccurred()))
290+
_, _, err = e2e.RunPodmanBootc("rm", "-f", activeVM.Id)
291+
Expect(err).To(Not(HaveOccurred()))
292+
293+
// verify there are no VMs
294+
vmDirs, err := e2e.ListCacheDirs()
295+
Expect(err).To(Not(HaveOccurred()))
296+
Expect(vmDirs).To(HaveLen(0))
297+
298+
// attempt rm -f --all
299+
_, _, err = e2e.RunPodmanBootc("rm", "-f", "--all")
300+
Expect(err).To(Not(HaveOccurred()))
301+
})
302+
303+
AfterAll(func() {
304+
activeVM.StdIn.Close()
305+
inactiveVM.StdIn.Close()
306+
stoppedVM.StdIn.Close()
307+
e2e.Cleanup()
308+
})
309+
})
310+
})

0 commit comments

Comments
 (0)