Repository navigation
test(e2e): lightweight E2E cluster (kind / Talos-in-Docker) for diff-scoped PR runs #3238
Description
Activity
- addedkind/featureCategorizes issue or PR as related to a new featureCategorizes issue or PR as related to a new feature
on Jul 8, 2026 - addedarea/ciIssues or PRs related to CI workflows, GitHub Actions, automationIssues or PRs related to CI workflows, GitHub Actions, automationarea/testingIssues or PRs related to testing (e2e, bats, unit tests)Issues or PRs related to testing (e2e, bats, unit tests)
on Jul 8, 2026 Survival audit — all 22
hack/e2e-apps/*.batson kind / Talos-in-DockerVerdict Count Files SURVIVES (only needs a default StorageClass) 15 bucket, clickhouse, etcd, external-dns, foundationdb, harbor, kafka, kuberture, mariadb, mongodb, openbao, postgres, qdrant, redis, serviceexposure CONDITIONAL (survives iff CNI=Cilium; no virt/kernel deps) 2 gateway, securitygroup NEEDS-REAL-CLUSTER (nested KVM virt and/or DRBD-replicated storage) 5 vminstance, kubernetes-latest, kubernetes-previous, kubernetes-oidc-system, kubernetes-oidc-customconfig SURVIVES (15): all set
storageClass: ""(or need no storage) and provision single replicas — no DRBD/replication. kind'slocal-pathor any Talos-in-Docker default SC satisfies them.bucket/harboradditionally need the SeaweedFS+COSI platform stack (a provisioning prerequisite, not a kernel blocker).serviceexposuredeliberately uses theexternalIPsbackend to avoid any MetalLB/Cilium pool or L2 reachability (serviceexposure.bats:5–11).CONDITIONAL (2) — no nested-virt/kernel deps, but require Cilium as CNI (fine on Talos-in-Docker w/ Cilium, fail on vanilla kindnet):
gateway— GatewayClasscilium,cilium-gateway-*LB Service,Programmedneeds Cilium dataplane + LB IPAM.securitygroup— round-trips SecurityGroup ↔cilium.io/v2CiliumNetworkPolicy CRD.
NEEDS-REAL-CLUSTER (5) — hard blockers, nested KVM virt and/or DRBD:
vminstance— KubeVirt VM/VMI + CDI DataVolume +storageClass: replicated.kubernetes-latest/-previous— Kamaji CP + CAPK KubeVirt worker VMs + CDI + DRBD-replicated worker disks (run-kubernetes.sh:68,402-438). (latest's second test,verify_storageclass_fallback_default, renders against the mgmt cluster and would survive alone.)kubernetes-oidc-system/-customconfig— tenant k8s withstorageClass: replicated.
Recommendation
A lightweight lane (Talos-in-Docker + stock Cilium CNI + a default StorageClass) can run 17 of 22 tests — the 15 SURVIVES plus the 2 CONDITIONAL. Only 5 (
vminstance+ the fourkubernetes-*) truly need a bare-metal-like cluster, each for the same reason: nested KVM for KubeVirt VMs + DRBD storage. Suggested split: run the 17 non-virt tests on the fast containerized lane for PR feedback; keep the 5 virt/DRBD tests on the existing nested-QEMU lane. Reclaims most of the suite's wall-clock with no loss of coverage that actually depends on the heavyweight environment.Follow-up: this is an
enabledPackagesprofile + a storage/CNI swap, not a new bundleCorrecting my earlier "someone has to author a bundle" — cozystack already has the granular mechanism. All four bundles (
system/iaas/paas/naas) have per-bundleenabledflags, and on top of thatbundles.enabledPackages/bundles.disabledPackagesgive per-package control — which is exactly whatcozypkg add/deldrives. So the lightweight path is:core-only platform (all bundles
enabled: false) →cozypkg addonly the packages that survive.The catch: core-only has no CNI and no StorageClass
Both normally arrive via the
systembundle:cozystack.networking= kube-ovn + cilium (OVS kernel module)cozystack.linstor= DRBD + ZFS
Neither runs in talos-in-docker. So the profile must substitute two things — this is the only part that isn't just
cozypkg add:- Default StorageClass → local-path/hostpath (mandatory: the 15 survivors all use
storageClass: ""= default SC and need some PV). - CNI → cilium-only (drop kube-ovn/OVS). Cilium runs on the host kernel (eBPF), and this is also what makes the 2 CONDITIONAL tests (
gateway,securitygroup) pass. Note: today's networking package only shipskubeovn-ciliumandkubeovn-cilium-genericvariants — a cilium-only variant is the one genuinely new piece of work here.
Result
With storage + CNI substituted, the e2e values just set
bundles.enabledPackagesto the surviving app set:- Enable: the 15 SURVIVES packages (+
gateway,securitygrouponce cilium-only is in). - Never enable the
iaasbundle → KubeVirt/CDI + the CAPK-VM tenant clusters stay off, so the 5 NEEDS-REAL tests (vminstance+ the fourkubernetes-*) drop out for free — no nested KVM, no DRBD.
Net: 17/22 tests on a container cluster, driven by an
enabledPackageslist + a local-path default SC + a cilium-only networking variant. No new bundle, no per-component conditional logic.- addedtriage/acceptedIndicates an issue is ready to be actively worked onIndicates an issue is ready to be actively worked on
on Aug 12, 2026 - addedpriority/important-longtermImportant over the long term, but may not be staffed and/or may need multiple releases to completeImportant over the long term, but may not be staffed and/or may need multiple releases to complete
on Sep 23, 2026 done via #4020
- added a commit that references this issue
on Oct 7, 2026
Problem
Every PR E2E run provisions a full Talos cluster of three nested QEMU/KVM VMs inside a privileged Docker container, on the largest VM runner class. This is the heaviest and slowest part of PR CI, and it is paid in full even for a PR that only touches a single app and needs nothing from the storage / networking / virtualization stack that the heavy cluster exists to exercise.
Sibling epic #2847 (Track A) makes the install lighter — a
test-minimalbundle +hack/select-install.shinstall only the packages a PR's diff needs. This issue tackles the orthogonal axis #2847 explicitly left out of scope: the cluster the install runs on. The two compose: a minimal install on a minimal cluster.Current heavy approach (evidence)
.github/workflows/pull-requests.yaml— thee2ejob runs on the 24 vCPU / 96 GB VM runner class with a 180-minute timeout.Makefile: prepare-env→packages/core/testingapply+prepare-cluster.packages/core/testing/Makefile: apply—docker run --privilegedlaunches thee2e-sandboxcontainer.hack/e2e-prepare-cluster.bats— inside that container it boots 3 QEMU VMs (-smp 8 -m 24576, i.e. 8 vCPU + 24 GB each), each with a 50 GB system disk cloned from the Talosnocloud-amd64.rawimage plus a 200 GB data disk, wires a bridge + masquerade, generates Talos machine-config, and waits on the Talos API before install.build-talosjob builds and uploads the nocloud disk (make -C packages/core/talos talos-nocloud) on every PR to feed the above.Net: full Talos + nested virtualization + a per-PR disk build, for all PRs.
Proposed lighter approach
Add a lightweight cluster provisioner as an alternative to the QEMU-VM path in
hack/e2e-prepare-cluster.bats, selected per-PR (e.g. reuse the existing diff/TIA signal from #2559 / #2847 so only diffs that don't touch storage, CNI dataplane, or virtualization opt into it). Two candidate backends:paas-hostedbundle.talosctl cluster create --provisioner docker) — keeps Talos as the distro (so machine-config, the cozystack API, and the platform bundle behave as in production) while dropping QEMU and the raw-disk build. Higher fidelity than kind; still no kernel-module / KVM surface.Either way the heavy Talos-on-QEMU path stays as the default and is always used for storage / networking / virtualization diffs and for every release run.
Constraints / non-goals
system/linstor,system/kubeovn/CNI, orapps/virtual-machineetc. must fall back to full Talos. Release E2E always runs full Talos.Open questions
e2e-tia/select-e2e.shdiff classification, or add a distincte2e-light-clusteropt-in label?hack/e2e-apps/*.batsfor module/VM/dataplane assumptions.bundles.testMinimal, [Epic] Diff-driven & scenario-based E2E testing for Cozystack #2847) behave on a containerized cluster — any packages in the minimal baseline that still assume Talos primitives?Relation to other work
build-talosnocloud-disk dependency.