You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[SPARK-57900][K8S][TEST] Add OIDC credential propagation E2E tests on Minikube with moto
### What changes were proposed in this pull request?
Add a new optional integration-test module, `connector/credential-aws-integration-tests`, that validates the end-to-end OIDC credential propagation pipeline on a real Kubernetes cluster (Minikube). This is Sub-task 11 of the OIDC Credential Propagation SPIP ([SPARK-57703](https://issues.apache.org/jira/browse/SPARK-57703)), and it exercises the
whole feature together: projected ServiceAccount token -> `FileTokenIngestor` -> `AwsStsCredentialProvider` -> STS -> S3A read/write, plus mid-job token rotation and late-registering executors.
The tests use [moto](https://github.com/getmoto/moto) (Apache 2.0-licensed) as a lightweight S3 + STS backend. The original SPIP mentioned LocalStack, but both LocalStack and MinIO have moved away from freely usable OSS distributions and are incompatible with the ASF license policy. moto runs as a plain HTTP server (no extra container) and does not
verify the OIDC JWT, keeping the test focused on Spark's credential propagation logic.
**Three scenarios are implemented:**
1. **Basic flow** (`OidcS3ReadWriteJob`): a Spark job on Minikube exchanges the identity token for STS credentials and reads/writes S3 via S3A.
2. **Mid-job token rotation** (`OidcTokenRotationJob`): a long-running job writes to S3 repeatedly while the test rewrites the identity token file in the driver pod. The initial token is supplied by an init container into an emptyDir (an externally-provided, rotatable token file, as the SPIP assumes). The rotated token carries a *different* principal; with a short renewal interval, `UserCredentialManager` re-reads it, re-exchanges it via STS, and propagates fresh credentials. The test asserts the driver logged the rotated principal (proving the new token was actually read, not a no-op) and that S3 output for all iterations spanning the rotation is present.
3. **Late-registering executor** (`OidcLateExecutorJob`): with dynamic allocation and a short idle timeout, a job warms up, idles until executors scale down, then runs a wider stage that forces new executors to register *after* credentials were acquired. The test asserts more than one distinct executor registered over the run (evidence of a genuinely late-registering executor) and that the wide stage produced all outputs — an executor that did not receive credentials via the `SparkAppConfig` registration response would have failed its task.
**Structure and design:**
- The module is gated behind the `-Poidc-e2e` Maven profile (and requires `-Pkubernetes`), so it is skipped by default.
- Jobs that run on the cluster live in `src/main` so they are packaged into the module jar and baked into the Spark image; test classes are not packaged.
- S3A support (hadoop-aws + AWS SDK) is provided by building the image with `-Phadoop-cloud`.
- Image building is handled by an explicit step (`docker-image-tool.sh`) in CI and by `dev-run-integration-tests.sh` locally, rather than being bound to the sbt test task.
- The spark-submit helpers (`SparkAppLauncher`, `SparkAppConf`, `SparkAppArguments`, `ProcessUtils`) are implemented locally instead of depending on the `spark-kubernetes-integration-tests` test-jar, which sbt could not resolve as an inter-project reference. They mirror the equivalents there.
- moto is reached from two vantage points: pods use the host gateway IP (`spark.oidc.test.s3Endpoint` / `stsEndpoint`), while the test process uses loopback (`spark.oidc.test.s3ClientEndpoint`). In CI, moto is installed into an isolated virtualenv
(to avoid the OS-provided urllib3/pyOpenSSL that crashes moto on startup) and started inside the same workflow step that runs the tests.
**New files:**
- `connector/credential-aws-integration-tests/` — module with the test suite (`OidcCredentialE2ESuite`), the three Spark jobs, spark-submit helpers, `pom.xml`, `log4j2.properties`, a local runner script (`dev-run-integration-tests.sh`), and `README.md`.
**Modified files:**
- `pom.xml` (root) — add the `oidc-e2e` profile / module.
- `project/SparkBuild.scala` — register `credentialAwsIntegrationTests`.
- `.github/workflows/build_and_test.yml` — add the `oidc-e2e` job (moto + Minikube).
### Why are the changes needed?
The SPIP calls for an end-to-end test that validates the full credential propagation pipeline in a realistic Kubernetes environment. The prior sub-tasks each cover a slice with unit/integration tests, but nothing exercised the entire flow — token ingestion, STS exchange, RPC + SparkAppConfig propagation, S3A read/write, and mid-job refresh — against a real cluster. This module provides that coverage and guards against regressions in how the pieces fit together.
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
This *is* the test. The suite was run on a local Minikube (with moto) under both build tools and all three scenarios passed:
- sbt: `build/sbt -Phadoop-3 -Pkubernetes -Pcredential-aws -Poidc-e2e ... credential-aws-integration-tests/test`
- Maven: `build/mvn integration-test -pl connector/credential-aws-integration-tests -Phadoop-3 -Pkubernetes -Pcredential-aws -Poidc-e2e ...`
The new `oidc-e2e` GitHub Actions job (Minikube + moto) is green. `dev-run-integration-tests.sh` was also verified to build the image, start/stop moto, and run the suite end-to-end.
### Was this patch authored or co-authored using generative AI tooling?
Kiro CLI / Claude
Closes#58426 from sarutak/oidc-propagation/e2e-tests.
Authored-by: Kousuke Saruta <sarutak@apache.org>
Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
0 commit comments