Skip to content

feat(sparkeks): add JAR/--class entrypoint support (chipmunk EMR->EKS migration, UoW-F3) - #126

Open
prasadlohakpure wants to merge 9 commits into
mainfrom
feat/chipmunk_sparkeks_jar_support
Open

feat(sparkeks): add JAR/--class entrypoint support (chipmunk EMR->EKS migration, UoW-F3)#126
prasadlohakpure wants to merge 9 commits into
mainfrom
feat/chipmunk_sparkeks_jar_support

Conversation

@prasadlohakpure

@prasadlohakpure prasadlohakpure commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

What

Adds JAR/--class entrypoint support to the Heimdall sparkeks plugin so it can run a custom-class
(Java/Scala) Spark application, not just the built-in Python SQL wrapper. Additive and opt-in — no
existing sparkeks command sets a JAR wrapper, so current behavior is unchanged.

Entrypoint strategy (internal/pkg/object/command/sparkeks/entrypoint.go) — the entrypoint-specific
part of the spec (Type / MainClass / Arguments) is dispatched behind an entrypointStrategy
interface, selected by the file extension of the command's wrapper_uri:

wrapper_uri Entrypoint Spec.Type Spec.MainClass
.py Python SQL wrapper (pre-existing) Python not set
.jar JVM JAR by main class Scala / Java (from application_type, default Scala) entry_point
anything else falls back to SQL wrapper Python not set

New optional job-context fields: parameters.entry_point (fully-qualified main class) and
parameters.application_type (Scala or Java, case-insensitive).

Argument contract. Both entrypoints submit the same positional vector, matching the deployed
com.pattern.chipmunk.Writer:

[appName, queryURI, user]              # return_result = false
[appName, queryURI, user, resultURI]   # return_result = true

queryURI is an s3a:// URI — the application reads the SQL from it rather than receiving it inline.
Only Type and MainClass differ between the JAR and SQL-wrapper paths.

s3:// vs s3a:// in properties. Cluster/job properties are no longer blanket-rewritten
s3://s3a://. Only keys read through Hadoop's FileSystem abstraction need s3a://, so rewriting
is restricted to an explicit hadoopPathProperties set (driver/executor podTemplateFile). App-specific
keys such as spark.chipmunk.output.path are read by application code via the AWS SDK, which requires
the literal s3:// scheme. Plugin-managed URIs (query, result, event log, wrapper) are still always
rewritten. ⚠️ Reviewers: this narrows an earlier commit in this branch — worth confirming no existing
cluster config depends on a non-podTemplateFile property being rewritten.

Also in this branch: submission-failure/unknown-state reasons are now written to job stderr, and
s3aQueryURI/s3aResultURI are precomputed once on the execution context.

jobContext.arguments is retained for config compatibility but is ignored — both strategies use a
fixed 3/4-element vector, so there is no positional slot to append to. Job-specific inputs travel as
SparkConf properties instead.

Why

Foundation step (UoW-F3) of migrating Chipmunk's collection-build Spark jobs off EMR-on-EKS onto
Spark-on-EKS (sparkeks). The chipmunk collection writer is a Scala JAR
(com.pattern.chipmunk.Writer); sparkeks could previously only launch the Python SQL wrapper, so it
could not run that JAR. This unlocks the spark-chipmunk-eks command + operator wiring (step 4).

Migration roadmap (where this PR fits)

Foundation phase. Steps 1–3 are mutually independent — none is consumed until step 4 wires them together
— so they are reviewable in parallel. Step 4 gates on all three.

Step Unit Repo(s) Status
1 Add Snowflake jars to sparkeks image data-platform-tf raised — patterninc/data-platform-tf#1836
2 Mount snowflake-key secret on sparkeks driver heimdall-config raised — patterninc/heimdall-config#444
3 JAR/--class support in sparkeks plugin heimdall ▶️ this PR
4 spark-chipmunk-eks command + ChipmunkCollection(use_eks=) operator (depends on 1–3) heimdall-config + data-airflow designed, not implemented
Shadow-parity validation harness (off critical path; required before cutover validation) TBD pending

After the foundation lands: per-pipeline cutovers (chipmunk_daily, product_catalog_chipmunk_daily,
amazon_merchant_asins_daily, chipmunk_org_collection, sensors_universal, core_dims_hourly), each
adding the sparkeks path alongside EMR → validating → cutting over, with EMR removal as a separate later
PR per pipeline. Then EMR-on-EKS decommission, gated on FinOps sign-off.

Test plan

  • go build ./internal/... ./cmd/... — clean.
  • go vet ./internal/pkg/object/command/sparkeks/ — clean.
  • gofmt -l internal/pkg/object/command/sparkeks/ — clean.
  • go test ./internal/pkg/object/command/sparkeks/ — ok (existing package tests).
  • No new unit tests in this PR (the ones added earlier in the branch were removed per request).
    The JAR argument contract was verified against the deployed chipmunk JAR by running it on the
    chipmunk_eks_migration_test collection, not by unit test.

Notes / rollback

  • Rollback: revert the branch — additive and opt-in; no existing command sets a .jar wrapper_uri.
    The one non-additive piece is the properties-rewriting narrowing described above.
  • Pre-existing, unrelated: go build ./... across the whole repo reports "function main is undeclared"
    for every plugins/* package — these are -buildmode=plugin packages with no func main() and fail
    the same way on an untouched origin/main checkout. Not introduced by this PR.

🤖 Generated with Claude Code

… migration UoW-F3)

Enables the sparkeks plugin to run a custom-class (Scala/Java) Spark application
via new optional jobParameters.EntryPoint + jobContext.Arguments. JAR mode sets
Type:Scala/MainClass and builds args [appName, user, query, ...arguments,
(resultURI)] server-side, matching com.pattern.chipmunk.Writer. SQL-wrapper path
unchanged. Defensive default Spec.Type=Python guards templateless jobs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 23, 2026 07:21

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.

Pull request overview

Adds opt-in JAR/--class entrypoint support to the sparkeks plugin so it can launch JVM Spark applications (e.g., Chipmunk Writer) in addition to the existing SQL-wrapper path, with a defensive fallback to ensure SparkApplication.Spec.Type is never submitted empty.

Changes:

  • Introduces entry_point and arguments job-context fields to enable JAR-mode SparkApplication submission.
  • Updates SparkApplication spec generation to support a JAR-mode argument vector and to default Spec.Type when not set by templates.
  • Adds unit tests covering JAR mode, JAR+return_result, SQL-wrapper non-regression, and the default-Type fallback.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
internal/pkg/object/command/sparkeks/sparkeks.go Adds JAR-mode (--class) SparkApplication configuration and a defensive default for Spec.Type.
internal/pkg/object/command/sparkeks/sparkeks_test.go Adds tests validating the new JAR-mode behavior and guarding existing SQL-wrapper behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// - SQL-wrapper (Parameters.EntryPoint unset — existing behavior, byte-for-byte
// unchanged below): wrapper_uri points at a .py wrapper that reads the query from
// queryURI.
if execCtx.commandContext.WrapperURI != "" {
sparkApp.Spec.Arguments = []string{execCtx.appName, s3aQueryURI, execCtx.job.User, s3aResultURI}
sparkApp.Spec.MainApplicationFile = &mainAppFile

if jobContext.Parameters != nil && jobContext.Parameters.EntryPoint != "" {
prasadlohakpure and others added 8 commits July 23, 2026 14:09
Extract entrypoint-specific spec (Type/MainClass/Arguments) into an
entrypointStrategy interface with jarEntrypointStrategy and
sqlWrapperEntrypointStrategy, selected by newEntrypointStrategy. Replaces the
inline if/else in applySparkOperatorConfig. Behavior unchanged — all 4 unit
tests still pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…dded tests

Move entrypointStrategy + jar/sqlWrapper strategies + newEntrypointStrategy and
jarApplicationType into internal/pkg/object/command/sparkeks/entrypoint.go.
sparkeks.go keeps applySparkOperatorConfig (delegates to newEntrypointStrategy)
and the Gap-4 default. Revert the added sparkeks_test.go unit tests per request.
Build + vet clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…; multi JAR type

- Select entrypoint strategy from wrapper_uri extension via a map
  (entrypointStrategiesByExt: .jar -> JAR, .py -> SQL wrapper), default SQL wrapper.
- Generalize JAR application type: jarApplicationTypes map + resolveJarApplicationType,
  configurable via new jobParameters.ApplicationType (Scala/Java), default Scala.
- Behavior-equivalent to the previous EntryPoint-based selection for all real commands;
  more extensible. Build + vet clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
monitorJobAndCollectLogs wrote the real error to stderr for
ApplicationStateFailed, but the FailedSubmission/Unknown branch
returned only a generic constant and never wrote anything to
e.runtime.Stderr, silently dropping sparkApp.Status.AppState.ErrorMessage.
applySparkOperatorConfig already normalized WrapperURI, EventLogURI,
queryURI, and resultURI via updateS3ToS3aURI, but merged arbitrary
job/cluster properties into SparkConf verbatim. Any property value
referencing a raw s3:// path (e.g. spark.kubernetes.driver.podTemplateFile)
hit Hadoop's UnsupportedFileSystemException, since only s3a:// is
registered without extra config.
The JAR strategy submitted [appName, user, query, ...arguments, (resultURI)] with the
query inlined as literal SQL text. The deployed com.pattern.chipmunk.Writer actually
takes the same shape as the Python SQL wrapper — [appName, queryURI, user, (resultURI)]
— reading the SQL from the URI. Only Type/MainClass differ between the two entrypoints
now.

Follow-on changes:

- Precompute s3aQueryURI/s3aResultURI once on executionContext instead of rewriting at
  each use site.
- Stop blanket-rewriting s3:// -> s3a:// across all cluster/job Properties. Only keys
  read through Hadoop's FileSystem abstraction need s3a://, so restrict rewriting to an
  explicit hadoopPathProperties set (driver/executor podTemplateFile). App-specific keys
  such as spark.chipmunk.output.path are read by application code via the AWS SDK, which
  requires the literal s3:// scheme.
- jobContext.Arguments is now unread by both strategies: there is no positional slot left
  to append to. Documented as ignored/retained for config compatibility; job-specific
  inputs travel as SparkConf properties.
- plugins/sparkeks/README.md: document JAR/--class support — extension-based entrypoint
  dispatch, entry_point/application_type, the shared argument contract, and the
  s3:// vs s3a:// property rule.

Co-Authored-By: Claude Opus 5 (1M context) <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.

2 participants