Skip to content

feat(run): add content-addressed source bundles - #36

Open
Kevin Cho (chokevin) wants to merge 1 commit into
mainfrom
chokevin-add-source-bundles
Open

Kevin Cho (chokevin) wants to merge 1 commit into
mainfrom
chokevin-add-source-bundles

Conversation

@chokevin

Copy link
Copy Markdown
Contributor

Summary

Add first-class content-addressed source bundles for direct tau run Jobs and RayJobs. Researchers can configure:

run:
  entrypoint: project/train.py
  source_bundle:
    path: project
    excludes: [large-data/**]
    digest: sha256:<optional-64-lowercase-hex>

Tau deterministically archives the local tree, stages it once at /data/tau/source-bundles/sha256/<hex>.zip on the workspace data PVC, and records the digest/PVC/path in workload annotations, status, and lifecycle history. Job and Ray pods verify the exact digest and every ZIP member before use; Jobs safely extract into an emptyDir, while Ray uses the shared file:// working-dir reference.

The implementation fails closed for missing, corrupt, unsafe, or mismatched content; preserves safe executable bits; handles concurrent same-digest staging; excludes repository credentials and metadata; and never serializes source bytes into Kubernetes objects, Secrets, environment variables, or arguments. Client and server dry-runs validate and render without staging.

AI tooling assisted with implementation and adversarial review. All findings were independently reconciled and the resulting behavior was validated with the repository's existing test and build workflows.

Related issue

N/A

Validation

cd cli && go test -count=1 ./... && go vet ./... && make build
cd core && go test -count=1 ./... && go vet ./...
helm unittest charts/tau-core-controller
helm lint charts/tau-core-controller
helm template taugrid charts/tau-core-controller --namespace taugrid-system
kubectl kustomize charts/tau-core-controller
bash -n controllers/tau-core/scripts/kind-e2e.sh
cd site && python3 scripts/check_content.py content/en/docs
cd site && python3 scripts/check_wiki_parity.py

All passed. Claude Opus 4.8 adversarial review: no blockers; one accepted Job import-path warning was fixed and covered.

Compatibility and operational impact

  • Existing Ray-only run.working_dir behavior and its fail-fast 64 KiB inline limit are unchanged. source_bundle is direct-run-only and mutually exclusive with working_dir.
  • Bundle input retains the existing 8 MiB uncompressed project limit; archives can exceed the 64 KiB inline limit.
  • Source-bundle mode uses storage.data_pvc, defaulting to blob-training when omitted. Multi-node RayJobs require a ReadWriteMany data PVC.
  • Workload images must include python3 for bundle verification and Job extraction.
  • The workspace researcher role gains namespace-scoped Pod create/get/list/watch/delete and pods/exec create permissions for the short-lived staging helper. It gains no Secret, PVC-write-API, patch/update, or cluster-scoped permissions.
  • Digest archives are shared immutable objects and are not deleted with individual runs. Operators must configure retention/GC for the source-bundle prefix while preserving digests referenced by live or retained workloads.
  • No CRD or dependency changes.

Checklist

  • The change is focused and does not include unrelated edits.
  • Tests cover the changed behavior, or I explained why tests are not needed.
  • Documentation and examples are updated when public behavior changes.
  • Generated files are updated from their source and included in this pull request.
  • New dependencies are publicly available and their license and provenance were reviewed.
  • The diff contains no secrets, private endpoints, customer data, or Microsoft-internal information.
  • Fork-based CI can validate the change without repository secrets, or maintainer-only validation is clearly identified.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Some pipeline(s) encountered errors during trigger evaluation.

@feiskyer

Copy link
Copy Markdown
Member

Kevin Cho (@chokevin) this PR needs rebase

// MaxInputBytes is deliberately the same limit imposed on working_dir.
MaxInputBytes = 8 << 20
DurableRoot = "/data/tau/source-bundles/sha256"
helperImage = "busybox:1.36.1@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P0 This helper image bypasses the repository's MCR image policy (AGENTS.md: "Container images go through MCR"). Every real source-bundle submission creates this pod before the workload; clusters enforcing that policy reject or pull-block it, so staging aborts. Use an approved MCR-hosted helper image.

target.mkdir(parents=True, exist_ok=True)
for info, normalized in members:
destination = (target / pathlib.PurePosixPath(normalized)).resolve()
if not destination.is_relative_to(target):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P1 Path.is_relative_to() requires Python 3.9, while the documented runtime contract only requires python3. Images on Python 3.8 raise an uncaught AttributeError in the bundle init container, so the workload never starts. Use a relative_to()/ValueError containment check, or explicitly require and validate Python 3.9+.

}
return []string{
"bash", "-c",
pythonPathPrefix + "exec " + shellQuote("./"+entrypoint) + argList.String() + extraFlagStr,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P1 This source-bundle path bypasses the existing shebang handling and directly executes the extracted file. A valid shebang entrypoint stored as mode 0644 is preserved as non-executable, so exec ./... fails with permission denied; a non-Python shebang in a .py file is also sent to Python. Restore shebang-based interpreter selection or stage executable mode consistently.

return err
}
}
if err := stageRunSourceBundle(ctx, o.dryRun, runner, ns, pvcMount, request.Name, source); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P1 This stages the archive only on the submission cluster before MultiKueue dispatch. The worker receives the workload object and path but not the archive bytes; with independently provisioned same-named PVCs, its init container cannot find the bundle and the run never starts. Reject this combination or stage/mirror the bundle on the selected worker; the Ray path has the same issue.

moduleName := strings.TrimSuffix(o.ScriptName, ".py")
if o.SourceBundle != nil {
var err error
moduleName, err = entrypointModule(o.SourceBundle.Entrypoint)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P1 For a nested entrypoint such as pkg/train.py, this exports pkg.train, but the Tune driver loads it with bare __import__, which returns the top-level pkg. getattr(pkg, "train_func") then fails before Tune starts unless the package re-exports the function. Load it with importlib.import_module, or change the generated module contract.

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