Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions build/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,29 +60,7 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
if ev.Record == nil {
continue
}
if ev.Record.Result != nil {
desc, predicateType := lookupProvenance(ev.Record.Result)
if desc == nil {
continue
}
eg.Go(func() error {
dt, err := content.ReadBlob(ctx, store, *desc)
if err != nil {
return errors.Wrapf(err, "failed to load provenance blob from build record")
}
prv, err := encodeProvenance(dt, predicateType, mode)
if err != nil {
return err
}
mu.Lock()
if out == nil {
out = make(map[string]string)
}
out["buildx.build.provenance"] = prv
mu.Unlock()
return nil
})
} else if ev.Record.Results != nil {
if len(ev.Record.Results) > 0 {
for platform, res := range ev.Record.Results {
desc, predicateType := lookupProvenance(res)
if desc == nil {
Expand All @@ -106,6 +84,28 @@ func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode con
return nil
})
}
} else if ev.Record.Result != nil {
desc, predicateType := lookupProvenance(ev.Record.Result)
if desc == nil {
continue
}
eg.Go(func() error {
dt, err := content.ReadBlob(ctx, store, *desc)
if err != nil {
return errors.Wrapf(err, "failed to load provenance blob from build record")
}
prv, err := encodeProvenance(dt, predicateType, mode)
if err != nil {
return err
}
mu.Lock()
if out == nil {
out = make(map[string]string)
}
out["buildx.build.provenance"] = prv
mu.Unlock()
return nil
})
}
}
return out, eg.Wait()
Expand Down
96 changes: 96 additions & 0 deletions tests/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
testBuildShmSize,
testBuildUlimit,
testBuildMetadataProvenance,
testBuildMetadataProvenanceMultiplatform,
testBuildMetadataWarnings,
testBuildMultiExporters,
testBuildLoadPush,
Expand Down Expand Up @@ -967,6 +968,101 @@ func buildMetadataProvenance(t *testing.T, sb integration.Sandbox, metadataMode
require.Equal(t, provenancetypes.BuildKitBuildType02, prv.BuildType)
}

func testBuildMetadataProvenanceMultiplatform(t *testing.T, sb integration.Sandbox) {
t.Run("default", func(t *testing.T) {
buildMetadataProvenanceMultiplatform(t, sb, "")
})
t.Run("max", func(t *testing.T) {
buildMetadataProvenanceMultiplatform(t, sb, "max")
})
t.Run("min", func(t *testing.T) {
buildMetadataProvenanceMultiplatform(t, sb, "min")
})
t.Run("disabled", func(t *testing.T) {
buildMetadataProvenanceMultiplatform(t, sb, "disabled")
})
}

func buildMetadataProvenanceMultiplatform(t *testing.T, sb integration.Sandbox, metadataMode string) {
if isMobyWorker(sb) {
t.Skip("multi-platform build is not supported")
}

dockerfile := []byte(`
FROM --platform=$BUILDPLATFORM busybox:latest AS base
COPY foo /etc/foo
RUN cp /etc/foo /etc/bar

FROM scratch
COPY --from=base /etc/bar /bar
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)

registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
target := registry + "/buildx/registry:latest"

cmd := buildxCmd(sb,
withArgs("build",
"--platform=linux/amd64,linux/arm64",
"--metadata-file", filepath.Join(dir, "md.json"),
fmt.Sprintf("--output=type=image,name=%s,push=true", target),
dir,
),
withEnv("BUILDX_METADATA_PROVENANCE="+metadataMode),
)

out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))

desc, provider, err := contentutil.ProviderFromRef(target)
require.NoError(t, err)
imgs, err := testutil.ReadImages(sb.Context(), provider, desc)
require.NoError(t, err)

img := imgs.Find("linux/amd64")
require.NotNil(t, img)
img = imgs.Find("linux/arm64")
require.NotNil(t, img)

dt, err := os.ReadFile(filepath.Join(dir, "md.json"))
require.NoError(t, err)

type mdT struct {
BuildRef string `json:"buildx.build.ref"`
BuildProvenanceAmd64 map[string]any `json:"buildx.build.provenance/linux/amd64"`
BuildProvenanceArm64 map[string]any `json:"buildx.build.provenance/linux/arm64"`
}
var md mdT
err = json.Unmarshal(dt, &md)
require.NoError(t, err)

require.NotEmpty(t, md.BuildRef)
if metadataMode == "disabled" {
require.Empty(t, md.BuildProvenanceAmd64)
require.Empty(t, md.BuildProvenanceArm64)
return
}
require.NotEmpty(t, md.BuildProvenanceAmd64)
require.NotEmpty(t, md.BuildProvenanceArm64)

for _, prov := range []map[string]any{md.BuildProvenanceAmd64, md.BuildProvenanceArm64} {
dtprv, err := json.Marshal(prov)
require.NoError(t, err)

var prv provenancetypes.ProvenancePredicateSLSA02
require.NoError(t, json.Unmarshal(dtprv, &prv))
require.Equal(t, provenancetypes.BuildKitBuildType02, prv.BuildType)
}
}

func testBuildMetadataWarnings(t *testing.T, sb integration.Sandbox) {
t.Run("default", func(t *testing.T) {
buildMetadataWarnings(t, sb, "")
Expand Down