test(sdn): drop the serviceexposure chainsaw suite left after the feature removal (#3336)

## What this PR does

Removes the dead `serviceexposure` chainsaw suite and its wiring. Commit
3dbc7d366 dropped ExposureClass/ServiceExposure (api/network, the
controller, and the `network.cozystack.io` CRDs shipped in
`cozystack-controller/definitions`) in favor of the native LoadBalancer
path, but the chainsaw port that landed the next day (dd2f89d20) brought
the suite along, so it tests an API that no longer exists.

Since then every full-suite E2E run — any PR touching core paths, and
every release cut — fails `serviceexposure-delete` and
`serviceexposure-externalips` with `no matches for
network.cozystack.io/v1alpha1` (see the crust-gather snapshot of e.g.
run 29561399440 on #3333: the network CRDs are absent while sibling CRDs
from the same `definitions/` dir, like
`tenantgateways.gateway.cozystack.io`, are present, confirming the chart
applied and the API is simply gone).

Alongside the suite dir this drops the `suite_to_source` case and its
core-platform bats test in `hack/select-install*`, the now-unreachable
`-` mapping mechanism itself (no suite returns it anymore and its only
executable coverage was the removed bats test; re-adding the three lines
with the next core-platform-owned suite is cheaper than keeping an
untestable dead branch), and the suite list entry in
`hack/e2e-chainsaw/README.md`. The `securitygroup` suite from the same
port is alive and green and is untouched. If the native LoadBalancer
replacement needs E2E coverage, that is a new suite against the new API,
not a revival of this one.

Verified: `hack/select-install.sh --validate` passes (97 sources),
`select-install_test.bats` 17/17, `select-e2e_test.bats` 13/13.

## Release note

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Tightened installation suite-to-PackageSource validation:
`serviceexposure` no longer has special handling, and mappings to `-`
are now treated as invalid unless the referenced PackageSource exists.

* **Tests**
* Removed Chainsaw E2E fixture coverage and manifests for
`serviceexposure`, and updated the listed E2E suites accordingly.
* Updated installation mapping tests to match the stricter validation
and added a negative validation case for a missing mapped PackageSource.

* **Documentation**
* Updated contributing guidance to reference the
`networking.publishing.externalIPs` → `publishing.loadBalancerClass`
migration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
IvanHunters authored and GitHub committed 2026-07-18 16:36:02 +03:00
commit 28a59daa29
6 files changed
+31 -178

No files matched your search

+1 -1
View File
@@ -195,7 +195,7 @@ The provider is hand-written: every Kind carries a hand-maintained Terraform sch
- Add a required key to `packages/core/installer/values.yaml`, or rename one the role passes (`cozystackOperator.variant`, `cozystack.apiServerHost`, `cozystack.apiServerPort`) → the chart values block in the role's `main.yml` task, and its `defaults/main.yml`.
- Change the `cozy-system` namespace or the PSA pre-install hook contract → the role carries namespace-adoption logic written against the current behaviour of `packages/core/installer/templates/`.
- Add or rename a platform variant → the variant list is hardcoded in Go, in `cmd/cozystack-operator/main.go`; over there it is `cozystack_platform_variant` in `defaults/main.yml` and the README table.
- Change a `packages/core/platform/values.yaml` key the role sets — `networking.podCIDR`, `networking.podGateway`, `networking.serviceCIDR`, `networking.joinCIDR`, `publishing.host`, `publishing.apiServerEndpoint`, `publishing.externalIPs` → the role's platform-package template. The `externalIPs` to `exposureClass` migration in particular needs a coordinated PR there.
- Change a `packages/core/platform/values.yaml` key the role sets — `networking.podCIDR`, `networking.podGateway`, `networking.serviceCIDR`, `networking.joinCIDR`, `publishing.host`, `publishing.apiServerEndpoint`, `publishing.externalIPs` → the role's platform-package template. The `externalIPs` to `publishing.loadBalancerClass` migration in particular needs a coordinated PR there.
- Rename an object the role waits on — `deployment/cozystack-operator`, `crd/packages.cozystack.io`, the root `Tenant` — or change the `<packagesource>.<package>` naming scheme of the Package it applies → the role's tasks and its own CI assertions.
- Change node prerequisites in `hack/e2e-prepare-cluster.bats` — the kernel modules a node must load, the containerd settings, the LVM `global_filter` → those are mirrored by hand into that repo's `examples/ubuntu`, `examples/rhel` and `examples/suse` prepare playbooks. Its CI tests some of them, but never against this repo, so a kernel module added here and not there surfaces as a broken storage layer on a generic cluster.
- Change what Cozystack requires from the Kubernetes distribution (CNI, kube-proxy, cluster domain, max pods) → the k3s flags, which live only in that repo's inventories.
+1 -1
View File
@@ -18,7 +18,7 @@ hack/e2e-chainsaw/
└── ...
```
Suites: `postgres`, `bucket`, `mariadb`, `mongodb`, `redis`, `qdrant`, `clickhouse`, `kafka`, `etcd`, `openbao`, `harbor`, `foundationdb`, `external-dns`, `kuberture`, `vminstance`, `gateway`, `kubernetes-latest`, `kubernetes-previous`, `kubernetes-oidc-system`, `kubernetes-oidc-customconfig`, `securitygroup`, `serviceexposure`.
Suites: `postgres`, `bucket`, `mariadb`, `mongodb`, `redis`, `qdrant`, `clickhouse`, `kafka`, `etcd`, `openbao`, `harbor`, `foundationdb`, `external-dns`, `kuberture`, `vminstance`, `gateway`, `kubernetes-latest`, `kubernetes-previous`, `kubernetes-oidc-system`, `kubernetes-oidc-customconfig`, `securitygroup`.
## What Chainsaw buys over the BATS suite
@@ -1,120 +0,0 @@
# Chainsaw port of hack/e2e-apps/serviceexposure.bats.
#
# Smoke test for the ServiceExposure controller (network.cozystack.io): the
# CRDs are installed, the controller resolves a ServiceExposure to its
# ExposureClass, renders NO foreign backend CRs for the externalIPs backend,
# and reports status. See serviceexposure.yaml for why externalIPs.
#
# Both Tests reuse the same fixture names — safe under the global
# execution.parallel: 1 (the first Test's auto-cleanup completes before the
# second starts).
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: serviceexposure-externalips
spec:
catch:
- events: {}
- describe:
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
name: se-e2e
- get:
apiVersion: network.cozystack.io/v1alpha1
kind: ExposureClass
name: se-e2e-externalips
- podLogs:
namespace: cozy-system
selector: app=cozystack-controller
tail: 100
steps:
- name: create-fixtures
try:
- apply:
file: serviceexposure.yaml
- name: resolved-backend
try:
# The controller stamps status.resolvedBackend once it has reconciled.
- assert:
timeout: 2m
resource:
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
metadata:
name: se-e2e
status:
resolvedBackend: externalIPs
- name: no-backend-pool
try:
# externalIPs backend must not create a MetalLB pool
# (backend.PoolName = cozystack-<class>). resolvedBackend above already
# proved the reconcile ran, so a short non-existence poll is race-free —
# same ordering as the bats.
- error:
timeout: 30s
resource:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: cozystack-se-e2e-externalips
namespace: cozy-metallb
- name: ready-and-assigned-ips
try:
# The pinned externalIPs make the exposure Ready.
- assert:
timeout: 1m
resource:
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
metadata:
name: se-e2e
status:
(conditions[?type == 'Ready']):
- status: "True"
(assignedIPs[0]): 203.0.113.10
---
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: serviceexposure-delete
spec:
catch:
- events: {}
- describe:
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
name: se-e2e
- podLogs:
namespace: cozy-system
selector: app=cozystack-controller
tail: 100
steps:
- name: create-fixtures
try:
- apply:
file: serviceexposure.yaml
- assert:
timeout: 2m
resource:
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
metadata:
name: se-e2e
status:
resolvedBackend: externalIPs
- name: delete-completes-despite-finalizer
try:
# Deletion must complete despite the cleanup finalizer (nothing to GC for
# the externalIPs backend, so the finalizer is removed promptly). The
# chainsaw delete op waits for the object to be fully gone — the delete
# IS the test. 3m covers the bats' 2m delete + 1m wait-for-delete.
- delete:
timeout: 3m
ref:
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
name: se-e2e
@@ -1,37 +0,0 @@
# The externalIPs path is chosen deliberately so the test creates no
# MetalLB/Cilium pool — it cannot collide with the cluster's existing
# LoadBalancer address pools or depend on real L2 reachability
# (203.0.113.10 is TEST-NET-3), keeping it deterministic. The metallb and
# cilium rendering paths (pool + announcer CRs, garbage collection, take-over
# guard) are covered exhaustively by the Go unit tests in
# internal/controller/serviceexposure.
#
# The ExposureClass is cluster-scoped; Chainsaw tracks cluster-scoped applies
# too, so auto-cleanup reclaims all three objects in reverse apply order.
apiVersion: network.cozystack.io/v1alpha1
kind: ExposureClass
metadata:
name: se-e2e-externalips
spec:
backend: externalIPs
---
apiVersion: v1
kind: Service
metadata:
name: se-e2e-svc
spec:
type: ClusterIP
externalIPs:
- 203.0.113.10
ports:
- port: 80
targetPort: 80
---
apiVersion: network.cozystack.io/v1alpha1
kind: ServiceExposure
metadata:
name: se-e2e
spec:
serviceRef:
name: se-e2e-svc
exposureClassName: se-e2e-externalips
+4 -11
View File
@@ -24,7 +24,7 @@
# --validate: check the whole graph AND the suite mapping — every dependsOn
# target resolves to a real PackageSource, there are no dependency cycles, and
# every Chainsaw suite under <suites-dir> resolves via suite_to_source() to a
# real PackageSource or to "-". Exit non-zero on any failure. The graph lives
# real PackageSource. Exit non-zero on any failure. The graph lives
# next to the packages and cannot drift from the suite directories; the
# hand-maintained suite mapping is the only part that can, so it is guarded here.
#
@@ -59,11 +59,6 @@ fi
# suite name -> owning PackageSource. Prints:
# - a "cozystack.*" name the suite's primary install target
# - "-" the suite targets a core-platform feature that
# has no separately-enabled package (always
# present via the base install), e.g.
# serviceexposure (ExposureClass/ServiceExposure
# live in packages/core/platform)
# - nothing no mapping is known (caller fails closed)
# Most suites follow the <suite>-application convention; a bare-<suite> fallback
# and the explicit cases cover suites whose source is named differently. Kept in
@@ -82,7 +77,6 @@ suite_to_source() {
echo cozystack.kubernetes-application ; return ;;
vminstance) echo cozystack.vm-instance-application ; return ;;
securitygroup) echo cozystack.securitygroup-controller ; return ;;
serviceexposure) echo - ; return ;;
esac
for cand in "cozystack.$1-application" "cozystack.$1"; do
if echo "$NODES" | grep -Fxq "$cand"; then echo "$cand"; return; fi
@@ -149,8 +143,8 @@ if [ "$MODE" = "validate" ]; then
done
done
# 3. Suite mapping: every Chainsaw suite dir must resolve to a real source or
# "-". The suite universe is auto-discovered while suite_to_source() is
# 3. Suite mapping: every Chainsaw suite dir must resolve to a real source.
# The suite universe is auto-discovered while suite_to_source() is
# hand-maintained, so this is the part that actually drifts. A missing
# suites dir is itself a failure — otherwise a moved/misspelled path would
# make discovery yield nothing and silently pass this whole check.
@@ -163,7 +157,7 @@ if [ "$MODE" = "validate" ]; then
if [ -z "$src" ]; then
echo "select-install: suite '$suite' has no PackageSource mapping (add it to suite_to_source)" >&2
rc=1
elif [ "$src" != "-" ] && ! echo "$NODES" | grep -Fxq "$src"; then
elif ! echo "$NODES" | grep -Fxq "$src"; then
echo "select-install: suite '$suite' maps to '$src', not a PackageSource in $SOURCES_DIR" >&2
rc=1
fi
@@ -181,7 +175,6 @@ for suite in $SUITES; do
[ -z "$suite" ] && continue
src="$(suite_to_source "$suite")"
case "$src" in
"-") continue ;; # core-platform feature — nothing separate to enable
"")
# Fail closed: a suite that maps to nothing must abort, not silently emit
# an empty set. Collect every bad suite so one run reports them all.
+25 -8
View File
@@ -101,14 +101,6 @@
echo "$output" | grep -wq cozystack.gateway-api-crds
}
@test "core-platform suite contributes no package and emits no warning" {
# serviceexposure is a core-platform CRD; nothing to enable, no warning.
err=$(hack/select-install.sh "serviceexposure" 2>&1 1>/dev/null)
[ -z "$err" ]
output=$(hack/select-install.sh "serviceexposure" 2>/dev/null)
[ -z "$output" ]
}
@test "validate passes on the real source graph and suite mapping" {
hack/select-install.sh --validate
}
@@ -180,6 +172,31 @@ YAML
fi
}
@test "validate detects a suite mapped to a source absent from the graph" {
# suite_to_source() hardcodes securitygroup -> cozystack.securitygroup-controller;
# a sources dir without that PackageSource must fail validation (fail closed),
# pinning that the elif validates every mapping, including hardcoded ones,
# against the graph.
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
mkdir -p "$tmp/sources" "$tmp/suites/securitygroup"
: > "$tmp/suites/securitygroup/chainsaw-test.yaml"
cat > "$tmp/sources/standalone.yaml" <<'YAML'
apiVersion: cozystack.io/v1alpha1
kind: PackageSource
metadata:
name: cozystack.standalone
spec:
variants:
- name: default
YAML
err=$(hack/select-install.sh --validate "$tmp/sources" "$tmp/suites" 2>&1 1>/dev/null) && {
echo "expected validation to fail on a mapping to a missing source" >&2
exit 1
}
echo "$err" | grep -q "maps to 'cozystack.securitygroup-controller', not a PackageSource"
}
@test "validate fails when the suites dir does not exist" {
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT