Skip to content

Commit 4873447

Browse files
stuggiclaude
andcommitted
Add conversion webhook kuttl test (Phase-1 milestone gate)
The service namespace rewrite (spec.conversion.webhook.clientConfig.service. namespace) is co-located with the annotation rewrite in the shared rewriteCRDNamespaces helper — no separate pass needed. This commit adds the definitive joint acceptance gate for the conversion webhook trio. kuttl test structure (ctlplane-conversion-webhook/): 01-deploy-openstack.yaml — deploy base OpenStackControlPlane 01-assert-deploy-openstack.yaml — symlink to common/assert-sample-deployment.yaml 02-assert-crd-namespaces.yaml — assert inject-ca-from annotation namespace, conversion service namespace, and non-empty caBundle (timeout 120s) 03-assert-conversion-roundtrip.yaml — milestone gate: discovers all service operator CRDs with v1beta2 support, tests each one by reading at v1beta2 (storage) and v1beta1 (fires conversion webhook), asserts spec equality across versions (timeout 120s) CONDITIONAL: 03-assert-conversion-roundtrip.yaml discovers service operator CRDs with both v1beta1 and v1beta2 served, tests all with deployed CR instances; skips gracefully if none found. This allows landing the test before any service operator adds v1beta2 support; test activates automatically and scales as service operators (keystone, nova, glance, etc.) add v1beta2. INSTALL_NS (operator namespace, source of CRD annotation/service.namespace) is derived from the OpenStack CR, not from $NAMESPACE (the kuttl test namespace where service API CRs are deployed). CR_NS=$NAMESPACE is used for CR lookups and raw API paths. Jira: OSPRH-30209 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Martin Schuppert <mschuppert@redhat.com>
1 parent e620d61 commit 4873447

6 files changed

Lines changed: 114 additions & 17 deletions

File tree

‎test/kuttl/tests/ctlplane-conversion-webhook/00-assert-deploy-openstack.yaml‎

Lines changed: 0 additions & 8 deletions
This file was deleted.

‎test/kuttl/tests/ctlplane-conversion-webhook/00-deploy-openstack.yaml‎

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../common/assert-sample-deployment.yaml
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: kuttl.dev/v1beta1
2+
kind: TestStep
3+
commands:
4+
- script: |
5+
oc kustomize ../../../../config/samples/base/openstackcontrolplane | oc apply -n $NAMESPACE -f -

test/kuttl/tests/ctlplane-conversion-webhook/01-assert-crd-namespaces.yaml renamed to test/kuttl/tests/ctlplane-conversion-webhook/02-assert-crd-namespaces.yaml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,25 @@ timeout: 120
55
# Assert that the keystone CRD installed by openstack-operator carries the correct
66
# install-namespace in both the cert-manager annotation and the conversion webhook
77
# service reference, and that cert-manager has populated the caBundle.
8+
#
9+
# INSTALL_NS: the namespace the openstack-operator controller runs in (where it installs
10+
# CRDs and writes the annotation/service.namespace). Derived from the operator deployment,
11+
# NOT from $NAMESPACE which is the kuttl test namespace (openstack-kuttl-tests).
812
commands:
913
- script: |
1014
set -e
11-
INSTALL_NS="${NAMESPACE:-openstack-operators}"
1215
CRD=keystoneapis.keystone.openstack.org
16+
INSTALL_NS=$(oc get deployment -A -l control-plane=controller-manager,app.kubernetes.io/name=openstack-operator -o jsonpath='{.items[0].metadata.namespace}')
17+
if [ -z "$INSTALL_NS" ]; then
18+
echo "FAIL: openstack-operator deployment not found — is the operator deployed?"; exit 1
19+
fi
1320
1421
# 1. inject-ca-from annotation namespace == install namespace (OSPRH-30208)
15-
ANNO=$(oc get crd $CRD -o jsonpath='{.metadata.annotations.cert-manager\.io/inject-ca-from}')
22+
ANNO=$(oc get crd "$CRD" -o jsonpath='{.metadata.annotations.cert-manager\.io/inject-ca-from}')
23+
if [ -z "$ANNO" ]; then
24+
echo "FAIL: cert-manager.io/inject-ca-from annotation is missing on $CRD"
25+
exit 1
26+
fi
1627
ANNO_NS="${ANNO%%/*}"
1728
if [ "$ANNO_NS" != "$INSTALL_NS" ]; then
1829
echo "FAIL: inject-ca-from annotation namespace '$ANNO_NS' != install namespace '$INSTALL_NS'"
@@ -21,15 +32,15 @@ commands:
2132
echo "OK: inject-ca-from annotation namespace = $ANNO_NS"
2233
2334
# 2. conversion webhook service namespace == install namespace (OSPRH-30209)
24-
SVC_NS=$(oc get crd $CRD -o jsonpath='{.spec.conversion.webhook.clientConfig.service.namespace}')
35+
SVC_NS=$(oc get crd "$CRD" -o jsonpath='{.spec.conversion.webhook.clientConfig.service.namespace}')
2536
if [ "$SVC_NS" != "$INSTALL_NS" ]; then
2637
echo "FAIL: conversion service namespace '$SVC_NS' != install namespace '$INSTALL_NS'"
2738
exit 1
2839
fi
2940
echo "OK: conversion service namespace = $SVC_NS"
3041
3142
# 3. caBundle non-empty — cert-manager injected (OSPRH-30208, async)
32-
CA=$(oc get crd $CRD -o jsonpath='{.spec.conversion.webhook.clientConfig.caBundle}')
43+
CA=$(oc get crd "$CRD" -o jsonpath='{.spec.conversion.webhook.clientConfig.caBundle}')
3344
if [ -z "$CA" ]; then
3445
echo "FAIL: caBundle is empty — cert-manager has not injected yet"
3546
exit 1
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
apiVersion: kuttl.dev/v1beta1
2+
kind: TestAssert
3+
# Phase-1 milestone gate for conversion webhook infrastructure.
4+
# Step 02 already verified the CRD field namespaces and caBundle; this step
5+
# focuses on the conversion round-trip: read a service API CR at each served
6+
# version and assert key fields are equal (proves the webhook fires correctly).
7+
#
8+
# CONDITIONAL: Discovers any service operator CRD with v1beta2 support; skips
9+
# gracefully if none found yet. Test activates automatically once any service
10+
# operator adds v1beta2.
11+
#
12+
# Two namespaces in play:
13+
# INSTALL_NS — operator namespace where openstack-operator runs and writes
14+
# the CRD annotation/service.namespace (derived from operator deployment)
15+
# CR_NS — kuttl test namespace where service API CRs are deployed by the
16+
# OpenStackControlPlane reconciler (= $NAMESPACE set by kuttl)
17+
timeout: 120
18+
commands:
19+
- script: |
20+
set -e
21+
22+
INSTALL_NS=$(oc get deployment -A -l control-plane=controller-manager,app.kubernetes.io/name=openstack-operator -o jsonpath='{.items[0].metadata.namespace}')
23+
if [ -z "$INSTALL_NS" ]; then
24+
echo "FAIL: openstack-operator deployment not found — is the operator deployed?"; exit 1
25+
fi
26+
CR_NS="$NAMESPACE"
27+
28+
# Discover service operator CRDs with v1beta2 support
29+
# Pattern: <service>apis.<service>.openstack.org (keystoneapis, novaapis, etc.)
30+
CANDIDATE_CRDS=$(oc get crds -o json | jq -r '
31+
.items[] |
32+
select(.metadata.name | test("^[a-z]+apis\\.[a-z]+\\.openstack\\.org$")) |
33+
select(.spec.versions | map(select(.name == "v1beta1" and .served)) | length > 0) |
34+
select(.spec.versions | map(select(.name == "v1beta2" and .served)) | length > 0) |
35+
.metadata.name
36+
')
37+
38+
if [ -z "$CANDIDATE_CRDS" ]; then
39+
echo "SKIP: no service operator CRDs with v1beta2 support found yet"
40+
echo " (test will activate automatically once any service operator adds v1beta2)"
41+
exit 0
42+
fi
43+
44+
# Test all CRDs that have v1beta2 support and deployed CR instances
45+
TESTED_COUNT=0
46+
for candidate in $CANDIDATE_CRDS; do
47+
API_GROUP=$(echo "$candidate" | cut -d. -f2-)
48+
RESOURCE=$(echo "$candidate" | cut -d. -f1)
49+
CR_NAME=$(oc get "$candidate" -n "$CR_NS" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
50+
51+
if [ -z "$CR_NAME" ]; then
52+
echo "SKIP: $candidate has v1beta2, but no CR instance deployed"
53+
continue
54+
fi
55+
56+
echo "Testing conversion webhook with $candidate (CR: $CR_NAME)"
57+
58+
# Read at v1beta2 (storage version — no conversion needed; proves CRD is installed)
59+
SPEC_V2=$(oc get --raw \
60+
"/apis/$API_GROUP/v1beta2/namespaces/$CR_NS/$RESOURCE/$CR_NAME" \
61+
| jq -e -c '.spec') || {
62+
echo "FAIL: $candidate v1beta2 read failed or spec missing (CRD install/conversion issue)"
63+
exit 1
64+
}
65+
echo "OK: $candidate v1beta2 read succeeded"
66+
67+
# Read at v1beta1 (legacy version — fires conversion webhook: v1beta2 storage -> v1beta1)
68+
SPEC_V1=$(oc get --raw \
69+
"/apis/$API_GROUP/v1beta1/namespaces/$CR_NS/$RESOURCE/$CR_NAME" \
70+
| jq -e -c '.spec') || {
71+
echo "FAIL: $candidate v1beta1 read failed or spec missing (conversion webhook not reachable)"
72+
exit 1
73+
}
74+
echo "OK: $candidate v1beta1 read succeeded"
75+
76+
# Round-trip equality: same spec across versions proves no data loss in conversion
77+
# Note: field order may differ, but jq -c normalizes to compact JSON for comparison
78+
if [ "$SPEC_V1" != "$SPEC_V2" ]; then
79+
echo "FAIL: $candidate round-trip mismatch between v1beta1 and v1beta2 spec"
80+
echo " v1beta2: $SPEC_V2"
81+
echo " v1beta1: $SPEC_V1"
82+
exit 1
83+
fi
84+
echo "OK: $candidate round-trip equality confirmed"
85+
TESTED_COUNT=$((TESTED_COUNT + 1))
86+
done
87+
88+
if [ "$TESTED_COUNT" -eq 0 ]; then
89+
echo "SKIP: found CRDs with v1beta2 support, but no CR instances deployed yet"
90+
exit 0
91+
fi
92+
93+
echo "PASS: conversion webhook Phase-1 milestone gate complete ($TESTED_COUNT service(s) tested)"

0 commit comments

Comments
 (0)