|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Copyright 2025 The Kubernetes Authors. |
| 4 | +# |
| 5 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | +# you may not use this file except in compliance with the License. |
| 7 | +# You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, software |
| 12 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +# See the License for the specific language governing permissions and |
| 15 | +# limitations under the License. |
| 16 | + |
| 17 | +# Parameter set definitions for e2e parameter tests. |
| 18 | +# Each set defines GINKGO_FOCUS, HELM_EXTRA_FLAGS, and optionally other env vars. |
| 19 | +# |
| 20 | +# Sets in PARAM_SETS_ALL (no "special" cluster config needed): |
| 21 | +# standard - Behavioral params (tagging, metrics, logging, storage classes, etc.) |
| 22 | +# other - Volume modification, volume attach limit, and metadata labeler |
| 23 | +# debug - debugLogs=true overrides individual logLevel settings |
| 24 | +# infra - Infrastructure/deployment params (resources, security, strategy, etc.) |
| 25 | +# fips - Builds FIPS image then validates it is deployed |
| 26 | +# |
| 27 | +# Not in PARAM_SETS_ALL: |
| 28 | +# legacy-compat - Legacy CSIDriver + XFS behavior |
| 29 | +# selinux - Needs SELinux-enabled nodes |
| 30 | + |
| 31 | +set -euo pipefail |
| 32 | + |
| 33 | +PARAM_SETS_ALL="standard other debug infra fips" |
| 34 | + |
| 35 | +param_set_standard() { |
| 36 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:(extraCreateMetadata|k8sTagClusterId|extraVolumeTags|controllerMetrics|nodeMetrics|batching|defaultFsType|controllerLoggingFormat|nodeLoggingFormat|controllerLogLevel|nodeLogLevel|provisionerLogLevel|attacherLogLevel|snapshotterLogLevel|resizerLogLevel|nodeDriverRegistrarLogLevel|storageClasses|volumeSnapshotClasses|defaultStorageClass|snapshotterForceEnable|controllerUserAgentExtra|controllerEnablePrometheusAnnotations|nodeEnablePrometheusAnnotations|nodeKubeletPath|nodeTolerateAllTaints|controllerPodDisruptionBudget|provisionerLeaderElection|attacherLeaderElection|resizerLeaderElection|reservedVolumeAttachments|hostNetwork|nodeDisableMutation|nodeTerminationGracePeriod)\]" |
| 37 | + HELM_EXTRA_FLAGS="--set=controller.extraCreateMetadata=true,controller.k8sTagClusterId=e2e-param-test,controller.extraVolumeTags.TestKey=TestValue,controller.enableMetrics=true,node.enableMetrics=true,controller.batching=true,controller.defaultFsType=xfs,controller.loggingFormat=json,node.loggingFormat=json,controller.logLevel=4,node.logLevel=4,sidecars.provisioner.logLevel=4,sidecars.attacher.logLevel=4,sidecars.snapshotter.logLevel=4,sidecars.resizer.logLevel=4,sidecars.nodeDriverRegistrar.logLevel=4,defaultStorageClass.enabled=true,storageClasses[0].name=test-sc,storageClasses[0].parameters.type=gp3,volumeSnapshotClasses[0].name=test-vsc,volumeSnapshotClasses[0].deletionPolicy=Delete,sidecars.snapshotter.forceEnable=true,controller.userAgentExtra=e2e-test,controller.enablePrometheusAnnotations=true,node.enablePrometheusAnnotations=true,node.kubeletPath=/var/lib/kubelet,node.tolerateAllTaints=true,controller.podDisruptionBudget.enabled=true,sidecars.provisioner.leaderElection.enabled=true,sidecars.attacher.leaderElection.enabled=true,sidecars.resizer.leaderElection.enabled=true,node.reservedVolumeAttachments=2,node.hostNetwork=true,node.serviceAccount.disableMutation=true,node.terminationGracePeriodSeconds=60" |
| 38 | +} |
| 39 | + |
| 40 | +# other combines volume-modification, volume-attach-limit, and metadata-labeler (no conflicting Helm values) |
| 41 | +param_set_other() { |
| 42 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:(volumeModification|volumemodifierLogLevel|volumemodifierLeaderElection|volumeAttachLimit|metadataLabeler|metadataLabelerLogLevel)\]" |
| 43 | + HELM_EXTRA_FLAGS="--set=controller.volumeModificationFeature.enabled=true,sidecars.provisioner.additionalArgs[0]='--feature-gates=VolumeAttributesClass=true',sidecars.resizer.additionalArgs[0]='--feature-gates=VolumeAttributesClass=true',sidecars.volumemodifier.logLevel=4,sidecars.volumemodifier.leaderElection.enabled=false,node.volumeAttachLimit=25,sidecars.metadataLabeler.enabled=true,node.metadataSources='metadata-labeler',sidecars.metadataLabeler.logLevel=4" |
| 44 | +} |
| 45 | + |
| 46 | +# debugLogs=true overrides individual logLevel settings, so this must be separate from standard |
| 47 | +param_set_debug() { |
| 48 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:(debugLogs|sdkDebugLog)\]" |
| 49 | + HELM_EXTRA_FLAGS="--set=debugLogs=true,controller.sdkDebugLog=true" |
| 50 | +} |
| 51 | + |
| 52 | +param_set_infra() { |
| 53 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:(controllerReplicaCount|controllerPriorityClassName|controllerResources|controllerPodAnnotations|controllerPodLabels|controllerDeploymentAnnotations|controllerRevisionHistoryLimit|nodePriorityClassName|nodeResources|nodePodAnnotations|nodeDaemonSetAnnotations|nodeRevisionHistoryLimit|provisionerResources|attacherResources|snapshotterResources|resizerResources|nodeDriverRegistrarResources|livenessProbeResources|customLabels|controllerEnv|nodeEnv|controllerTopologySpreadConstraints|controllerSecurityContext|nodeSecurityContext|controllerContainerSecurityContext|controllerVolumes|controllerVolumeMounts|nodeVolumes|nodeVolumeMounts|controllerDnsConfig|nodeDnsConfig|controllerInitContainers|nodeInitContainers|imagePullPolicy|controllerUpdateStrategy|nodeUpdateStrategy)\]" |
| 54 | + HELM_EXTRA_FLAGS="--set=controller.replicaCount=3,controller.priorityClassName=system-cluster-critical,controller.resources.requests.cpu=100m,controller.resources.limits.memory=256Mi,controller.podAnnotations.test-annotation=test-value,controller.podLabels.test-label=test-value,controller.deploymentAnnotations.deploy-annotation=deploy-value,controller.revisionHistoryLimit=5,node.priorityClassName=system-node-critical,node.resources.requests.cpu=50m,node.resources.limits.memory=128Mi,node.podAnnotations.node-annotation=node-value,node.daemonSetAnnotations.ds-annotation=ds-value,node.revisionHistoryLimit=3,sidecars.provisioner.resources.requests.cpu=20m,sidecars.attacher.resources.requests.cpu=15m,sidecars.snapshotter.resources.requests.cpu=15m,sidecars.resizer.resources.requests.cpu=15m,sidecars.nodeDriverRegistrar.resources.requests.cpu=10m,sidecars.livenessProbe.resources.requests.cpu=5m,customLabels.custom-label=custom-value,controller.env[0].name=TEST_ENV,controller.env[0].value=test-value,node.env[0].name=NODE_ENV,node.env[0].value=node-value,controller.topologySpreadConstraints[0].maxSkew=1,controller.topologySpreadConstraints[0].topologyKey=topology.kubernetes.io/zone,controller.topologySpreadConstraints[0].whenUnsatisfiable=ScheduleAnyway,controller.securityContext.runAsNonRoot=true,controller.containerSecurityContext.readOnlyRootFilesystem=true,controller.volumes[0].name=extra-volume,controller.volumes[0].configMap.name=kube-root-ca.crt,controller.volumeMounts[0].name=extra-volume,controller.volumeMounts[0].mountPath=/extra,node.volumes[0].name=node-extra-volume,node.volumes[0].configMap.name=kube-root-ca.crt,node.volumeMounts[0].name=node-extra-volume,node.volumeMounts[0].mountPath=/node-extra,controller.dnsConfig.nameservers[0]=8.8.8.8,node.dnsConfig.nameservers[0]=8.8.4.4,controller.initContainers[0].name=init-container,controller.initContainers[0].image=busybox,controller.initContainers[0].command[0]=echo,controller.initContainers[0].command[1]=init,node.initContainers[0].name=node-init-container,node.initContainers[0].image=busybox,node.initContainers[0].command[0]=echo,node.initContainers[0].command[1]=node-init,image.pullPolicy=Always,controller.updateStrategy.type=Recreate,controller.updateStrategy.rollingUpdate=null,node.updateStrategy.type=OnDelete" |
| 55 | +} |
| 56 | + |
| 57 | +param_set_legacy-compat() { |
| 58 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:(useOldCSIDriver|legacyXFS)\]" |
| 59 | + HELM_EXTRA_FLAGS="--set=useOldCSIDriver=true,node.legacyXFS=true" |
| 60 | +} |
| 61 | + |
| 62 | +param_set_selinux() { |
| 63 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:selinux\]" |
| 64 | + HELM_EXTRA_FLAGS="--set=node.selinux=true" |
| 65 | +} |
| 66 | + |
| 67 | +param_set_fips() { |
| 68 | + GINKGO_FOCUS="\[ebs-csi-e2e\] \[param:fips\]" |
| 69 | + FIPS_TEST=true |
| 70 | + HELM_EXTRA_FLAGS="--set=fips=true" |
| 71 | + FIPS_TEST=true make cluster/image |
| 72 | +} |
| 73 | + |
| 74 | +# Load a parameter set by name, exporting GINKGO_FOCUS and HELM_EXTRA_FLAGS |
| 75 | +load_param_set() { |
| 76 | + local name="$1" |
| 77 | + local func="param_set_${name}" |
| 78 | + if ! declare -f "$func" > /dev/null 2>&1; then |
| 79 | + echo "Unknown parameter set: ${name}" >&2 |
| 80 | + echo "Available sets: standard, other, debug, infra, legacy-compat, selinux, fips" >&2 |
| 81 | + exit 1 |
| 82 | + fi |
| 83 | + "$func" |
| 84 | + export GINKGO_FOCUS HELM_EXTRA_FLAGS |
| 85 | + export GINKGO_PARALLEL="${GINKGO_PARALLEL:-5}" |
| 86 | + export AWS_AVAILABILITY_ZONES="${AWS_AVAILABILITY_ZONES:-us-west-2a}" |
| 87 | + export TEST_PATH="${TEST_PATH:-./tests/e2e/...}" |
| 88 | + export JUNIT_REPORT="${REPORT_DIR:-/logs/artifacts}/junit-params-${name}.xml" |
| 89 | + # Export optional vars if set by the param set function |
| 90 | + if [[ -n "${EBS_INSTALL_SNAPSHOT+x}" ]]; then export EBS_INSTALL_SNAPSHOT; fi |
| 91 | + if [[ -n "${FIPS_TEST+x}" ]]; then export FIPS_TEST; fi |
| 92 | +} |
| 93 | + |
| 94 | +# Run a single parameter set |
| 95 | +run_param_set() { |
| 96 | + load_param_set "$1" |
| 97 | + echo "### Running parameter set: $1" |
| 98 | + ./hack/e2e/run.sh |
| 99 | +} |
| 100 | + |
| 101 | +# Merge per-set JUnit XMLs into a single file with duplicate skipped tests removed. |
| 102 | +# Each Ginkgo run reports ALL specs (most as skipped), so the same skipped test appears |
| 103 | +# in every per-set file. This merges all results into one file, keeping non-skipped results |
| 104 | +# (passed/failed) over skipped duplicates, and emitting each skipped test only once. |
| 105 | +merge_junit_results() { |
| 106 | + local report_dir="${REPORT_DIR:-/logs/artifacts}" |
| 107 | + local output="${report_dir}/junit-params.xml" |
| 108 | + |
| 109 | + python3 - "$report_dir" "$output" <<'PYEOF' |
| 110 | +import glob, sys, xml.etree.ElementTree as ET |
| 111 | +
|
| 112 | +report_dir, output = sys.argv[1], sys.argv[2] |
| 113 | +merged = {} |
| 114 | +time_total = 0.0 |
| 115 | +
|
| 116 | +def priority(tc): |
| 117 | + if tc.find("failure") is not None or tc.find("error") is not None: |
| 118 | + return 2 # failed/errored |
| 119 | + if tc.find("skipped") is not None: |
| 120 | + return 0 # skipped |
| 121 | + return 1 # passed |
| 122 | +
|
| 123 | +for path in sorted(glob.glob(f"{report_dir}/junit-params-*.xml")): |
| 124 | + tree = ET.parse(path) |
| 125 | + time_total += float(tree.getroot().get("time", "0")) |
| 126 | + for tc in tree.iter("testcase"): |
| 127 | + name = tc.get("name", "") |
| 128 | + if name not in merged or priority(tc) > priority(merged[name]): |
| 129 | + merged[name] = tc |
| 130 | +
|
| 131 | +tests = list(merged.values()) |
| 132 | +skipped = sum(1 for tc in tests if tc.find("skipped") is not None) |
| 133 | +failed = sum(1 for tc in tests if tc.find("failure") is not None) |
| 134 | +errored = sum(1 for tc in tests if tc.find("error") is not None) |
| 135 | +
|
| 136 | +root = ET.Element("testsuites", tests=str(len(tests)), disabled=str(skipped), |
| 137 | + errors=str(errored), failures=str(failed), time=str(time_total)) |
| 138 | +suite = ET.SubElement(root, "testsuite", name="AWS EBS CSI Driver Parameter Tests", |
| 139 | + tests=str(len(tests)), skipped=str(skipped), |
| 140 | + errors=str(errored), failures=str(failed), time=str(time_total)) |
| 141 | +for tc in tests: |
| 142 | + suite.append(tc) |
| 143 | +
|
| 144 | +ET.ElementTree(root).write(output, xml_declaration=True, encoding="UTF-8") |
| 145 | +PYEOF |
| 146 | + |
| 147 | + # Remove per-set files only if merge succeeded, so CI only sees the merged result |
| 148 | + if [[ $? -eq 0 && -f "$output" ]]; then |
| 149 | + rm -f "${report_dir}"/junit-params-*.xml |
| 150 | + echo "Merged JUnit results into ${output}" |
| 151 | + else |
| 152 | + echo "WARNING: JUnit merge failed, keeping per-set files" >&2 |
| 153 | + fi |
| 154 | +} |
| 155 | + |
| 156 | +# Run all standard parameter sets sequentially |
| 157 | +run_all_param_sets() { |
| 158 | + echo "Running all parameter sets sequentially..." |
| 159 | + for set in $PARAM_SETS_ALL; do |
| 160 | + run_param_set "$set" |
| 161 | + done |
| 162 | + merge_junit_results |
| 163 | + echo "All parameter sets completed successfully!" |
| 164 | +} |
| 165 | + |
| 166 | +# Allow direct invocation: ./hack/e2e/param-sets.sh run <name> or ./hack/e2e/param-sets.sh run-all |
| 167 | +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then |
| 168 | + case "${1:-}" in |
| 169 | + run) |
| 170 | + [[ -z "${2:-}" ]] && { echo "Usage: $0 run <param-set-name>" >&2; exit 1; } |
| 171 | + run_param_set "$2" |
| 172 | + merge_junit_results |
| 173 | + ;; |
| 174 | + run-all) |
| 175 | + run_all_param_sets |
| 176 | + ;; |
| 177 | + *) |
| 178 | + echo "Usage: $0 {run <param-set-name>|run-all}" >&2 |
| 179 | + exit 1 |
| 180 | + ;; |
| 181 | + esac |
| 182 | +fi |
0 commit comments