Skip to content

Commit fc2cf8e

Browse files
qinqonclaude
andcommitted
kola: add KubeVirt platform support
Add a new "kubevirt" platform to kola for testing CoreOS on KubeVirt VMs. This enables end-to-end testing of afterburn's KubeVirt metadata provider, supporting both ConfigDrive and NoCloud cloud-init types. Platform implementation: - API wrapper using controller-runtime client + kubevirt.io/api types (lightweight alternative to kubevirt.io/client-go) - SSH access via WebSocket port-forward through the KubeVirt API server (no virtctl binary dependency) - containerDisk support for FCOS images (cosa already builds these) - Per-test CloudInitType and NetworkData via MachineOptions External test framework: - Detect network_data.json in test dir -> ConfigDrive cloud-init type - Detect network-config in test dir -> NoCloud cloud-init type - File naming follows real-world conventions (OpenStack / cloud-init) - Error if both files present in the same test directory - Silently ignored on platforms that don't support network data New CLI flags: --kubevirt-kubeconfig, --kubevirt-namespace, --kubevirt-image, --kubevirt-cloud-init-type, --kubevirt-memory, --kubevirt-cpus New tests: fcos.metadata.kubevirt.configdrive fcos.metadata.kubevirt.nocloud Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 361b1af commit fc2cf8e

10 files changed

Lines changed: 917 additions & 5 deletions

File tree

mantle/cmd/kola/options.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var (
4040
kolaPlatform string
4141
kolaParallelArg string
4242
kolaArchitectures = []string{"amd64"}
43-
kolaPlatforms = []string{"aws", "azure", "do", "esx", "gcp", "openstack", "qemu", "qemu-iso"}
43+
kolaPlatforms = []string{"aws", "azure", "do", "esx", "gcp", "kubevirt", "openstack", "qemu", "qemu-iso"}
4444
kolaDistros = []string{"fcos", "rhcos", "scos"}
4545
)
4646

@@ -140,6 +140,14 @@ func init() {
140140
sv(&kola.OpenStackOptions.Domain, "openstack-domain", "", "OpenStack domain ID")
141141
sv(&kola.OpenStackOptions.FloatingIPNetwork, "openstack-floating-ip-network", "", "OpenStack network to use when creating a floating IP")
142142

143+
// kubevirt-specific options
144+
sv(&kola.KubeVirtOptions.Kubeconfig, "kubevirt-kubeconfig", "", "Path to kubeconfig (default: in-cluster or ~/.kube/config)")
145+
sv(&kola.KubeVirtOptions.Namespace, "kubevirt-namespace", "default", "Kubernetes namespace for VMs")
146+
sv(&kola.KubeVirtOptions.Image, "kubevirt-image", "", "Container disk image pull spec")
147+
sv(&kola.KubeVirtOptions.CloudInitType, "kubevirt-cloud-init-type", "configdrive", "Cloud-init type: configdrive or nocloud")
148+
sv(&kola.KubeVirtOptions.Memory, "kubevirt-memory", "2Gi", "VM memory")
149+
root.PersistentFlags().Uint32Var(&kola.KubeVirtOptions.CPUs, "kubevirt-cpus", 2, "VM CPUs")
150+
143151
// QEMU-specific options
144152
sv(&kola.QEMUOptions.Firmware, "qemu-firmware", "", "Boot firmware: bios,uefi,uefi-secure (default bios)")
145153
sv(&kola.QEMUOptions.DiskImage, "qemu-image", "", "path to CoreOS disk image")

mantle/kola/harness.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ import (
4545
doapi "github.com/coreos/coreos-assembler/mantle/platform/api/do"
4646
esxapi "github.com/coreos/coreos-assembler/mantle/platform/api/esx"
4747
gcloudapi "github.com/coreos/coreos-assembler/mantle/platform/api/gcloud"
48+
kubevirtapi "github.com/coreos/coreos-assembler/mantle/platform/api/kubevirt"
4849
openstackapi "github.com/coreos/coreos-assembler/mantle/platform/api/openstack"
4950
"github.com/coreos/coreos-assembler/mantle/platform/conf"
5051
"github.com/coreos/coreos-assembler/mantle/platform/machine/aws"
5152
"github.com/coreos/coreos-assembler/mantle/platform/machine/azure"
5253
"github.com/coreos/coreos-assembler/mantle/platform/machine/do"
5354
"github.com/coreos/coreos-assembler/mantle/platform/machine/esx"
5455
"github.com/coreos/coreos-assembler/mantle/platform/machine/gcloud"
56+
"github.com/coreos/coreos-assembler/mantle/platform/machine/kubevirt"
5557
"github.com/coreos/coreos-assembler/mantle/platform/machine/openstack"
5658
"github.com/coreos/coreos-assembler/mantle/platform/machine/qemu"
5759
"github.com/coreos/coreos-assembler/mantle/platform/machine/qemuiso"
@@ -113,6 +115,7 @@ var (
113115
DOOptions = doapi.Options{Options: &Options} // glue to set platform options from main
114116
ESXOptions = esxapi.Options{Options: &Options} // glue to set platform options from main
115117
GCPOptions = gcloudapi.Options{Options: &Options} // glue to set platform options from main
118+
KubeVirtOptions = kubevirtapi.Options{Options: &Options} // glue to set platform options from main
116119
OpenStackOptions = openstackapi.Options{Options: &Options} // glue to set platform options from main
117120
QEMUOptions = qemu.Options{Options: &Options} // glue to set platform options from main
118121
QEMUIsoOptions = qemuiso.Options{Options: &Options} // glue to set platform options from main
@@ -310,6 +313,8 @@ func NewFlight(pltfrm string) (flight platform.Flight, err error) {
310313
flight, err = esx.NewFlight(&ESXOptions)
311314
case "gcp":
312315
flight, err = gcloud.NewFlight(&GCPOptions)
316+
case "kubevirt":
317+
flight, err = kubevirt.NewFlight(&KubeVirtOptions)
313318
case "openstack":
314319
flight, err = openstack.NewFlight(&OpenStackOptions)
315320
case "qemu":
@@ -1164,7 +1169,7 @@ func runExternalTest(c cluster.TestCluster, mach platform.Machine, testNum int)
11641169
}
11651170
}
11661171

1167-
func registerExternalTest(testname, executable, dependencydir string, userdata *conf.UserData, baseMeta externalTestMeta) error {
1172+
func registerExternalTest(testname, executable, dependencydir string, userdata *conf.UserData, baseMeta externalTestMeta, networkData, cloudInitType string) error {
11681173
targetMeta, err := metadataFromTestBinary(executable)
11691174
if err != nil {
11701175
return errors.Wrapf(err, "Parsing metadata from %s", executable)
@@ -1250,6 +1255,8 @@ ExecStart=%s
12501255
InstanceType: targetMeta.InstanceType,
12511256
NonExclusive: !targetMeta.Exclusive,
12521257
Conflicts: targetMeta.Conflicts,
1258+
CloudInitType: cloudInitType,
1259+
NetworkData: networkData,
12531260

12541261
Run: func(c cluster.TestCluster) {
12551262
mach := c.Machines()[0]
@@ -1337,6 +1344,8 @@ func patternMatchesTests(pattern string, tests map[string]*register.Test) (bool,
13371344
// registerTestDir parses one test directory and registers it as a test
13381345
func registerTestDir(dir, testprefix string, children []os.DirEntry) error {
13391346
var dependencydir string
1347+
var networkData string
1348+
var cloudInitType string
13401349
var meta externalTestMeta
13411350
userdata := conf.EmptyIgnition()
13421351
executables := []string{}
@@ -1381,6 +1390,26 @@ func registerTestDir(dir, testprefix string, children []os.DirEntry) error {
13811390
if err := dec.Decode(&meta); err != nil {
13821391
return errors.Wrapf(err, "parsing %s", fpath)
13831392
}
1393+
} else if isreg && c.Name() == "network_data.json" {
1394+
if cloudInitType != "" {
1395+
return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir)
1396+
}
1397+
v, err := os.ReadFile(filepath.Join(dir, c.Name()))
1398+
if err != nil {
1399+
return errors.Wrapf(err, "reading %s", c.Name())
1400+
}
1401+
networkData = string(v)
1402+
cloudInitType = "configdrive"
1403+
} else if isreg && c.Name() == "network-config" {
1404+
if cloudInitType != "" {
1405+
return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir)
1406+
}
1407+
v, err := os.ReadFile(filepath.Join(dir, c.Name()))
1408+
if err != nil {
1409+
return errors.Wrapf(err, "reading %s", c.Name())
1410+
}
1411+
networkData = string(v)
1412+
cloudInitType = "nocloud"
13841413
} else if c.IsDir() && c.Name() == kolaExtBinDataName {
13851414
dependencydir = filepath.Join(dir, c.Name())
13861415
} else if c.Mode()&os.ModeSymlink != 0 && c.Name() == kolaExtBinDataName {
@@ -1427,7 +1456,7 @@ func registerTestDir(dir, testprefix string, children []os.DirEntry) error {
14271456
continue
14281457
}
14291458

1430-
err := registerExternalTest(testname, executable, dependencydir, userdata, meta)
1459+
err := registerExternalTest(testname, executable, dependencydir, userdata, meta, networkData, cloudInitType)
14311460
if err != nil {
14321461
return err
14331462
}
@@ -1870,6 +1899,8 @@ func runTest(h *harness.H, t *register.Test, pltfrm string, flight platform.Flig
18701899
AppendFirstbootKernelArgs: t.AppendFirstbootKernelArgs,
18711900
SkipStartMachine: true,
18721901
InstanceType: t.InstanceType,
1902+
CloudInitType: t.CloudInitType,
1903+
NetworkData: t.NetworkData,
18731904
}
18741905

18751906
if testSecureBoot(t) {

mantle/kola/register/register.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ type Test struct {
128128
// If provided, this test will be run on the target instance type.
129129
// This overrides the instance type set with `kola run`
130130
InstanceType string
131+
132+
// CloudInitType specifies the cloud-init volume type (kubevirt-only):
133+
// "configdrive" or "nocloud". Auto-detected from network data filename:
134+
// network_data.json -> configdrive, network-config -> nocloud.
135+
CloudInitType string
136+
137+
// NetworkData is network configuration passed to the cloud-init volume
138+
// (kubevirt-only). Format depends on CloudInitType.
139+
NetworkData string
131140
}
132141

133142
// Registered tests that run as part of `kola run` live here. Mapping of names

mantle/kola/tests/metadata/contents.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ import (
1919

2020
"github.com/coreos/coreos-assembler/mantle/kola/cluster"
2121
"github.com/coreos/coreos-assembler/mantle/kola/register"
22+
"github.com/coreos/coreos-assembler/mantle/platform"
2223
"github.com/coreos/coreos-assembler/mantle/platform/conf"
2324
)
2425

25-
func init() {
26-
enableMetadataService := conf.Ignition(`{
26+
var enableMetadataService = conf.Ignition(`{
2727
"ignition": {"version": "3.0.0"},
2828
"systemd": {
2929
"units": [{
@@ -37,6 +37,8 @@ func init() {
3737
}
3838
}`)
3939

40+
func init() {
41+
4042
register.RegisterTest(&register.Test{
4143
Name: "fcos.metadata.aws",
4244
Description: "Verify the metadata on AWS.",
@@ -56,6 +58,24 @@ func init() {
5658
UserData: enableMetadataService,
5759
Distros: []string{"fcos"},
5860
})
61+
62+
register.RegisterTest(&register.Test{
63+
Name: "fcos.metadata.kubevirt.configdrive",
64+
Description: "Verify afterburn metadata on KubeVirt with ConfigDrive.",
65+
Run: verifyKubeVirtConfigDrive,
66+
ClusterSize: 0,
67+
Platforms: []string{"kubevirt"},
68+
Distros: []string{"fcos"},
69+
})
70+
71+
register.RegisterTest(&register.Test{
72+
Name: "fcos.metadata.kubevirt.nocloud",
73+
Description: "Verify afterburn metadata on KubeVirt with NoCloud.",
74+
Run: verifyKubeVirtNoCloud,
75+
ClusterSize: 0,
76+
Platforms: []string{"kubevirt"},
77+
Distros: []string{"fcos"},
78+
})
5979
}
6080

6181
func verifyAWS(c cluster.TestCluster) {
@@ -68,6 +88,28 @@ func verifyAzure(c cluster.TestCluster) {
6888
// which is required for AFTERBURN_AZURE_IPV4_VIRTUAL to be present
6989
}
7090

91+
func verifyKubeVirtConfigDrive(c cluster.TestCluster) {
92+
opts := platform.MachineOptions{
93+
CloudInitType: "configdrive",
94+
}
95+
_, err := c.NewMachineWithOptions(enableMetadataService, opts)
96+
if err != nil {
97+
c.Fatalf("Unable to create machine: %v", err)
98+
}
99+
verify(c, "AFTERBURN_KUBEVIRT_INSTANCE_ID", "AFTERBURN_KUBEVIRT_HOSTNAME")
100+
}
101+
102+
func verifyKubeVirtNoCloud(c cluster.TestCluster) {
103+
opts := platform.MachineOptions{
104+
CloudInitType: "nocloud",
105+
}
106+
_, err := c.NewMachineWithOptions(enableMetadataService, opts)
107+
if err != nil {
108+
c.Fatalf("Unable to create machine: %v", err)
109+
}
110+
verify(c, "AFTERBURN_KUBEVIRT_INSTANCE_ID", "AFTERBURN_KUBEVIRT_HOSTNAME")
111+
}
112+
71113
func verify(c cluster.TestCluster, keys ...string) {
72114
m := c.Machines()[0]
73115

0 commit comments

Comments
 (0)