Skip to content

Commit e084a7f

Browse files
authored
Merge pull request #3536 from crazy-max/fix-provnance-metadata
fix empty provenance metadata for multi-platform builds
2 parents 1aa6b6c + 1b9791f commit e084a7f

File tree

2 files changed

+119
-23
lines changed

2 files changed

+119
-23
lines changed

build/provenance.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
6060
if ev.Record == nil {
6161
continue
6262
}
63-
if ev.Record.Result != nil {
64-
desc, predicateType := lookupProvenance(ev.Record.Result)
65-
if desc == nil {
66-
continue
67-
}
68-
eg.Go(func() error {
69-
dt, err := content.ReadBlob(ctx, store, *desc)
70-
if err != nil {
71-
return errors.Wrapf(err, "failed to load provenance blob from build record")
72-
}
73-
prv, err := encodeProvenance(dt, predicateType, mode)
74-
if err != nil {
75-
return err
76-
}
77-
mu.Lock()
78-
if out == nil {
79-
out = make(map[string]string)
80-
}
81-
out["buildx.build.provenance"] = prv
82-
mu.Unlock()
83-
return nil
84-
})
85-
} else if ev.Record.Results != nil {
63+
if len(ev.Record.Results) > 0 {
8664
for platform, res := range ev.Record.Results {
8765
desc, predicateType := lookupProvenance(res)
8866
if desc == nil {
@@ -106,6 +84,28 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
10684
return nil
10785
})
10886
}
87+
} else if ev.Record.Result != nil {
88+
desc, predicateType := lookupProvenance(ev.Record.Result)
89+
if desc == nil {
90+
continue
91+
}
92+
eg.Go(func() error {
93+
dt, err := content.ReadBlob(ctx, store, *desc)
94+
if err != nil {
95+
return errors.Wrapf(err, "failed to load provenance blob from build record")
96+
}
97+
prv, err := encodeProvenance(dt, predicateType, mode)
98+
if err != nil {
99+
return err
100+
}
101+
mu.Lock()
102+
if out == nil {
103+
out = make(map[string]string)
104+
}
105+
out["buildx.build.provenance"] = prv
106+
mu.Unlock()
107+
return nil
108+
})
109109
}
110110
}
111111
return out, eg.Wait()

tests/build.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
7070
testBuildShmSize,
7171
testBuildUlimit,
7272
testBuildMetadataProvenance,
73+
testBuildMetadataProvenanceMultiplatform,
7374
testBuildMetadataWarnings,
7475
testBuildMultiExporters,
7576
testBuildLoadPush,
@@ -967,6 +968,101 @@ func buildMetadataProvenance(t *testing.T, sb integration.Sandbox, metadataMode
967968
require.Equal(t, provenancetypes.BuildKitBuildType02, prv.BuildType)
968969
}
969970

971+
func testBuildMetadataProvenanceMultiplatform(t *testing.T, sb integration.Sandbox) {
972+
t.Run("default", func(t *testing.T) {
973+
buildMetadataProvenanceMultiplatform(t, sb, "")
974+
})
975+
t.Run("max", func(t *testing.T) {
976+
buildMetadataProvenanceMultiplatform(t, sb, "max")
977+
})
978+
t.Run("min", func(t *testing.T) {
979+
buildMetadataProvenanceMultiplatform(t, sb, "min")
980+
})
981+
t.Run("disabled", func(t *testing.T) {
982+
buildMetadataProvenanceMultiplatform(t, sb, "disabled")
983+
})
984+
}
985+
986+
func buildMetadataProvenanceMultiplatform(t *testing.T, sb integration.Sandbox, metadataMode string) {
987+
if isMobyWorker(sb) {
988+
t.Skip("multi-platform build is not supported")
989+
}
990+
991+
dockerfile := []byte(`
992+
FROM --platform=$BUILDPLATFORM busybox:latest AS base
993+
COPY foo /etc/foo
994+
RUN cp /etc/foo /etc/bar
995+
996+
FROM scratch
997+
COPY --from=base /etc/bar /bar
998+
`)
999+
dir := tmpdir(
1000+
t,
1001+
fstest.CreateFile("Dockerfile", dockerfile, 0600),
1002+
fstest.CreateFile("foo", []byte("foo"), 0600),
1003+
)
1004+
1005+
registry, err := sb.NewRegistry()
1006+
if errors.Is(err, integration.ErrRequirements) {
1007+
t.Skip(err.Error())
1008+
}
1009+
require.NoError(t, err)
1010+
target := registry + "/buildx/registry:latest"
1011+
1012+
cmd := buildxCmd(sb,
1013+
withArgs("build",
1014+
"--platform=linux/amd64,linux/arm64",
1015+
"--metadata-file", filepath.Join(dir, "md.json"),
1016+
fmt.Sprintf("--output=type=image,name=%s,push=true", target),
1017+
dir,
1018+
),
1019+
withEnv("BUILDX_METADATA_PROVENANCE="+metadataMode),
1020+
)
1021+
1022+
out, err := cmd.CombinedOutput()
1023+
require.NoError(t, err, string(out))
1024+
1025+
desc, provider, err := contentutil.ProviderFromRef(target)
1026+
require.NoError(t, err)
1027+
imgs, err := testutil.ReadImages(sb.Context(), provider, desc)
1028+
require.NoError(t, err)
1029+
1030+
img := imgs.Find("linux/amd64")
1031+
require.NotNil(t, img)
1032+
img = imgs.Find("linux/arm64")
1033+
require.NotNil(t, img)
1034+
1035+
dt, err := os.ReadFile(filepath.Join(dir, "md.json"))
1036+
require.NoError(t, err)
1037+
1038+
type mdT struct {
1039+
BuildRef string `json:"buildx.build.ref"`
1040+
BuildProvenanceAmd64 map[string]any `json:"buildx.build.provenance/linux/amd64"`
1041+
BuildProvenanceArm64 map[string]any `json:"buildx.build.provenance/linux/arm64"`
1042+
}
1043+
var md mdT
1044+
err = json.Unmarshal(dt, &md)
1045+
require.NoError(t, err)
1046+
1047+
require.NotEmpty(t, md.BuildRef)
1048+
if metadataMode == "disabled" {
1049+
require.Empty(t, md.BuildProvenanceAmd64)
1050+
require.Empty(t, md.BuildProvenanceArm64)
1051+
return
1052+
}
1053+
require.NotEmpty(t, md.BuildProvenanceAmd64)
1054+
require.NotEmpty(t, md.BuildProvenanceArm64)
1055+
1056+
for _, prov := range []map[string]any{md.BuildProvenanceAmd64, md.BuildProvenanceArm64} {
1057+
dtprv, err := json.Marshal(prov)
1058+
require.NoError(t, err)
1059+
1060+
var prv provenancetypes.ProvenancePredicateSLSA02
1061+
require.NoError(t, json.Unmarshal(dtprv, &prv))
1062+
require.Equal(t, provenancetypes.BuildKitBuildType02, prv.BuildType)
1063+
}
1064+
}
1065+
9701066
func testBuildMetadataWarnings(t *testing.T, sb integration.Sandbox) {
9711067
t.Run("default", func(t *testing.T) {
9721068
buildMetadataWarnings(t, sb, "")

0 commit comments

Comments
 (0)