Skip to content

podvm: fail loud when oras resolve returns empty digest#3092

Open
Agrek11 wants to merge 1 commit into
confidential-containers:mainfrom
Agrek11:fix/pull-artifact-hard-fail-on-missing-tag
Open

podvm: fail loud when oras resolve returns empty digest#3092
Agrek11 wants to merge 1 commit into
confidential-containers:mainfrom
Agrek11:fix/pull-artifact-hard-fail-on-missing-tag

Conversation

@Agrek11

@Agrek11 Agrek11 commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

pull_agent_artifact and pull_gc_artifact in src/cloud-api-adaptor/podvm/Makefile.inc fail with a misleading error far from the root cause when the registry does not have the tag they ask for. This PR makes
them fail loud at the point of failure instead.

Both functions do OCI_DIGEST := $(shell oras resolve $OCI_IMAGE:$tag), then OCI_REF := $(OCI_IMAGE)@$(OCI_DIGEST). When oras resolve exits non-zero (missing tag), $(shell) swallows the exit code and
captures empty stdout, so OCI_REF becomes $OCI_IMAGE@ (trailing @). The subsequent oras pull then fails with a generic usage error (Error: "<image>@": no tag or digest specified) — two steps removed
from the actual problem, with nothing pointing at the missing tag or the versions.yaml pin that caused it.

This change adds $(if $(OCI_DIGEST),,$(error ...)) between the resolve and the OCI_REF construction. Empty digest → immediate make error naming the exact image:tag that failed to resolve.

Why this matters (real-world impact)

I hit this while iterating on versions.yaml pins during a CAA v0.20.0 SEV-SNP peer-pod POC on AWS: pinning a guest-components SHA whose artifact tag had not yet been published by the publish workflow makes the
build die on an oras usage error that names neither the missing tag nor the versions.yaml field to fix. With this guard, the same mistake is a single self-explanatory error line at the resolve step.

Reproduction (without this PR)

  1. In src/cloud-api-adaptor/versions.yaml, set oci.guest-components.reference to a 40-char SHA for which no <sha>-snp_amd64 (or equivalent) tag has been published — for example, a fresh commit on
    guest-components main that hasn't yet been built and pushed by the publish workflow.
  2. Run TEE_PLATFORM=amd make image-debug from src/cloud-api-adaptor/podvm-mkosi/.
  3. Observe: the build fails with Error: "ghcr.io/confidential-containers/guest-components/attestation-agent@": no tag or digest specified from oras pull — a usage error that does not mention the missing tag.

With this PR applied, step 2 instead exits with:

Makefile.inc:119: *** oras resolve returned empty digest for ghcr.io/confidential-containers/guest-components/attestation-agent:<sha>-snp_amd64.  Stop.

What this PR does NOT do

  • Does not change anything for builds whose versions.yaml pins resolve correctly — no-op in the happy path.
  • Does not suppress or alter oras resolve's own stderr — registry error details still print as before; the $(error) adds the make-level context on top.
  • Does not add a CI workflow to pre-validate all versions.yaml SHAs at PR / release time. That would be a useful follow-up but kept out of this PR to keep the diff minimal.

Test plan

  • Diff is minimal — both functions get the same guard, no other code path touched
  • No-op in the happy path (existing successful builds unchanged)
  • Error message names the exact image:tag that failed to resolve
  • CI: existing podvm_mkosi_ubuntu smoke test + AWS e2e workflow should pass unchanged on this PR
  • Reviewer can verify the failure mode by setting GUEST_COMPONENTS_REF to any 40-char SHA that lacks published artifacts and running make binaries

Happy to follow this up with a CI workflow that pre-validates all versions.yaml::oci.*.reference SHAs resolve in their registries, if maintainers see value.

@Agrek11
Agrek11 requested a review from a team as a code owner May 25, 2026 07:00
Comment thread src/cloud-api-adaptor/podvm/Makefile.inc Outdated
@Agrek11
Agrek11 force-pushed the fix/pull-artifact-hard-fail-on-missing-tag branch from c441ba8 to f2af6df Compare May 27, 2026 05:16
Comment thread src/cloud-api-adaptor/podvm/Makefile.inc Outdated
Comment thread src/cloud-api-adaptor/podvm/Makefile.inc Outdated
pull_agent_artifact and pull_gc_artifact in
src/cloud-api-adaptor/podvm/Makefile.inc both call `oras resolve
$OCI_IMAGE:$tag` and capture the result in OCI_DIGEST via make's
$(shell) function. When the registry does not have the requested tag
(for example, when versions.yaml pins a GUEST_COMPONENTS_REF or
KATA_REF that does not have a corresponding published `<sha>-<arch>`
or `<sha>-<tee>_<arch>` artifact), `oras resolve` exits non-zero and
prints nothing to stdout. make captures the empty stdout into
OCI_DIGEST, then constructs `OCI_REF := $(OCI_IMAGE)@` (trailing @ with
no digest), and the downstream `oras pull` either fails opaquely or --
depending on local cache state -- pulls a stale layer from a previous
successful build at a different commit. The resulting PodVM image then
contains guest-components or kata-agent binaries that do not match the
SHAs declared in versions.yaml, with no warning at build time.

This is real. It was the underlying cause of multi-day diagnosis effort
when a PodVM AMI built from CAA v0.20.0 contained an attestation-agent
binary that spoke an older wire protocol than the commit pinned in
versions.yaml shipped, leading to KBS-side `RcarAuthFailed: deserialize
Request - missing field 'version'` rejections. The AMI build succeeded
silently; the failure surfaced 1+ hours later at attestation handshake
time, in a separate component (Trustee).

This change adds a single guard between `oras resolve` and the
subsequent `OCI_REF` construction: the shell output is wrapped in
$(strip ...) (defensive against whitespace), and if OCI_DIGEST is
empty afterwards, make's $(error) function aborts the build
immediately with a message naming the missing image:tag and pointing
at the versions.yaml field to investigate. oras's own stderr is left
visible (no `2>/dev/null` redirection) so users see the underlying
network / auth / 404 details alongside the make-level abort message.

Behavior change: builds that previously produced a stale-binary PodVM
image now fail at the make level with a clear error. Builds whose
versions.yaml pins resolve correctly are unchanged. No CI workflow,
script, or other consumer of this Makefile.inc relies on the silent-
fallback behavior.

A follow-up could add a CI workflow that pre-validates all
versions.yaml::oci.*.reference SHAs resolve in their registries before
a release tag is cut, but that is out of scope for this minimal-diff
fix.

Signed-off-by: Abhishek Agrawal <abhishek.yours4@gmail.com>
@Agrek11
Agrek11 force-pushed the fix/pull-artifact-hard-fail-on-missing-tag branch from f2af6df to 9ac77f5 Compare May 29, 2026 17:16
@Agrek11

Agrek11 commented May 31, 2026

Copy link
Copy Markdown
Contributor Author

@mkulke both suggestions applied and threads resolved. Could you run ok-to-test when you have a moment? Thanks!

@mkulke

mkulke commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

pull_agent_artifact and pull_gc_artifact in src/cloud-api-adaptor/podvm/Makefile.inc silently produce stale PodVM images when the registry does not have the tag they ask for. This PR makes them fail loud instead.

@Agrek11 can you elaborate under which circumstances the Makefile.inc will produce "stale PodVM images". that should not happen, either with or without the change in the PR, so we might have a serious bug in the Makefile chain. My understanding from looking at the changes, the build should always fail hard, because "oras pull $image@" should result in a failure.

AFAIU this PR improves diagnostics, but if we end up with an image that contains undesired (stale) artifacts, then we need to fix more.

@Agrek11

Agrek11 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@mkulke you're right,thanks for pushing on this, i re-verified. With a missing tag,oras resolve fails inside $(shell)
(code swallowed, empty digest), and the subsequent oras pull $(OCI_IMAGE)@ fails hard with Error: "...@": no tag or digest specified. So the build does abort on this path — no stale image possible from the empty digest alone. I've updated the PR description: this change is a diagnostics improvement (fail at resolve, naming the missing image:tag and the versions.yaml field, instead of a usage error two steps removed from the cause).
The stale-binary incident that motivated this was real, but I now believe I misattributed it. Two candidate vectors I can see, both independent of this PR: (1) the binaries file targets depend on $(FORCE_TARGET), which is empty unless FORCE is set — so a pre-existing binary in FILES_DIR short-circuits the pull entirely; (2) in the podvm-mkosi flow, the binaries stage runs inside docker buildx build, so a cached layer can be reused even when the registry content behind a mutable tag has changed. I'd like to reproduce these properly and file a separate issue with evidence rather than widen this PR — does that sound reasonable?

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