Skip to content

Commit 6a1b56f

Browse files
test(kubernetes): cover LINSTOR StorageClass propagation
- csi_test.yaml: assert the fallback storageClasses map yields a default "replicated" plus the non-default "kubevirt" alias, and that the alias tracks the storageClass selector. - kubevirt-csi-node: add a Makefile test target (otherwise the package is silently skipped by the unit-test runner) and storageclasses_test covering per-entry rendering, the single-default invariant, and the absence of any enforcement allowList in driver-config. - run-kubernetes.sh: verify in e2e that "replicated" propagates as the default kubevirt-CSI class, the "kubevirt" alias is retained, and the node-local "local" infra class is filtered out. Refs: #2094 Assisted-By: Claude <[email protected]> Co-Authored-By: Claude Opus 4.8 <[email protected]> Signed-off-by: Myasnikov Daniil <[email protected]>
1 parent 021f614 commit 6a1b56f

4 files changed

Lines changed: 189 additions & 0 deletions

File tree

‎hack/e2e-apps/run-kubernetes.sh‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,50 @@ EOF
383383
# the 5m pod-Succeeded budget when containerd's CreateContainer stalls.
384384
kubectl wait hr -n tenant-test "kubernetes-${test_name}-csi" --timeout=10m --for=condition=ready
385385

386+
# ----------------------------------------------------------------------
387+
# StorageClass propagation (issue #2094). Remote-accessible LINSTOR infra
388+
# classes propagate to the tenant under the same name; node-local classes
389+
# ("local", allowRemoteVolumeAccess=false) are filtered out; the legacy
390+
# "kubevirt" alias is retained for backward compatibility. The e2e infra
391+
# cluster ships both "replicated" (remote) and "local" (node-local).
392+
# ----------------------------------------------------------------------
393+
echo "Verifying StorageClass propagation to tenant..."
394+
timeout 2m bash -c '
395+
until kubectl --kubeconfig tenantkubeconfig-'"${test_name}"' get sc replicated >/dev/null 2>&1; do
396+
sleep 5
397+
done
398+
'
399+
400+
rep_prov=$(kubectl --kubeconfig "tenantkubeconfig-${test_name}" get sc replicated -o jsonpath='{.provisioner}')
401+
rep_infra=$(kubectl --kubeconfig "tenantkubeconfig-${test_name}" get sc replicated -o jsonpath='{.parameters.infraStorageClassName}')
402+
if [ "$rep_prov" != "csi.kubevirt.io" ] || [ "$rep_infra" != "replicated" ]; then
403+
echo "replicated SC misconfigured: provisioner=$rep_prov infraStorageClassName=$rep_infra" >&2
404+
kubectl --kubeconfig "tenantkubeconfig-${test_name}" get sc >&2
405+
exit 1
406+
fi
407+
408+
# Legacy kubevirt alias must still exist (existing PVCs depend on it).
409+
if ! kubectl --kubeconfig "tenantkubeconfig-${test_name}" get sc kubevirt >/dev/null 2>&1; then
410+
echo "legacy kubevirt StorageClass alias is missing" >&2
411+
exit 1
412+
fi
413+
414+
# Node-local "local" class must NOT be propagated (allowRemoteVolumeAccess=false).
415+
if kubectl --kubeconfig "tenantkubeconfig-${test_name}" get sc local >/dev/null 2>&1; then
416+
echo "node-local StorageClass 'local' should not be propagated to the tenant" >&2
417+
exit 1
418+
fi
419+
420+
# Exactly one default StorageClass, and it must be "replicated".
421+
default_scs=$(kubectl --kubeconfig "tenantkubeconfig-${test_name}" get sc \
422+
-o jsonpath='{range .items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")]}{.metadata.name}{"\n"}{end}')
423+
default_count=$(printf '%s' "$default_scs" | grep -c .)
424+
if [ "$default_count" -ne 1 ] || [ "$default_scs" != "replicated" ]; then
425+
echo "expected exactly one default StorageClass 'replicated', got: ${default_scs:-<none>} (count=$default_count)" >&2
426+
exit 1
427+
fi
428+
echo "StorageClass propagation OK (replicated default, kubevirt alias present, local filtered)"
429+
386430
# Clean up NFS test resources from any previous failed attempt
387431
kubectl --kubeconfig "tenantkubeconfig-${test_name}" delete pod nfs-test-pod \
388432
-n tenant-test --ignore-not-found --timeout=60s || true
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
suite: csi.yaml StorageClass propagation
2+
3+
templates:
4+
- templates/helmreleases/csi.yaml
5+
6+
values:
7+
- values/common.yaml
8+
9+
# NOTE: helm unittest has no live cluster, so `lookup` returns empty and these
10+
# tests exercise the non-destructive fallback path of the storageClasses map.
11+
# The LINSTOR provisioner / allowRemoteVolumeAccess filtering is covered by e2e.
12+
13+
tests:
14+
- it: renders the -csi HelmRelease when the tenant has etcd
15+
release:
16+
name: kubernetes-test
17+
namespace: tenant-test
18+
asserts:
19+
- hasDocuments:
20+
count: 1
21+
- isKind:
22+
of: HelmRelease
23+
- equal:
24+
path: metadata.name
25+
value: kubernetes-test-csi
26+
27+
- it: fallback yields replicated (default) and the kubevirt alias
28+
release:
29+
name: kubernetes-test
30+
namespace: tenant-test
31+
asserts:
32+
- equal:
33+
path: spec.values.storageClasses.replicated.infraStorageClass
34+
value: replicated
35+
- equal:
36+
path: spec.values.storageClasses.replicated.default
37+
value: true
38+
- equal:
39+
path: spec.values.storageClasses.kubevirt.infraStorageClass
40+
value: replicated
41+
- equal:
42+
path: spec.values.storageClasses.kubevirt.default
43+
value: false
44+
45+
- it: kubevirt alias tracks a custom storageClass selector, replicated stays default
46+
release:
47+
name: kubernetes-test
48+
namespace: tenant-test
49+
set:
50+
storageClass: fast
51+
asserts:
52+
- equal:
53+
path: spec.values.storageClasses.kubevirt.infraStorageClass
54+
value: fast
55+
- equal:
56+
path: spec.values.storageClasses.replicated.default
57+
value: true
58+
- equal:
59+
path: spec.values.storageClasses.kubevirt.default
60+
value: false
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.PHONY: test
2+
3+
test:
4+
helm unittest .
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
suite: tenant StorageClass rendering
2+
3+
templates:
4+
- templates/deploy.yaml
5+
6+
tests:
7+
- it: renders default replicated (default) and kubevirt alias (non-default)
8+
asserts:
9+
- equal:
10+
path: provisioner
11+
value: csi.kubevirt.io
12+
documentSelector:
13+
path: metadata.name
14+
value: replicated
15+
- equal:
16+
path: parameters.infraStorageClassName
17+
value: replicated
18+
documentSelector:
19+
path: metadata.name
20+
value: replicated
21+
- equal:
22+
path: parameters.bus
23+
value: scsi
24+
documentSelector:
25+
path: metadata.name
26+
value: replicated
27+
- equal:
28+
path: metadata.annotations["storageclass.kubernetes.io/is-default-class"]
29+
value: "true"
30+
documentSelector:
31+
path: metadata.name
32+
value: replicated
33+
- notExists:
34+
path: metadata.annotations
35+
documentSelector:
36+
path: metadata.name
37+
value: kubevirt
38+
39+
- it: renders one StorageClass per entry with exactly one default
40+
set:
41+
storageClasses:
42+
replicated:
43+
infraStorageClass: replicated
44+
default: true
45+
fast:
46+
infraStorageClass: nvme
47+
default: false
48+
kubevirt:
49+
infraStorageClass: replicated
50+
default: false
51+
asserts:
52+
- equal:
53+
path: parameters.infraStorageClassName
54+
value: nvme
55+
documentSelector:
56+
path: metadata.name
57+
value: fast
58+
- notExists:
59+
path: metadata.annotations
60+
documentSelector:
61+
path: metadata.name
62+
value: fast
63+
- equal:
64+
path: metadata.annotations["storageclass.kubernetes.io/is-default-class"]
65+
value: "true"
66+
documentSelector:
67+
path: metadata.name
68+
value: replicated
69+
- notExists:
70+
path: metadata.annotations
71+
documentSelector:
72+
path: metadata.name
73+
value: kubevirt
74+
75+
- it: leaves the driver-config ConfigMap free of any enforcement allowList
76+
asserts:
77+
- notExists:
78+
path: data.infraStorageClassEnforcement
79+
documentSelector:
80+
path: metadata.name
81+
value: driver-config

0 commit comments

Comments
 (0)