Bake provenance JSON into images and auto-populate post_results.py args - #338
Bake provenance JSON into images and auto-populate post_results.py args#338misiugodfrey wants to merge 10 commits into
Conversation
Writes /opt/velox-testing/provenance.json into each Presto image at build time (both native_build.dockerfile and provenance_labels.dockerfile), so run_context.py can read it from the container filesystem in both Docker and SLURM/Enroot environments. run_context.py: reads the provenance file and merges presto_sha/branch/repo and velox_sha/branch/repo into the benchmark context dict. post_results.py: auto-populates --velox-branch, --velox-repo, --presto-branch, --presto-repo from the benchmark_result.json context section; CLI args still take precedence. BenchmarkMetadata gains 6 optional provenance fields. presto-build.yml: replaces the labels: input on the deps and coordinator build steps with a second provenance_labels.dockerfile wrapper step, so CI and local builds both write the provenance file (buildx labels: applies OCI metadata only and does not execute RUN steps).
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
The provenance.json baked into images at /opt/velox-testing/provenance.json
is only visible from inside the container, but pytest runs on the host in
the Docker flow (run_benchmark.sh:294) and inside the coord container in
SLURM/Enroot. The host has no /opt/velox-testing/provenance.json, so
run_context.py's _get_image_provenance() returned {} and the six provenance
fields were silently missing from benchmark_result.json's context.
Have each launch script copy the baked-in file into LOGS_DIR at startup
(worker_provenance.json from launch_presto_servers.sh, coordinator_provenance.json
from launch_coordinator.sh). In Docker the dir is bind-mounted to the host,
so pytest reads it directly. In SLURM, add ${LOGS}:/opt/presto-server/logs
to both coord and worker srun --container-mounts so the same scheme works
across the cluster, and export LOGS_DIR=/opt/presto-server/logs in
run_queries so the in-container pytest looks in the right place.
run_context.py replaces the hardcoded path constant with a resolver that
prefers worker_provenance.json (full presto+velox fields) over
coordinator_provenance.json (presto only in CI builds), then falls back to
the original /opt/velox-testing/provenance.json for callers running
directly inside a container that has the file baked in.
…nance # Conflicts: # presto/slurm/presto-nvl72/functions.sh
The 'Apply provenance labels to Presto deps image' step reconstructed the deps image tag by hand but omitted the build_variant and run_id segments that the build-and-push step includes, so it resolved a tag that was never pushed (buildx: 'not found'). Align BASE_IMAGE and the output tag with the deps build tag.
Add presto_sha/velox_sha as their own engine_config entries alongside the existing branch/repo fields, sourced from the baked image provenance in the benchmark context. Provenance fields are now emitted individually with empties omitted, so a coordinator-only image contributes just its presto entries. Derive query_engine.commit_hash from the SHAs as 'presto-<sha>-velox-<sha>' (or 'presto-<sha>' when velox is absent) when --commit-hash is not given; explicit --commit-hash still wins, and it stays 'unknown' when no SHAs exist. Degrades gracefully on older result files with no provenance.
| build-args: | | ||
| CUDA_VERSION=${{ matrix.cuda_version }} | ||
| ARM_BUILD_TARGET=generic | ||
| labels: | |
There was a problem hiding this comment.
Docker labels were lost if we convert docker images to sqsh images, so instead we store that information in a provenance.json file which is preserved inside the image regardless of conversion.
| validation_results: dict | None = None, | ||
| velox_branch: str | None = None, | ||
| velox_repo: str | None = None, | ||
| velox_sha: str | None = None, |
There was a problem hiding this comment.
Usually only branch or sha is used, but decided it was better to have separate values than try to overload the meaning of one value.
| # Derive commit_hash from the image provenance SHAs when not explicitly given: | ||
| # "presto-<sha>-velox-<sha>" (worker), "presto-<sha>" (coordinator, no velox). | ||
| if commit_hash is None: | ||
| commit_hash = "unknown" |
There was a problem hiding this comment.
commit_hash doesn't apply well to presto-velox builds since they are combination, but this is a top-level value in the benchmarking DB, so it seems useful to fill it with a concatenation of both.
The deps and coordinator provenance wrapper steps were near-identical build-push-action invocations differing only in base image, tags, and the velox fields. Extract them into .github/actions/apply-provenance-labels so the pinned action SHA, provenance dockerfile wiring, and outputs live in one place (mirroring the repo's existing container-registry-login/delete-arch-tags composite actions), removing the risk of the two steps drifting. Coordinator omits the velox_* inputs (default empty), preserving its presto-only provenance. Behaviour is unchanged.
ruff format (v0.14.x) expands the frozenset literal to one element per line; run the formatter to satisfy the check-style pre-commit hook.
patdevinwilson
left a comment
There was a problem hiding this comment.
Nice approach — baking a readable provenance.json (instead of relying on OCI labels that disappear in .sqsh conversion) and surfacing it via LOGS_DIR is clean. A few nits below; otherwise LGTM once the real submission path is confirmed.
| velox-testing.velox.branch=${VELOX_BRANCH} \ | ||
| velox-testing.velox.repository=${VELOX_REPOSITORY} | ||
| RUN mkdir -p /opt/velox-testing && \ | ||
| printf '{"presto_sha":"%s","presto_branch":"%s","presto_repo":"%s","velox_sha":"%s","velox_branch":"%s","velox_repo":"%s"}\n' \ |
There was a problem hiding this comment.
Minor robustness nit: building the JSON with printf/%s will produce invalid JSON if a branch/repo ever contains ", \, or a newline. SHAs are safe, but branch names aren't guaranteed. Consider writing via Python/jq (jq -n --arg ...) so values are escaped, or document that only plain git refs are supported.
There was a problem hiding this comment.
Note, the same printf pattern in native_build.dockerfile would need the corresponding change.
| echo \"Worker ${worker_id}: KVIKIO_NTHREADS=\${KVIKIO_NTHREADS:-unset}\" | ||
|
|
||
| if [ -f /opt/velox-testing/provenance.json ]; then | ||
| cp /opt/velox-testing/provenance.json /opt/presto-server/logs/worker_provenance.json |
There was a problem hiding this comment.
With multi-worker runs, every worker writes the same path worker_provenance.json. That's fine if images are identical (last writer wins with the same content), but worth a one-line comment so nobody later tries to make this per-worker without also updating run_context.py.
| logs_dir = os.environ.get("LOGS_DIR") | ||
| if logs_dir: | ||
| ld = Path(logs_dir) | ||
| for name in ("worker_provenance.json", "coordinator_provenance.json"): |
There was a problem hiding this comment.
Preferring worker over coordinator provenance here matches the CI shape (worker has presto+velox; coordinator is presto-only). Good call. One edge case: if only the coordinator file is present (e.g. worker copy failed), you still get useful presto fields — nice fallback chain.
There was a problem hiding this comment.
I think these fixed filenames can cause us to report provenance from an earlier run. start_presto_helper.sh archives only *.log, so worker_provenance.json survives between starts. If the next run uses Java, which doesn't write this file, or an older worker image without baked provenance, we will still prefer the stale worker file over the current coordinator file.
Could we clear both provenance files when setting up LOGS_DIR, or make the filenames run-specific using SERVER_START_TIMESTAMP? Clearing them at startup seems like the smaller fix.
| commit_parts.append(f"presto-{presto_sha}") | ||
| if velox_sha: | ||
| commit_parts.append(f"velox-{velox_sha}") | ||
| commit_hash = "-".join(commit_parts) if commit_parts else "unknown" |
There was a problem hiding this comment.
Agree with your note that commit_hash is awkward for a dual-repo build. The presto-<sha>-velox-<sha> form is clear for humans/DB. One thing to confirm with a real (non-dry-run) submission: does the benchmarking DB impose a max length / charset on commit_hash? Full SHAs make this ~85 chars.
| presto_branch = presto_branch or benchmark_metadata.presto_branch | ||
| presto_repo = presto_repo or benchmark_metadata.presto_repo | ||
| # SHAs come only from the baked image provenance (no CLI override). | ||
| velox_sha = benchmark_metadata.velox_sha |
There was a problem hiding this comment.
Asymmetry is intentional but slightly surprising: branch/repo allow CLI override, SHAs do not. Worth a sentence in the --help text (or PR description is enough) so operators don't expect --velox-sha.
| uses: ./velox-testing/.github/actions/apply-provenance-labels | ||
| with: | ||
| base_image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:presto-deps-${{ inputs.presto_short_sha }}-velox-${{ inputs.velox_short_sha }}-cuda${{ matrix.cuda_version }}-${{ inputs.date }}-${{ inputs.build_variant }}-${{ github.run_id }}-${{ matrix.arch }} | ||
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:presto-deps-${{ inputs.presto_short_sha }}-velox-${{ inputs.velox_short_sha }}-cuda${{ matrix.cuda_version }}-${{ inputs.date }}-${{ inputs.build_variant }}-${{ github.run_id }}-${{ matrix.arch }} |
There was a problem hiding this comment.
Good catch fixing the deps tag reconstruction. Overwriting the same tag with the provenance-wrapped image means the non-provenance digest is briefly published then replaced — attestation correctly uses steps.push after this step. If anything external could pull between the two pushes, consider pushing provenance under a temp tag then moving, but probably fine for this registry flow.
|
|
||
| # Fall back to image provenance labels captured in the benchmark context. | ||
| velox_branch = velox_branch or benchmark_metadata.velox_branch | ||
| velox_repo = velox_repo or benchmark_metadata.velox_repo |
There was a problem hiding this comment.
I think we should normalize these repository values before persisting them. Local builds populate them from the raw output of git remote get-url origin, and HTTPS remotes can contain credentials such as https://user:TOKEN@github.com/.... This fallback would submit that value in engine_config. It would also already be present in the baked file, shared logs, and benchmark result.. yet, we definitely don't want them in the engine_config.
Could we strip URL userinfo in capture_build_provenance and store either owner/repo or a credential-free URL? Sanitizing at capture time seems preferable because it protects every downstream artifact, not just the API submission.
| # Surface baked-in image provenance in the shared logs dir so the host-side | ||
| # pytest (Docker) or in-container pytest (SLURM) can read it via LOGS_DIR. | ||
| if [ -f /opt/velox-testing/provenance.json ]; then | ||
| cp /opt/velox-testing/provenance.json "${LOGS_DIR}/coordinator_provenance.json" |
There was a problem hiding this comment.
Should we mirror this in launch_java_worker.sh? The Java worker image also gets provenance baked into it, but its launcher never copies that file into the shared logs directory, so Java benchmarks always fall back to the coordinator's provenance. This also was noted in my earlier comment in regards to run_context.py.
Summary
Capture the Presto/Velox source that built an image and thread it into benchmark reports so results are traceable to their build. Follow-up to #316 (image labels).
/opt/velox-testing/provenance.jsoninto each Presto image at build time (bothnative_build.dockerfileandprovenance_labels.dockerfile). Unlike the OCI labels from Add image labels #316, a file is readable from inside a running container.launch_presto_servers.sh,launch_coordinator.sh) copies that file into the sharedLOGS_DIRat container startup asworker_provenance.json/coordinator_provenance.json, sorun_context.pyrunning on the host (Docker) or inside the coordinator container (SLURM/Enroot) can read it via the existing logs bind-mount. The in-container/opt/velox-testing/provenance.jsonpath is kept as a fallback for callers running directly inside an image that has the file baked in.functions.sh: adds${LOGS}:/opt/presto-server/logsto both coordinator and workersrun --container-mountsso the same scheme works across the cluster, mirrors the provenancecpin the inline worker bash andCOORD_SCRIPT, and exportsLOGS_DIR=/opt/presto-server/logsinrun_queriesso the in-container pytest looks at the mounted path.run_context.pyreads the provenance file and mergespresto_sha/branch/repoandvelox_sha/branch/repointo the benchmark context dict written tobenchmark_result.json. Empty values are dropped, and the resolver prefersworker_provenance.json(full presto+velox fields) overcoordinator_provenance.json(presto only on CI builds).post_results.pysubmission:--velox-branch,--velox-repo,--presto-branch,--presto-repofrom thebenchmark_result.jsoncontext; explicit CLI args still take precedence.presto_sha/velox_shaas their ownengine_configentries alongside branch/repo. Provenance fields are added individually with empties omitted, so a coordinator-only image contributes just its presto entries.query_engine.commit_hashfrom the SHAs aspresto-<sha>-velox-<sha>(orpresto-<sha>when velox is absent) when--commit-hashis not given; explicit--commit-hashstill wins, and it staysunknownwhen no SHAs exist.commit_hash=unknown, no error).presto-build.yml:labels:input on the deps and coordinator build steps with a secondprovenance_labels.dockerfilewrapper step — buildxlabels:applies OCI metadata only and does not executeRUNsteps, so the provenance file would not have been written via that path.-<build_variant>-<run_id>segments, so buildx failed withnot found).Notes / follow-ups
commit_hash = presto-<sha>-velox-<sha>, and branch fields only appear for locally-built images that baked a branch.engine_configkeys (presto_sha/velox_sha, and the existing branch/repo keys) have so far only been exercised via--dry-run; server-side acceptance should be confirmed with a real submission.Test plan
presto/scripts/start_native_cpu_presto.shdocker run --rm presto-native-worker-cpu:$USER cat /opt/velox-testing/provenance.jsonpresto/scripts/run_benchmark.sh -b tpch -s bench_sf1benchmark_result.jsoncontext hasvelox_branch,velox_repo,presto_branch,presto_repopopulatedpost_results.py --dry-runwithout branch/repo args; confirm payloadengine_configshows values auto-populated from the result file--velox-branch override; confirm CLI value wins over the labelengine_configandcommit_hashisunknown, without errorpresto-build.yml; confirm all deps/coordinator/worker images build and push with the provenance step green (run 29847632984)provenance.jsonin the CI images: coordinator has presto-only fields; worker has both presto and velox (repo + sha), branches emptypost_results.pysubmission is accepted withcommit_hash = presto-<sha>-velox-<sha>and the separate sha/repo entriesCloses: #381