Skip to content

[#13616] feat(k8s-job-executor): Register the k8s job executor, ship it and run built-in jobs on it - #13892

Open
jerryshao wants to merge 3 commits into
apache:mainfrom
jerryshao:13616-k8s-executor-register
Open

jerryshao wants to merge 3 commits into
apache:mainfrom
jerryshao:13616-k8s-executor-register

Conversation

@jerryshao

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

The k8s job executor added in #13613 can now be used by Gravitino, and runs the built-in Spark jobs.

  • Registration: JobExecutorFactory registers k8s by class name, as the executor is a plugin that depends on core. The name and class name are public constants of JobExecutorFactory, which the plugin uses, and a plugin test checks the class name against K8sJobExecutor. If the class can't be loaded, the error names the missing gravitino-k8s-job-executor-<version>.jar and the libs directory.
  • Distribution: the shaded jar of the executor is copied into libs of the distribution package. LICENSE.bin already lists everything it bundles (fabric8, zjsonpatch, Jackson, SnakeYAML, SnakeYAML Engine).
  • Smoke test of the shaded jar: a separate shadedJarTest source set and task, part of check, puts the shaded jar on the runtime classpath of :server (with its Jackson 2.15.2), without the module's classes or unshaded dependencies. It checks that Jackson and SnakeYAML are relocated, that the jar bundles no class that is also on the server classpath, and creates the executor through JobExecutorFactory to submit a built-in job to a stub API server. It only needs shadowJar, not a distribution build.
  • Built-in jobs: the executable of a built-in template is the path of the jobs jar on the Gravitino server, which a Spark pod can't reach. The new spark.builtinJobsJar is the URI of the jobs jar that the driver and executors can reach, local:///opt/gravitino/jobs/gravitino-jobs.jar by default: a file in the Spark image or in a mounted volume, or https://, s3a:// and so on. A template whose name starts with builtin- and whose executable is a path on the server runs that jar instead. An executable that the cluster can already reach is kept. The stored templates don't change, and the local executor keeps using the jar on the server.
  • The check of whether a resource is reachable from the cluster moves to K8sJobResourceUtils.isClusterReachable, so that the resource check, the spark.builtinJobsJar validation and the substitution share it.

Why are the changes needed?

Without this, gravitino.job.executor = k8s fails to load the executor, and the built-in jobs can't run on it.

Fix: #13616

Does this PR introduce any user-facing change?

Yes.

  • gravitino.job.executor = k8s selects the k8s job executor, configured with the gravitino.jobExecutor.k8s. keys added in [Subtask] Add the k8s job executor to run Spark jobs on Kubernetes #13613.
  • New key gravitino.jobExecutor.k8s.spark.builtinJobsJar, default local:///opt/gravitino/jobs/gravitino-jobs.jar.
  • The distribution package ships libs/gravitino-k8s-job-executor-<version>.jar.

The gravitino-spark image that holds the jobs jar at the default path, and the docs, come with #13618.

How was this patch tested?

  • Unit tests for the factory, the new configuration and the substitution, and the shadedJarTest smoke test.
  • Manually, with a Gravitino server started from the distribution package of this branch against a local Kubernetes cluster (OrbStack) with spark-kubernetes-operator 0.9.0, through the REST API:
    • the built-in Spark Pi job: success with its times and driver output, cancel, a failing job, and a user template whose executable is a path on the server, which is rejected;
    • the jobs jar given as an http:// link through spark.builtinJobsJar;
    • built-in Iceberg jobs on an image without the Iceberg runtime, with the runtime given through gravitino.jobExecutor.k8s.spark.conf.spark.jars or spark.conf.spark.jars.packages (with spark.jars.ivy). Passing spark.jars in the job's spark_conf argument doesn't work, which [Subtask] Let a job run set submit-time Spark configurations of the built-in job templates #13703 addresses.

🤖 Generated with Claude Code

… ship it and run built-in jobs on it

- Register `k8s` in `JobExecutorFactory` by class name, so that core doesn't
  depend on the plugin, and report a missing job executor jar clearly.
- Copy the shaded jar of the job executor into `libs` of the distribution.
- Add `shadedJarTest`, which loads the shaded jar on the classpath of the
  Gravitino server, creates the job executor through the factory and submits
  a job to a stub API server, to catch relocation and dependency conflicts.
- Add `spark.builtinJobsJar`, the URI of the built-in jobs jar that the Spark
  pods can reach. It replaces the executable of a `builtin-` job template when
  that is a path on the Gravitino server. The stored templates don't change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 16, 2026 06:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Distribution assembly and shaded server-classpath compatibility need human confirmation of runtime validation results.

0 open findings

What changed in this PR

Enables Gravitino to load and ship the Kubernetes job executor and run built-in Spark jobs using a cluster-accessible jobs jar.

Changes:

  • Registers k8s with actionable class-loading errors and includes its shaded jar in distributions.
  • Adds configurable built-in jobs jar substitution and shared resource validation.
  • Adds unit coverage and a server-classpath smoke test for the shaded plugin.
File Description
plugins/​k8s-job-executor/​src/​test/​java/​org/​apache/​gravitino/​job/​k8s/​TestK8sJobResourceUtils.java Tests resource URI classification.
plugins/​k8s-job-executor/​src/​test/​java/​org/​apache/​gravitino/​job/​k8s/​TestK8sJobExecutorConfigs.java Tests jobs jar configuration and validation.
plugins/​k8s-job-executor/​src/​test/​java/​org/​apache/​gravitino/​job/​k8s/​TestK8sJobExecutor.java Checks factory registration constants.
plugins/​k8s-job-executor/​src/​test/​java/​org/​apache/​gravitino/​job/​k8s/​spark/​TestSparkApplicationUtils.java Tests built-in executable substitution boundaries.
plugins/​k8s-job-executor/​src/​shadedJarTest/​java/​org/​apache/​gravitino/​job/​k8s/​TestK8sJobExecutorShadedJar.java Checks shading, class conflicts, and job submission.
plugins/​k8s-job-executor/​src/​main/​java/​org/​apache/​gravitino/​job/​k8s/​spark/​SparkApplicationUtils.java Substitutes cluster-accessible built-in executables.
plugins/​k8s-job-executor/​src/​main/​java/​org/​apache/​gravitino/​job/​k8s/​K8sJobResourceUtils.java Centralizes resource URI classification.
plugins/​k8s-job-executor/​src/​main/​java/​org/​apache/​gravitino/​job/​k8s/​K8sJobExecutorConfigs.java Adds the built-in jobs jar setting.
plugins/​k8s-job-executor/​build.gradle.kts Configures shaded tests and distribution copying.
core/​src/​test/​java/​org/​apache/​gravitino/​job/​TestJobExecutorFactory.java Tests missing executor class diagnostics.
core/​src/​main/​java/​org/​apache/​gravitino/​job/​JobExecutorFactory.java Registers Kubernetes support and improves loading errors.
build.gradle.kts Includes the plugin in distribution assembly.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Code Coverage Report

Overall Project 72.0% +0.03% 🟢
Files changed 73.15% 🟢

Module Coverage
aliyun 19.74% 🔴
api 55.09% 🟢
authorization-common 85.96% 🟢
authorization-ranger 14.44% 🔴
aws 53.54% 🟢
aws-bundle 0.0% 🔴
azure 32.1% 🔴
azure-bundle 0.0% 🔴
catalog-common 29.49% 🔴
catalog-fileset 82.37% 🟢
catalog-glue 73.24% 🟢
catalog-hive 83.63% -1.71% 🟢
catalog-jdbc-common 57.98% 🟢
catalog-jdbc-doris 87.56% 🟢
catalog-jdbc-mysql 81.91% 🟢
catalog-jdbc-postgresql 84.02% 🟢
catalog-jdbc-starrocks 79.16% 🟢
catalog-kafka 75.21% 🟢
catalog-lakehouse-generic 84.58% 🟢
catalog-lakehouse-hudi 79.1% 🟢
catalog-lakehouse-iceberg 85.93% -1.33% 🟢
catalog-lakehouse-paimon 84.36% 🟢
catalog-model 77.99% 🟢
cli 44.62% 🟢
client-java 79.44% 🟢
client-java-runtime 0.0% 🔴
common 60.78% 🟢
core 87.54% +0.19% 🟢
filesystem-hadoop3 76.89% 🟢
flink 55.44% 🟢
flink-common 32.2% 🔴
flink-runtime 0.0% 🔴
gcp 32.2% 🔴
hadoop-auth 68.0% 🟢
hadoop-common 17.84% 🔴
hive-metastore-common 62.3% 🟢
hive-metastore3-libs 58.8% 🟢
iceberg-aliyun-bundle 0.0% 🔴
iceberg-common 66.53% 🟢
iceberg-rest-server 76.94% 🟢
idp-basic 86.77% 🟢
integration-test-common 0.0% 🔴
jobs 71.35% 🟢
k8s-job-executor 97.81% -1.26% 🟢
lance-common 39.9% 🔴
lance-rest-server 69.51% 🟢
lineage 59.39% 🟢
optimizer 84.27% 🟢
optimizer-api 46.58% 🟢
server 91.43% +0.33% 🟢
server-common 83.86% 🟢
spark 61.79% 🟢
tencent 81.78% 🟢
tms 80.0% 🟢
trino-connector 63.31% -6.17% 🟢
Files
Module File Coverage
catalog-hive HiveViewCatalogOperations.java 78.9% 🟢
catalog-lakehouse-iceberg IcebergViewCatalogOperations.java 78.47% 🟢
core JobEventDispatcher.java 100.0% 🟢
Configs.java 99.18% 🟢
HiddenPropertyMaskUtils.java 97.92% 🟢
JobInfo.java 97.06% 🟢
JobExecutorFactory.java 90.0% 🟢
k8s-job-executor K8sJobExecutorConfigs.java 100.0% 🟢
K8sJobResourceUtils.java 100.0% 🟢
SparkApplicationUtils.java 100.0% 🟢
K8sJobExecutor.java 93.8% 🟢
server JobOperations.java 97.0% 🟢
trino-connector GravitinoErrorCode.java 100.0% 🟢
GeneralDataTypeTransformer.java 89.19% 🟢
GravitinoConnector.java 61.31% 🟢
CatalogConnectorMetadata.java 39.31% 🔴
JsonCodec.java 34.25% 🔴
CatalogConnectorMetadataAdapter.java 33.33% 🔴
GravitinoMetadata.java 31.6% 🔴
CatalogConnectorContext.java 5.66% 🔴
CatalogConnectorAdapter.java 0.0% 🔴

@jerryshao jerryshao left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: ship it

Reviewed 23bc020 (12 files, +516/-15) against origin/main (361b1e6), reading every changed file in full and grepping for callers of each moved or added symbol.

What I verified

  • The registration path is complete end to end: addJarInDir "${GRAVITINO_HOME}/libs" (bin/gravitino.sh.template:186, helper at bin/common.sh.template:98-102) puts libs/* on the server classpath; :plugins:k8s-job-executor:copyLibAndConfigs is in the single compileDistribution list (build.gradle.kts:1189) and drops the shaded jar into distribution/package/libs, matching the pattern of plugins/idp-basic/build.gradle.kts:70-84.
  • The builtin- substitution cannot be triggered by a user template: JobTemplateValidationDispatcher.validateNotBuiltInTemplateName (core/src/main/java/org/apache/gravitino/job/JobTemplateValidationDispatcher.java:146-153) rejects user names with that prefix, and the dispatcher is wired in GravitinoEnv.java:1064.
  • The substitution covers the whole server-path surface of built-in jobs: every built-in template sets only withExecutable(resolveExecutable(...)); grep -rn "withJars|withFiles|withArchives" maintenance/jobs/src/main returns nothing, so no built-in template carries extra server-side resources that would still be rejected.
  • Moving the scheme check into K8sJobResourceUtils.isClusterReachable is behaviour-preserving: the anchored ^([A-Za-z][A-Za-z0-9+.-]*): with find() behaves as lookingAt(), and the single-letter and file exclusions are carried over verbatim from the old checkClusterReachable.
  • The constants the plugin pulls from core (JobExecutorFactory.K8S_JOB_EXECUTOR_NAME, JobTemplateProvider.BUILTIN_NAME_PREFIX) are compile-time String constants, so they inline and add no runtime dependency on core from the shaded jar.
  • The new shadedJarTest task is picked up by the root tasks.configureEach<Test> (build.gradle.kts:981), so it inherits useJUnitPlatform(); its Copy into the distribution picks up dependsOn(cleanDistributionPackage) from build.gradle.kts:1631-1637.
  • No legal change is needed: LICENSE.bin already lists Jackson (line 350), SnakeYAML / SnakeYAML Engine (359-360) and Fabric8 Kubernetes Client / Model / zjsonpatch (411-413). No other module declares fabric8 (grep -rn fabric8 --include=*.kts), so the relocation prefix has no cross-module conflict.

Tests: adequate. The new unit tests cover the default and overridden spark.builtinJobsJar, the three invalid forms, both substituted and kept executables, a my-builtin-job name that must not match the prefix, and a built-in job whose extra jars are still rejected; the shadedJarTest smoke test is the valuable addition, since it exercises relocation, class overlap and a real submission through JobExecutorFactory. I did not find a branch introduced here that is unexercised.

I could not execute the tests in this sandbox: Gradle failed to resolve its plugin dependencies (HTTP 429 from repo.maven.apache.org), so every finding below comes from reading the code, not from a run. The findings are all non-blocking.

Review drafted with Claude, requested by @jerryshao.

Comment thread core/src/main/java/org/apache/gravitino/job/JobExecutorFactory.java

@jerryshao jerryshao left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: ship it

Reviewed 23bc020 (12 files, +516/-15) against origin/main (361b1e6), reading every changed file in full and grepping for callers of each moved or added symbol.

What I verified

  • The registration path is complete end to end: addJarInDir "${GRAVITINO_HOME}/libs" (bin/gravitino.sh.template:186, helper at bin/common.sh.template:98-102) puts libs/* on the server classpath; :plugins:k8s-job-executor:copyLibAndConfigs is in the single compileDistribution list (build.gradle.kts:1189) and drops the shaded jar into distribution/package/libs, matching the pattern of plugins/idp-basic/build.gradle.kts:70-84.
  • The builtin- substitution cannot be triggered by a user template: JobTemplateValidationDispatcher.validateNotBuiltInTemplateName (core/src/main/java/org/apache/gravitino/job/JobTemplateValidationDispatcher.java:146-153) rejects user names with that prefix, and the dispatcher is wired in GravitinoEnv.java:1064.
  • The substitution covers the whole server-path surface of built-in jobs: every built-in template sets only withExecutable(resolveExecutable(...)); grep -rn "withJars|withFiles|withArchives" maintenance/jobs/src/main returns nothing, so no built-in template carries extra server-side resources that would still be rejected.
  • Moving the scheme check into K8sJobResourceUtils.isClusterReachable is behaviour-preserving: the anchored ^([A-Za-z][A-Za-z0-9+.-]*): with find() behaves as lookingAt(), and the single-letter and file exclusions are carried over verbatim from the old checkClusterReachable.
  • The constants the plugin pulls from core (JobExecutorFactory.K8S_JOB_EXECUTOR_NAME, JobTemplateProvider.BUILTIN_NAME_PREFIX) are compile-time String constants, so they inline and add no runtime dependency on core from the shaded jar.
  • The new shadedJarTest task is picked up by the root tasks.configureEach<Test> (build.gradle.kts:981), so it inherits useJUnitPlatform(); its Copy into the distribution picks up dependsOn(cleanDistributionPackage) from build.gradle.kts:1631-1637.
  • No legal change is needed: LICENSE.bin already lists Jackson (line 350), SnakeYAML / SnakeYAML Engine (359-360) and Fabric8 Kubernetes Client / Model / zjsonpatch (411-413). No other module declares fabric8 (grep -rn fabric8 --include=*.kts), so the relocation prefix has no cross-module conflict.

Tests: adequate. The new unit tests cover the default and overridden spark.builtinJobsJar, the three invalid forms, both substituted and kept executables, a my-builtin-job name that must not match the prefix, and a built-in job whose extra jars are still rejected; the shadedJarTest smoke test is the valuable addition, since it exercises relocation, class overlap and a real submission through JobExecutorFactory. I did not find a branch introduced here that is unexercised.

I could not execute the tests in this sandbox: Gradle failed to resolve its plugin dependencies (HTTP 429 from repo.maven.apache.org), so every finding below comes from reading the code, not from a run. The findings are all non-blocking.

Review drafted with Claude, requested by @jerryshao.

Comment thread core/src/main/java/org/apache/gravitino/job/JobExecutorFactory.java
jerryshao and others added 2 commits October 16, 2026 20:18
- Log the jobs jar that a built-in job runs instead of the template's
  executable, which is what the job records, and the configured
  `spark.builtinJobsJar` when the executor starts.
- Warn when `gravitino.jobExecutor.<name>.class` is set for a built-in job
  executor name, as it is ignored, and say in the docs that `local` and `k8s`
  are reserved.
- Say which classpath the overlap check of the shaded jar test covers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Subtask] Register the k8s job executor, ship it in the distribution, and run built-in jobs on it

3 participants