You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by do...@apache.org on 2026/08/04 19:24:01 UTC

(spark-kubernetes-operator) branch main updated: [SPARK-58565] Run every assertion in the Helm RBAC test hooks

This is an automated email from the ASF dual-hosted git repository.

dongjoon-hyun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/spark-kubernetes-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new c6833f7  [SPARK-58565] Run every assertion in the Helm RBAC test hooks
c6833f7 is described below

commit c6833f7f3443daf7e6c7602e4ae98ae6fd6e9f64
Author: Nicholas Barton <12...@users.noreply.github.com>
AuthorDate: Tue Aug 4 12:23:53 2026 -0700

    [SPARK-58565] Run every assertion in the Helm RBAC test hooks
    
    ### What changes were proposed in this pull request?
    Move the RBAC Helm test assertions into a single block scalar, one `kubectl auth can-i` per line under `set -ex`. `can-i` already exits non-zero when it answers "no", and -x echoes each assertion into the pod log beside its answer.
    
    Expand the test assertions to more comprehensively cover cases not already included:
    
    - Cover every grant; only core resources and spark.apache.org were checked before.
      - Leases are asserted only for a replicated operator, matching the replicas > 1 condition on that grant.
      - Assert the Gateway API grant only where the CRDs are installed. `kubectl auth can-i` answers "no" for a resource type the cluster does not serve, even when the rules grant it, so an unguarded assertion would fail on every cluster without the Gateway API.
      - Add negative assertions
    - Stop deleting the pod when a hook fails, so its log survives for inspection. A passing pod is still cleaned up.
    
    ### Why are the changes needed?
    If a future change breaks the permissions model, the tests will continue to pass. We should catch breaking changes in tests.
    
    `command: ['bash', '-c']` with a multi-element `args` array passes only the first element to bash as the script; the rest become positional parameters. Six of the seven operator assertions and three of the four workload assertions therefore never ran, and `helm test` passed regardless.
    
    ### Does this PR introduce _any_ user-facing change?
    No
    
    ### How was this patch tested?
    Repro listed in the ticket + it should pass CI.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    Generated by: Claude Code (Opus 5)
    
    Closes #783 from snicholasbarton/SPARK-58565.
    
    Authored-by: Nicholas Barton <12...@users.noreply.github.com>
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
---
 .../templates/tests/test-rbac.yaml                 | 99 +++++++++++++++++-----
 1 file changed, 80 insertions(+), 19 deletions(-)

diff --git a/build-tools/helm/spark-kubernetes-operator/templates/tests/test-rbac.yaml b/build-tools/helm/spark-kubernetes-operator/templates/tests/test-rbac.yaml
index 141c4d0..9647769 100644
--- a/build-tools/helm/spark-kubernetes-operator/templates/tests/test-rbac.yaml
+++ b/build-tools/helm/spark-kubernetes-operator/templates/tests/test-rbac.yaml
@@ -21,21 +21,69 @@ metadata:
     {{- include "spark-operator.commonLabels" . | nindent 4 }}
   annotations:
     "helm.sh/hook": test
-    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed
+    # Keep a failed pod so its log can be read; a passing one is cleaned up.
+    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
 spec:
   containers:
     - name: kubectl
       image: bitnamisecure/kubectl:latest
-      command: ['bash', '-c' ]
-      args: [
-        'kubectl auth can-i list sparkapplications --all-namespaces',
-        'kubectl auth can-i list sparkclusters --all-namespaces',
-        'kubectl auth can-i create pods --all-namespaces',
-        'kubectl auth can-i create services --all-namespaces',
-        'kubectl auth can-i create configmaps --all-namespaces',
-        'kubectl auth can-i create persistentvolumeclaims',
-        'kubectl auth can-i create events --all-namespaces'
-      ]
+      command: ['bash', '-c']
+      args:
+        - |
+          # Every assertion must live in this one script: `bash -c` runs only its
+          # first operand and turns the rest into positional parameters.
+          # -e fails the pod on the first "no", which is what `kubectl auth can-i`
+          # exits with; -x echoes each assertion into the log beside its answer.
+          set -ex
+
+          kubectl auth can-i watch  pods --all-namespaces
+          kubectl auth can-i create pods --all-namespaces
+          kubectl auth can-i watch  services --all-namespaces
+          kubectl auth can-i create services --all-namespaces
+          kubectl auth can-i watch  configmaps --all-namespaces
+          kubectl auth can-i create configmaps --all-namespaces
+          kubectl auth can-i watch  persistentvolumeclaims --all-namespaces
+          kubectl auth can-i create persistentvolumeclaims --all-namespaces
+          kubectl auth can-i watch  events --all-namespaces
+          kubectl auth can-i create events --all-namespaces
+          kubectl auth can-i watch  statefulsets.apps --all-namespaces
+          kubectl auth can-i create statefulsets.apps --all-namespaces
+          kubectl auth can-i watch  horizontalpodautoscalers.autoscaling --all-namespaces
+          kubectl auth can-i create horizontalpodautoscalers.autoscaling --all-namespaces
+          kubectl auth can-i watch  sparkapplications.spark.apache.org --all-namespaces
+          kubectl auth can-i create sparkapplications.spark.apache.org --all-namespaces
+          kubectl auth can-i watch  sparkclusters.spark.apache.org --all-namespaces
+          kubectl auth can-i create sparkclusters.spark.apache.org --all-namespaces
+          kubectl auth can-i watch  ingresses.networking.k8s.io --all-namespaces
+          kubectl auth can-i create ingresses.networking.k8s.io --all-namespaces
+          kubectl auth can-i watch  networkpolicies.networking.k8s.io --all-namespaces
+          kubectl auth can-i create networkpolicies.networking.k8s.io --all-namespaces
+          kubectl auth can-i watch  poddisruptionbudgets.policy --all-namespaces
+          kubectl auth can-i create poddisruptionbudgets.policy --all-namespaces
+          {{- if gt (int .Values.operatorDeployment.replicas) 1 }}
+
+          # Leases back leader election, so the rules only grant them for a
+          # replicated operator.
+          kubectl auth can-i watch  leases.coordination.k8s.io --all-namespaces
+          kubectl auth can-i create leases.coordination.k8s.io --all-namespaces
+          {{- end }}
+
+          # The Gateway API grant is unconditional, but `kubectl auth can-i`
+          # answers "no" for a resource type the cluster does not serve, so only
+          # assert it where the CRDs are installed.
+          if kubectl api-resources --api-group=gateway.networking.k8s.io --no-headers -o name | grep -q httproutes; then
+            kubectl auth can-i watch  httproutes.gateway.networking.k8s.io --all-namespaces
+            kubectl auth can-i create httproutes.gateway.networking.k8s.io --all-namespaces
+            kubectl auth can-i watch  grpcroutes.gateway.networking.k8s.io --all-namespaces
+            kubectl auth can-i create grpcroutes.gateway.networking.k8s.io --all-namespaces
+          fi
+
+          # The operator must not be cluster-admin. These also prove the suite is
+          # able to observe a denial at all. `set -e` ignores a failing `!`
+          # pipeline, so each denial must be asserted with an explicit exit.
+          if kubectl auth can-i create clusterroles.rbac.authorization.k8s.io; then exit 1; fi
+          if kubectl auth can-i delete nodes; then exit 1; fi
+          if kubectl auth can-i '*' '*' --all-namespaces; then exit 1; fi
   serviceAccountName: {{ .Values.operatorRbac.serviceAccount.name }}
   restartPolicy: Never
 ---
@@ -48,17 +96,30 @@ metadata:
     {{- include "spark-operator.commonLabels" . | nindent 4 }}
   annotations:
     "helm.sh/hook": test
-    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed
+    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
 spec:
   containers:
     - name: kubectl
       image: bitnamisecure/kubectl:latest
-      command: ['bash', '-c' ]
-      args: [
-        'kubectl auth can-i create pods -n {{ .Release.Namespace }}',
-        'kubectl auth can-i create configmaps -n {{ .Release.Namespace }}',
-        'kubectl auth can-i create services -n {{ .Release.Namespace }}',
-        'kubectl auth can-i create persistentvolumeclaims -n {{ .Release.Namespace }}'
-      ]
+      command: ['bash', '-c']
+      args:
+        - |
+          set -ex
+
+          kubectl auth can-i watch  pods --namespace={{ .Release.Namespace }}
+          kubectl auth can-i create pods --namespace={{ .Release.Namespace }}
+          kubectl auth can-i watch  services --namespace={{ .Release.Namespace }}
+          kubectl auth can-i create services --namespace={{ .Release.Namespace }}
+          kubectl auth can-i watch  configmaps --namespace={{ .Release.Namespace }}
+          kubectl auth can-i create configmaps --namespace={{ .Release.Namespace }}
+          kubectl auth can-i watch  persistentvolumeclaims --namespace={{ .Release.Namespace }}
+          kubectl auth can-i create persistentvolumeclaims --namespace={{ .Release.Namespace }}
+          kubectl auth can-i watch  statefulsets.apps --namespace={{ .Release.Namespace }}
+          kubectl auth can-i create statefulsets.apps --namespace={{ .Release.Namespace }}
+
+          # Spark workloads must not reach beyond their own namespace. `set -e`
+          # ignores a failing `!` pipeline, so assert each denial explicitly.
+          if kubectl auth can-i create clusterroles.rbac.authorization.k8s.io; then exit 1; fi
+          if kubectl auth can-i '*' '*' --all-namespaces; then exit 1; fi
   serviceAccountName: {{ .Values.workloadResources.serviceAccount.name }}
   restartPolicy: Never


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org