From c6a5ba933f955d2633f04a8f525d0dd2d1b2df23 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 9 Nov 2022 17:17:29 +0900 Subject: [PATCH] containerimage: support SOURCE_DATE_EPOCH for CreatedAt Fix issue 3167 Signed-off-by: Akihiro Suda --- client/client_test.go | 55 +++++++++++++++++++++++++++++++ exporter/containerimage/export.go | 6 +++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/client/client_test.go b/client/client_test.go index 4fdc79ccb190f..7fc1aab16d9e1 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -179,6 +179,7 @@ func TestIntegration(t *testing.T) { testSourceDateEpochReset, testSourceDateEpochLocalExporter, testSourceDateEpochTarExporter, + testSourceDateEpochImageExporter, testAttestationBundle, testSBOMScan, testSBOMScanSingleRef, @@ -2683,6 +2684,60 @@ func testSourceDateEpochTarExporter(t *testing.T, sb integration.Sandbox) { checkAllReleasable(t, c, sb, true) } +func testSourceDateEpochImageExporter(t *testing.T, sb integration.Sandbox) { + integration.SkipIfDockerd(t, sb, "image exporter") + + requiresLinux(t) + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + + busybox := llb.Image("busybox:latest") + st := llb.Scratch() + + run := func(cmd string) { + st = busybox.Run(llb.Shlex(cmd), llb.Dir("/wd")).AddMount("/wd", st) + } + + run(`sh -c "echo -n first > foo"`) + run(`sh -c "echo -n second > bar"`) + + def, err := st.Marshal(sb.Context()) + require.NoError(t, err) + + name := strings.ToLower(path.Base(t.Name())) + tm := time.Date(2015, time.October, 21, 7, 28, 0, 0, time.UTC) + + _, err = c.Solve(sb.Context(), def, SolveOpt{ + FrontendAttrs: map[string]string{ + "build-arg:SOURCE_DATE_EPOCH": fmt.Sprintf("%d", tm.Unix()), + }, + Exports: []ExportEntry{ + { + Type: ExporterImage, + Attrs: map[string]string{ + "name": name, + }, + }, + }, + }, nil) + require.NoError(t, err) + + if cdAddress := sb.ContainerdAddress(); cdAddress != "" { + ctx := namespaces.WithNamespace(sb.Context(), "buildkit") + client, err := newContainerd(cdAddress) + require.NoError(t, err) + defer client.Close() + + img, err := client.GetImage(ctx, name) + require.NoError(t, err) + require.Equal(t, tm, img.Metadata().CreatedAt) + + // TODO: mount and validate the snapshots + } + + checkAllReleasable(t, c, sb, true) +} + func testFrontendMetadataReturn(t *testing.T, sb integration.Sandbox) { requiresLinux(t) c, err := New(sb.Context(), sb.Address()) diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 15350a361ca15..07e6ee51dd9cc 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -241,9 +241,13 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source for _, targetName := range targetNames { if e.opt.Images != nil && e.store { tagDone := progress.OneOff(ctx, "naming to "+targetName) + tm := time.Now() + if e.opts.Epoch != nil { + tm = *e.opts.Epoch + } img := images.Image{ Target: *desc, - CreatedAt: time.Now(), + CreatedAt: tm, } sfx := []string{""} if nameCanonical {