Skip to content

Commit 41e879f

Browse files
Luca Stocchilstocchi
authored andcommitted
test: add e2e test with fedora
it adds an e2e test which downloads fedora 40 and create a VM using it. Then mounts an ISO image containing the user-data and meta-data files needed by cloud-init to apply our configuration. The meta-data file is empty but the user-data file contains info about a new user that has to be added to the system. After the VM gets started it verifies that the user has been actually added by connecting using SSH. At the end, the VM is stopped. The test is not run on macOS12 as it does not support efi bootloader. Signed-off-by: Luca Stocchi <[email protected]>
1 parent 0ae085d commit 41e879f

File tree

4 files changed

+172
-1
lines changed

4 files changed

+172
-1
lines changed

test/assets/seed.img

352 KB
Binary file not shown.

test/osprovider.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212

1313
"github.com/crc-org/vfkit/pkg/config"
14+
"github.com/xi2/xz"
1415

1516
"github.com/cavaliergopher/grab/v3"
1617
"github.com/crc-org/crc/v2/pkg/extract"
@@ -42,6 +43,59 @@ func downloadPuipui(destDir string) ([]string, error) {
4243
return extract.Uncompress(resp.Filename, destDir)
4344
}
4445

46+
func downloadFedora(destDir string) (string, error) {
47+
const fedoraVersion = "40"
48+
arch := kernelArch()
49+
release := "1.14"
50+
buildString := fmt.Sprintf("%s-%s-%s", arch, fedoraVersion, release)
51+
52+
var fedoraURL = fmt.Sprintf("https://download.fedoraproject.org/pub/fedora/linux/releases/%s/Cloud/%s/images/Fedora-Cloud-Base-AmazonEC2.%s.raw.xz", fedoraVersion, arch, buildString)
53+
54+
// https://github.com/cavaliergopher/grab/issues/104
55+
grab.DefaultClient.UserAgent = "vfkit"
56+
resp, err := grab.Get(destDir, fedoraURL)
57+
if err != nil {
58+
return "", err
59+
}
60+
return uncompressFedora(resp.Filename, destDir)
61+
}
62+
63+
func uncompressFedora(fileName string, targetDir string) (string, error) {
64+
file, err := os.Open(filepath.Clean(fileName))
65+
if err != nil {
66+
return "", err
67+
}
68+
defer file.Close()
69+
70+
stat, err := file.Stat()
71+
if err != nil {
72+
return "", err
73+
}
74+
header := make([]byte, min(262, stat.Size()))
75+
if _, err := io.ReadFull(file, header); err != nil {
76+
return "", err
77+
}
78+
79+
reader, err := xz.NewReader(file, 0)
80+
if err != nil {
81+
return "", err
82+
}
83+
84+
xzCutName, _ := strings.CutSuffix(filepath.Base(file.Name()), ".xz")
85+
outPath := filepath.Join(targetDir, xzCutName)
86+
out, err := os.Create(outPath)
87+
if err != nil {
88+
return "", err
89+
}
90+
91+
_, err = io.Copy(out, reader)
92+
if err != nil {
93+
return "", err
94+
}
95+
96+
return outPath, nil
97+
}
98+
4599
type OsProvider interface {
46100
Fetch(destDir string) error
47101
ToVirtualMachine() (*config.VirtualMachine, error)
@@ -64,6 +118,16 @@ func NewPuipuiProvider() *PuiPuiProvider {
64118
return &PuiPuiProvider{}
65119
}
66120

121+
type FedoraProvider struct {
122+
diskImage string
123+
efiVariableStorePath string
124+
createVariableStore bool
125+
}
126+
127+
func NewFedoraProvider() *FedoraProvider {
128+
return &FedoraProvider{}
129+
}
130+
67131
func findFile(files []string, filename string) (string, error) {
68132
for _, f := range files {
69133
if filepath.Base(f) == filename {
@@ -143,6 +207,18 @@ func (puipui *PuiPuiProvider) Fetch(destDir string) error {
143207
return nil
144208
}
145209

210+
func (fedora *FedoraProvider) Fetch(destDir string) error {
211+
log.Infof("downloading fedora to %s", destDir)
212+
file, err := downloadFedora(destDir)
213+
if err != nil {
214+
return err
215+
}
216+
217+
fedora.diskImage = file
218+
219+
return nil
220+
}
221+
146222
const puipuiMemoryMiB = 1 * 1024
147223
const puipuiCPUs = 2
148224

@@ -153,6 +229,13 @@ func (puipui *PuiPuiProvider) ToVirtualMachine() (*config.VirtualMachine, error)
153229
return vm, nil
154230
}
155231

232+
func (fedora *FedoraProvider) ToVirtualMachine() (*config.VirtualMachine, error) {
233+
bootloader := config.NewEFIBootloader(fedora.efiVariableStorePath, fedora.createVariableStore)
234+
vm := config.NewVirtualMachine(puipuiCPUs, puipuiMemoryMiB, bootloader)
235+
236+
return vm, nil
237+
}
238+
156239
func (puipui *PuiPuiProvider) SSHConfig() *ssh.ClientConfig {
157240
return &ssh.ClientConfig{
158241
User: "root",
@@ -174,3 +257,26 @@ func (puipui *PuiPuiProvider) SSHAccessMethods() []SSHAccessMethod {
174257
},
175258
}
176259
}
260+
261+
func (fedora *FedoraProvider) SSHConfig() *ssh.ClientConfig {
262+
return &ssh.ClientConfig{
263+
User: "vfkituser",
264+
Auth: []ssh.AuthMethod{ssh.Password("vfkittest")},
265+
// #nosec 106 -- the host SSH key of the VM will change each time it boots
266+
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
267+
}
268+
269+
}
270+
271+
func (fedora *FedoraProvider) SSHAccessMethods() []SSHAccessMethod {
272+
return []SSHAccessMethod{
273+
{
274+
network: "tcp",
275+
port: 22,
276+
},
277+
{
278+
network: "vsock",
279+
port: 2222,
280+
},
281+
}
282+
}

test/vm_helpers.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,12 @@ func (vm *testVM) Start(t *testing.T) {
215215
}
216216

217217
func (vm *testVM) Stop(t *testing.T) {
218-
vm.SSHRun(t, "poweroff")
218+
switch vm.provider.(type) {
219+
case *FedoraProvider:
220+
vm.SSHRun(t, "sudo shutdown now")
221+
default:
222+
vm.SSHRun(t, "poweroff")
223+
}
219224
vm.vfkitCmd.Wait(t)
220225
}
221226

test/vm_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,63 @@ func checkPCIDevice(t *testing.T, vm *testVM, vendorID, deviceID int) {
341341
require.NoError(t, err)
342342
require.Regexp(t, re, string(lspci))
343343
}
344+
345+
func TestCloudInit(t *testing.T) {
346+
if err := macOSAvailable(13); err != nil {
347+
t.Log("Skipping TestCloudInit test")
348+
return
349+
}
350+
fedoraProvider := NewFedoraProvider()
351+
log.Info("fetching os image")
352+
tempDir := t.TempDir()
353+
err := fedoraProvider.Fetch(tempDir)
354+
require.NoError(t, err)
355+
356+
// set efi bootloader
357+
fedoraProvider.efiVariableStorePath = "efi-variable-store"
358+
fedoraProvider.createVariableStore = true
359+
360+
vm := NewTestVM(t, fedoraProvider)
361+
defer vm.Close(t)
362+
require.NotNil(t, vm)
363+
364+
vm.AddSSH(t, "tcp")
365+
366+
// add vm image
367+
dev1, err := config.VirtioBlkNew(fedoraProvider.diskImage)
368+
require.NoError(t, err)
369+
vm.AddDevice(t, dev1)
370+
log.Infof("shared disk: %s - fedora", dev1.DevName)
371+
372+
/* add cloud init config by using a premade ISO image
373+
seed.img is an ISO image containing the user-data and meta-data file needed to configure the VM by cloud-init.
374+
meta-data is an empty file
375+
user-data has info about a new user that will be used to verify if the configuration has been applied. Its content is
376+
----
377+
#cloud-config
378+
users:
379+
- name: vfkituser
380+
sudo: ALL=(ALL) NOPASSWD:ALL
381+
shell: /bin/bash
382+
groups: users
383+
plain_text_passwd: vfkittest
384+
lock_passwd: false
385+
ssh_pwauth: true
386+
chpasswd: { expire: false }
387+
*/
388+
dev, err := config.VirtioBlkNew("assets/seed.img")
389+
require.NoError(t, err)
390+
vm.AddDevice(t, dev)
391+
log.Infof("shared disk: %s - cloud-init", dev.DevName)
392+
393+
vm.Start(t)
394+
vm.WaitForSSH(t)
395+
396+
data, err := vm.SSHCombinedOutput(t, "whoami")
397+
require.NoError(t, err)
398+
log.Infof("executed whoami - output: %s", string(data))
399+
require.Equal(t, "vfkituser\n", string(data))
400+
401+
log.Info("stopping vm")
402+
vm.Stop(t)
403+
}

0 commit comments

Comments
 (0)