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
4 changes: 2 additions & 2 deletions exporter/local/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func CreateFS(ctx context.Context, sessionID string, k string, ref cache.Immutab
return nil, nil, err
}
stmtFS := staticfs.NewFS()
split := opt.UsePlatformSplit(isMap)
addPlatformToFilename := isMap && !opt.UsePlatformSplit(isMap)

names := map[string]struct{}{}
for i, stmt := range stmts {
Expand All @@ -190,7 +190,7 @@ func CreateFS(ctx context.Context, sessionID string, k string, ref cache.Immutab
}

name := opt.AttestationPrefix + path.Base(attestations[i].Path)
if !split {
if addPlatformToFilename {
nameExt := path.Ext(name)
namBase := strings.TrimSuffix(name, nameExt)
name = fmt.Sprintf("%s.%s%s", namBase, strings.ReplaceAll(k, "/", "_"), nameExt)
Expand Down
259 changes: 259 additions & 0 deletions frontend/dockerfile/dockerfile_provenance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ import (
"github.com/tonistiigi/fsutil"
)

var provenanceTests = integration.TestFuncs(
testProvenanceAttestation,
testGitProvenanceAttestation,
testMultiPlatformProvenance,
testClientFrontendProvenance,
testClientLLBProvenance,
testSecretSSHProvenance,
testOCILayoutProvenance,
testNilProvenance,
testDuplicatePlatformProvenance,
testDockerIgnoreMissingProvenance,
testCommandSourceMapping,
testFrontendDeduplicateSources,
testDuplicateLayersProvenance,
testProvenanceExportLocal,
testProvenanceExportLocalForceSplit,
testProvenanceExportLocalMultiPlatform,
testProvenanceExportLocalMultiPlatformNoSplit,
)

func init() {
allTests = append(allTests, provenanceTests...)
}

func testProvenanceAttestation(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush, workers.FeatureProvenance)
ctx := sb.Context()
Expand Down Expand Up @@ -1735,3 +1759,238 @@ RUN date +%s > /b.txt
require.NotNil(t, layers)
require.Len(t, layers, 1)
}

func testProvenanceExportLocal(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureProvenance)
ctx := sb.Context()

c, err := client.New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()

f := getFrontend(t, sb)

dockerfile := []byte(`
FROM busybox:latest AS base
COPY <<EOF /out/foo
ok
EOF

FROM scratch
COPY --from=base /out /
`)
dir := integration.Tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)

destDir := t.TempDir()
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
LocalMounts: map[string]fsutil.FS{
dockerui.DefaultLocalNameDockerfile: dir,
dockerui.DefaultLocalNameContext: dir,
},
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max",
},
Exports: []client.ExportEntry{
{
Type: client.ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)

dt, err := os.ReadFile(filepath.Join(destDir, "foo"))
require.NoError(t, err)
require.Equal(t, "ok\n", string(dt))

dt, err = os.ReadFile(filepath.Join(destDir, "provenance.json"))
require.NoError(t, err)
require.NotEqual(t, 0, len(dt))

var pred provenancetypes.ProvenancePredicateSLSA02
require.NoError(t, json.Unmarshal(dt, &pred))
}

func testProvenanceExportLocalForceSplit(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureProvenance)
ctx := sb.Context()

c, err := client.New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()

f := getFrontend(t, sb)

dockerfile := []byte(`
FROM busybox:latest AS base
COPY <<EOF /out/foo
ok
EOF

FROM scratch
COPY --from=base /out /
`)
dir := integration.Tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)

destDir := t.TempDir()
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
LocalMounts: map[string]fsutil.FS{
dockerui.DefaultLocalNameDockerfile: dir,
dockerui.DefaultLocalNameContext: dir,
},
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max",
},
Exports: []client.ExportEntry{
{
Type: client.ExporterLocal,
OutputDir: destDir,
Attrs: map[string]string{
"platform-split": "true",
},
},
},
}, nil)
require.NoError(t, err)

expPlatform := strings.ReplaceAll(platforms.FormatAll(platforms.DefaultSpec()), "/", "_")

dt, err := os.ReadFile(filepath.Join(destDir, expPlatform, "foo"))
require.NoError(t, err)
require.Equal(t, "ok\n", string(dt))

dt, err = os.ReadFile(filepath.Join(destDir, expPlatform, "provenance.json"))
require.NoError(t, err)
require.NotEqual(t, 0, len(dt))

var pred provenancetypes.ProvenancePredicateSLSA02
require.NoError(t, json.Unmarshal(dt, &pred))
}

func testProvenanceExportLocalMultiPlatform(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureMultiPlatform, workers.FeatureProvenance)
ctx := sb.Context()

c, err := client.New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()

f := getFrontend(t, sb)

dockerfile := []byte(`
FROM busybox:latest AS base
COPY <<EOF /out/foo
ok
EOF

FROM scratch
COPY --from=base /out /
`)
dir := integration.Tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)

destDir := t.TempDir()
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
LocalMounts: map[string]fsutil.FS{
dockerui.DefaultLocalNameDockerfile: dir,
dockerui.DefaultLocalNameContext: dir,
},
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max",
"platform": "linux/amd64,linux/arm64",
},
Exports: []client.ExportEntry{
{
Type: client.ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)

for _, platform := range []string{"linux_amd64", "linux_arm64"} {
dt, err := os.ReadFile(filepath.Join(destDir, platform, "foo"))
require.NoError(t, err)
require.Equal(t, "ok\n", string(dt))

dt, err = os.ReadFile(filepath.Join(destDir, platform, "provenance.json"))
require.NoError(t, err)
require.NotEqual(t, 0, len(dt))

var pred provenancetypes.ProvenancePredicateSLSA02
require.NoError(t, json.Unmarshal(dt, &pred))
}
}

func testProvenanceExportLocalMultiPlatformNoSplit(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureMultiPlatform, workers.FeatureProvenance)
ctx := sb.Context()

c, err := client.New(ctx, sb.Address())
require.NoError(t, err)
defer c.Close()

f := getFrontend(t, sb)

dockerfile := []byte(`
FROM busybox:latest AS base
ARG TARGETARCH
COPY <<EOF /out/foo_${TARGETARCH}
ok
EOF

FROM scratch
COPY --from=base /out /
`)
dir := integration.Tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)

destDir := t.TempDir()
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
LocalMounts: map[string]fsutil.FS{
dockerui.DefaultLocalNameDockerfile: dir,
dockerui.DefaultLocalNameContext: dir,
},
FrontendAttrs: map[string]string{
"attest:provenance": "mode=max",
"platform": "linux/amd64,linux/arm64",
},
Exports: []client.ExportEntry{
{
Type: client.ExporterLocal,
OutputDir: destDir,
Attrs: map[string]string{
"platform-split": "false",
},
},
},
}, nil)
require.NoError(t, err)

for _, arch := range []string{"amd64", "arm64"} {
dt, err := os.ReadFile(filepath.Join(destDir, "foo_"+arch))
require.NoError(t, err)
require.Equal(t, "ok\n", string(dt))

dt, err = os.ReadFile(filepath.Join(destDir, "provenance.linux_"+arch+".json"))
require.NoError(t, err)
require.NotEqual(t, 0, len(dt))

var pred provenancetypes.ProvenancePredicateSLSA02
require.NoError(t, json.Unmarshal(dt, &pred))
}
}
11 changes: 0 additions & 11 deletions frontend/dockerfile/dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,17 +189,6 @@ var allTests = integration.TestFuncs(
testDockerfileAddChownExpand,
testSourceDateEpochWithoutExporter,
testSBOMScannerImage,
testProvenanceAttestation,
testGitProvenanceAttestation,
testMultiPlatformProvenance,
testClientFrontendProvenance,
testClientLLBProvenance,
testSecretSSHProvenance,
testOCILayoutProvenance,
testNilProvenance,
testDuplicatePlatformProvenance,
testDockerIgnoreMissingProvenance,
testCommandSourceMapping,
testSBOMScannerArgs,
testMultiNilRefsOCIExporter,
testNilContextInSolveGateway,
Expand Down