Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix scratch multi-platform images #4526

Merged
merged 3 commits into from
Jan 9, 2024

Conversation

jedevc
Copy link
Member

@jedevc jedevc commented Jan 5, 2024

Fixes docker/build-push-action#1024.

The main fix is a bug in ConvertResult that accidentally discarded zero-values. This essentially meant that images that had nil entries in the refs map (i.e. scratch images) would lose those - this caused an error in the exporter: cannot export multiple platforms without multi-platform enabled.

Everything else is a fix needed to not get nil panics everywhere - there are certain parts of the code that never saw nil in the Refs map, so these now need to be handled (and mostly skipped over).

Skipping over attestations

@tonistiigi raised this in 3c1deef#r135018757.

Note from the commit message:

Instead, we just skip over nil refs. This isn't ideal - we should be able generate provenance for a nil ref. However, 1. this isn't handled for llb.Scratch as a single-platform result, and 2. this is tricky, since the ResultProxy is nil at this point (so there's no ID to match on). This should be ok for a quick fix, but we can come back and fix this later.

If the ResultProxy is nil there's no ID that we can match for findByResult - to allow provenance for nil reference (which we should do), then we'd need to have a ResultProxy for this empty result.

Here's an example of where this matters: we do two builds on a provenanceBridge, and both of them have multi-platform scratch results (so nil in the refs entry). If we pass nil to findByResult, which one of these should match? We have no way of working backwards to it.

A potential fix to this problem (but one I think is out-of-scope for here) is we need to make sure the ResultProxy is never nil so that we can track it for provenance - essentially make sure that even when req.Definition.Def below is nil, that we still have a proxy:

} else {
return &frontend.Result{}, nil
}

This feels a bit more invasive, and I'm less confident about that change than the rest of this, so figured it would make sense to submit this as a patch, and we could discuss about the rest of it as a follow-up.

jedevc added 2 commits January 5, 2024 14:08
It's possible for ref to be nil (e.g. from llb.Scratch stored
in a multi-platform result). In this case, we should handle a nil value
correctly, and *not* call Definition on it.

Also, we update the corresponding check for a single Ref to be the same
for consistency.

Signed-off-by: Justin Chadwell <[email protected]>
This is possible with llb.Scratch in a multi-platform build. We were
accidentally discarding the nil entries in Refs.

Instead, we just skip over nil refs. This isn't ideal - we *should* be
able generate provenance for a nil ref. However, 1. this isn't handled
for llb.Scratch as a single-platform result, and 2. this is tricky,
since the ResultProxy is nil at this point (so there's no ID to match
on). This should be ok for a quick fix, but we can come back and fix
this later.

Signed-off-by: Justin Chadwell <[email protected]>
@jedevc jedevc requested review from tonistiigi and crazy-max January 5, 2024 15:27
Copy link
Member

@tonistiigi tonistiigi left a comment

Choose a reason for hiding this comment

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

I wonder if it makes sense to extend the test to return image config for the scratch result (or move to Dockerfile test where this is easier). So that we can test that everything else around tracking the results by the platform key and results having separate metadata still works even if there are no layers.

Regarding provenance, I still think the nil result should have provenance. Ok to leave it as a follow up but would be good to at least have a test that checks that asking for a provenance for such build does not cause any panics. Otherwise these extra continue and zero value changes are hard to validate.

Otherwise LGTM

@jedevc
Copy link
Member Author

jedevc commented Jan 8, 2024

I wonder if it makes sense to extend the test to return image config for the scratch result

Added this.

Out of curiosity (potential follow-up) - if we don't return a config key as part of the metadata, each platforms config defaults to the current platform:

func defaultImageConfig() ([]byte, error) {
pl := platforms.Normalize(platforms.DefaultSpec())
img := ocispecs.Image{}
img.Architecture = pl.Architecture
img.OS = pl.OS
img.OSVersion = pl.OSVersion
img.OSFeatures = pl.OSFeatures
img.Variant = pl.Variant
img.RootFS.Type = "layers"
img.Config.WorkingDir = "/"
img.Config.Env = []string{"PATH=" + system.DefaultPathEnv(pl.OS)}
dt, err := json.Marshal(img)
return dt, errors.Wrap(err, "failed to create empty image config")
}

Instead, shouldn't we prefer to use the detected platform from ParsePlatforms which uses ExporterPlatformsKey if available?

Ok to leave it as a follow up but would be good to at least have a test that checks that asking for a provenance for such build does not cause any panics

Added attest:provenance flag in here. This shouldn't have an impact, since the provenance is always generated (so it can be added to the history API). However, the codepaths are slightly difference, so worth having the check anyways.

@tonistiigi
Copy link
Member

Instead, shouldn't we prefer to use the detected platform from ParsePlatforms which uses ExporterPlatformsKey if available?

SGTM.

p := platforms.MustParse(pk)

var img ocispecs.Image
img.Platform = p
Copy link
Member

Choose a reason for hiding this comment

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

Could we add like a label in here that is unique per platform so that we can verify it. Otherwise there still isn't a strong guarantee that this config ended up in build result.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@jedevc jedevc force-pushed the fix-scratch-multiplatform branch from fc55dfb to 490e359 Compare January 9, 2024 12:49
@jedevc jedevc force-pushed the fix-scratch-multiplatform branch from 490e359 to 206fa17 Compare January 9, 2024 12:52
@tonistiigi tonistiigi merged commit d2b7b92 into moby:master Jan 9, 2024
63 of 64 checks passed
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.

Cannot export multiple platforms without multi-platform enabled with scratch image
2 participants