Conversation
|
Hi @qinqon. Thanks for your PR. I'm waiting for a coreos member to verify that this patch is reasonable to test. If it is, they should reply with Tip We noticed you've done this a few times! Consider joining the org to skip this step and gain Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
52d3ba6 to
9eedd3d
Compare
There was a problem hiding this comment.
Code Review
This pull request integrates KubeVirt as a new platform into the mantle/kola testing framework, enabling VM lifecycle management and SSH port forwarding for KubeVirt-based tests. The changes include adding KubeVirt-specific options, API implementations, and machine/cluster logic. Review feedback highlights several areas for improvement: the VM deletion polling loop should specifically check for NotFound errors to prevent premature termination, rand.Read failures for VM name generation should panic to ensure unique names, and there are opportunities to refactor duplicated code for handling network data and KubeVirt metadata verification to improve conciseness and maintainability. Additionally, the Kubernetes config loading logic could be simplified for better readability.
| if err != nil { | ||
| return true, nil // Gone | ||
| } |
There was a problem hiding this comment.
The current implementation incorrectly assumes that any error from a.client.Get means the VM is gone. This could lead to premature termination of the polling loop if a transient error occurs. You should specifically check for a NotFound error.
You'll need to add an import for k8s.io/apimachinery/pkg/api/errors as apierrors.
if err != nil {
if apierrors.IsNotFound(err) {
return true, nil // Gone
}
return false, err // Propagate other errors to stop polling.
}| if _, err := rand.Read(b); err != nil { | ||
| plog.Errorf("failed to generate a random vmname: %v", err) | ||
| } |
There was a problem hiding this comment.
If rand.Read fails, the error is logged but the function continues with a zero-filled byte slice. This will result in non-unique VM names and could cause test flakiness. Since this function doesn't return an error, it should panic on failure to prevent creating VMs with predictable names.
| if _, err := rand.Read(b); err != nil { | |
| plog.Errorf("failed to generate a random vmname: %v", err) | |
| } | |
| if _, err := rand.Read(b); err != nil { | |
| panic(fmt.Sprintf("failed to generate a random vmname: %v", err)) | |
| } |
| } else if isreg && c.Name() == "network_data.json" { | ||
| if cloudInitType != "" { | ||
| return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir) | ||
| } | ||
| v, err := os.ReadFile(filepath.Join(dir, c.Name())) | ||
| if err != nil { | ||
| return errors.Wrapf(err, "reading %s", c.Name()) | ||
| } | ||
| networkData = string(v) | ||
| cloudInitType = "configdrive" | ||
| } else if isreg && c.Name() == "network-config" { | ||
| if cloudInitType != "" { | ||
| return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir) | ||
| } | ||
| v, err := os.ReadFile(filepath.Join(dir, c.Name())) | ||
| if err != nil { | ||
| return errors.Wrapf(err, "reading %s", c.Name()) | ||
| } | ||
| networkData = string(v) | ||
| cloudInitType = "nocloud" |
There was a problem hiding this comment.
There's some code duplication in handling network_data.json and network-config. You can refactor this to make it more concise and maintainable by combining the logic into a single if block.
| } else if isreg && c.Name() == "network_data.json" { | |
| if cloudInitType != "" { | |
| return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir) | |
| } | |
| v, err := os.ReadFile(filepath.Join(dir, c.Name())) | |
| if err != nil { | |
| return errors.Wrapf(err, "reading %s", c.Name()) | |
| } | |
| networkData = string(v) | |
| cloudInitType = "configdrive" | |
| } else if isreg && c.Name() == "network-config" { | |
| if cloudInitType != "" { | |
| return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir) | |
| } | |
| v, err := os.ReadFile(filepath.Join(dir, c.Name())) | |
| if err != nil { | |
| return errors.Wrapf(err, "reading %s", c.Name()) | |
| } | |
| networkData = string(v) | |
| cloudInitType = "nocloud" | |
| } else if isreg && (c.Name() == "network_data.json" || c.Name() == "network-config") { | |
| if cloudInitType != "" { | |
| return fmt.Errorf("found both network_data.json and network-config in %s; use only one", dir) | |
| } | |
| v, err := os.ReadFile(filepath.Join(dir, c.Name())) | |
| if err != nil { | |
| return errors.Wrapf(err, "reading %s", c.Name()) | |
| } | |
| networkData = string(v) | |
| if c.Name() == "network_data.json" { | |
| cloudInitType = "configdrive" | |
| } else { | |
| cloudInitType = "nocloud" | |
| } |
| func verifyKubeVirtConfigDrive(c cluster.TestCluster) { | ||
| opts := platform.MachineOptions{ | ||
| CloudInitType: "configdrive", | ||
| } | ||
| _, err := c.NewMachineWithOptions(enableMetadataService, opts) | ||
| if err != nil { | ||
| c.Fatalf("Unable to create machine: %v", err) | ||
| } | ||
| verify(c, "AFTERBURN_KUBEVIRT_INSTANCE_ID", "AFTERBURN_KUBEVIRT_HOSTNAME") | ||
| } | ||
|
|
||
| func verifyKubeVirtNoCloud(c cluster.TestCluster) { | ||
| opts := platform.MachineOptions{ | ||
| CloudInitType: "nocloud", | ||
| } | ||
| _, err := c.NewMachineWithOptions(enableMetadataService, opts) | ||
| if err != nil { | ||
| c.Fatalf("Unable to create machine: %v", err) | ||
| } | ||
| verify(c, "AFTERBURN_KUBEVIRT_INSTANCE_ID", "AFTERBURN_KUBEVIRT_HOSTNAME") | ||
| } |
There was a problem hiding this comment.
The functions verifyKubeVirtConfigDrive and verifyKubeVirtNoCloud are nearly identical. To improve maintainability and reduce code duplication, you could extract the common logic into a helper function.
For example:
func verifyKubeVirt(c cluster.TestCluster, cloudInitType string) {
opts := platform.MachineOptions{
CloudInitType: cloudInitType,
}
_, err := c.NewMachineWithOptions(enableMetadataService, opts)
if err != nil {
c.Fatalf("Unable to create machine: %v", err)
}
verify(c, "AFTERBURN_KUBEVIRT_INSTANCE_ID", "AFTERBURN_KUBEVIRT_HOSTNAME")
}
func verifyKubeVirtConfigDrive(c cluster.TestCluster) {
verifyKubeVirt(c, "configdrive")
}
func verifyKubeVirtNoCloud(c cluster.TestCluster) {
verifyKubeVirt(c, "nocloud")
}| if opts.Kubeconfig != "" { | ||
| config, err = clientcmd.BuildConfigFromFlags("", opts.Kubeconfig) | ||
| } else { | ||
| // Try in-cluster first, fall back to default kubeconfig | ||
| config, err = rest.InClusterConfig() | ||
| if err != nil { | ||
| loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() | ||
| configOverrides := &clientcmd.ConfigOverrides{} | ||
| config, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig( | ||
| loadingRules, configOverrides).ClientConfig() | ||
| } | ||
| } |
There was a problem hiding this comment.
The logic for loading the Kubernetes config can be simplified by using clientcmd.NewDefaultClientConfigLoadingRules more directly. This will make the code cleaner and more idiomatic.
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
if opts.Kubeconfig != "" {
loadingRules.ExplicitPath = opts.Kubeconfig
}
configOverrides := &clientcmd.ConfigOverrides{}
config, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
loadingRules, configOverrides).ClientConfig()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>
Add vendored dependencies for the new KubeVirt kola platform: - kubevirt.io/api v1.8.0 - sigs.k8s.io/controller-runtime v0.23.3 - k8s.io/client-go v0.35.3 - k8s.io/apimachinery v0.35.3 - k8s.io/api v0.35.3 - github.com/gorilla/websocket v1.5.4 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9eedd3d to
65687fd
Compare
|
Let's use #4508 to track this. Parent issue in FCOS: coreos/fedora-coreos-tracker#1432 |
Add a workflow that builds a Fedora CoreOS KubeVirt OCI archive image using coreos-assembler. Runs on pull requests and can be triggered manually via workflow_dispatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cosa init fails when the working directory is not empty. Use a separate temp directory for the cosa build and copy artifacts back for upload. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
PR needs rebase. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
Summary
kubevirtplatform to kola for testing CoreOS on KubeVirt VMsDetails
Platform implementation
sigs.k8s.io/controller-runtimeclient +kubevirt.io/apitypes (lightweight alternative tokubevirt.io/client-go)virtctlbinary dependency)containerDisksupport for FCOS images (cosa already builds these)CloudInitTypeandNetworkDataviaMachineOptionsCloses #4508
External test framework
network_data.jsonin test dir → ConfigDrive cloud-init type (OpenStack convention)network-configin test dir → NoCloud cloud-init type (cloud-init convention)New CLI flags
New built-in tests
fcos.metadata.kubevirt.configdrive— verify afterburn metadata with ConfigDrivefcos.metadata.kubevirt.nocloud— verify afterburn metadata with NoCloudTest plan
go build ./mantle/cmd/kola/...compiles cleango vetpasseskola spawnagainst a kind cluster with KubeVirt — VM boots, SSH works through WebSocket tunnelkola run fcos.metadata.kubevirt.configdrive— PASSkola run fcos.metadata.kubevirt.nocloudnetwork_data.jsonfilenetwork-configfilego mod vendor)TODO
We may need a mechanism to specify kubevirt VMs inetrfaces and network since that may be needed to propertly tests afterburn network data support.
🤖 Generated with Claude Code