diff --git a/README.md b/README.md index 1cc65a982397..ccef9a024ad1 100644 --- a/README.md +++ b/README.md @@ -259,8 +259,6 @@ Keys supported by image output: * `compression=`: choose compression type for layers newly created and cached, gzip is default value. estargz should be used with `oci-mediatypes=true`. * `compression-level=`: compression level for gzip, estargz (0-9) and zstd (0-22) * `force-compression=true`: forcefully apply `compression` option to all layers (including already existing layers) -* `buildinfo=true`: attach inline build info in [image config](docs/build-repro.md#image-config) (default `true`) -* `buildinfo-attrs=true`: attach inline build info attributes in [image config](docs/build-repro.md#image-config) (default `false`) * `store=true`: store the result images to the worker's (e.g. containerd) image store as well as ensures that the image has all blobs in the content store (default `true`). Ignored if the worker doesn't have image store (e.g. OCI worker). * `annotation.=`: attach an annotation with the respective `key` and `value` to the built image * Using the extended syntaxes, `annotation-.=`, `annotation[].=` and both combined with `annotation-[].=`, allows configuring exactly where to attach the annotation. @@ -582,26 +580,6 @@ jq '.' metadata.json ``` ```json { - "containerimage.buildinfo": { - "frontend": "dockerfile.v0", - "attrs": { - "context": "https://github.com/crazy-max/buildkit-buildsources-test.git#master", - "filename": "Dockerfile", - "source": "docker/dockerfile:master" - }, - "sources": [ - { - "type": "docker-image", - "ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - "pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0" - }, - { - "type": "docker-image", - "ref": "docker.io/library/alpine:3.13", - "pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c" - } - ] - }, "containerimage.config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66", "containerimage.descriptor": { "annotations": { diff --git a/client/client_test.go b/client/client_test.go index 8a387695dfe3..da999900f002 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -51,7 +51,6 @@ import ( sourcepolicypb "github.com/moby/buildkit/sourcepolicy/pb" spb "github.com/moby/buildkit/sourcepolicy/pb" "github.com/moby/buildkit/util/attestation" - binfotypes "github.com/moby/buildkit/util/buildinfo/types" "github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/purl" @@ -168,9 +167,6 @@ func TestIntegration(t *testing.T) { testRmSymlink, testMoveParentDir, testBuildExportWithForeignLayer, - testBuildInfoExporter, - testBuildInfoInline, - testBuildInfoNoExport, testZstdLocalCacheExport, testCacheExportIgnoreError, testZstdRegistryCacheImportExport, @@ -6672,164 +6668,6 @@ func testRelativeMountpoint(t *testing.T, sb integration.Sandbox) { require.Equal(t, dt, []byte(id)) } -// moby/buildkit#2476 -func testBuildInfoExporter(t *testing.T, sb integration.Sandbox) { - requiresLinux(t) - c, err := New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { - st := llb.Image("busybox:latest").Run( - llb.Args([]string{"/bin/sh", "-c", `echo hello`}), - ) - def, err := st.Marshal(sb.Context()) - if err != nil { - return nil, err - } - return c.Solve(ctx, gateway.SolveRequest{ - Definition: def.ToPB(), - }) - } - - var exports []ExportEntry - if integration.IsTestDockerdMoby(sb) { - exports = []ExportEntry{{ - Type: "moby", - Attrs: map[string]string{ - "name": "reg.dummy:5000/buildkit/test:latest", - }, - }} - } else { - exports = []ExportEntry{{ - Type: ExporterOCI, - Attrs: map[string]string{}, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }} - } - - res, err := c.Build(sb.Context(), SolveOpt{ - Exports: exports, - }, "", frontend, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - decbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var exbi binfotypes.BuildInfo - err = json.Unmarshal(decbi, &exbi) - require.NoError(t, err) - - require.Equal(t, len(exbi.Sources), 1) - require.Equal(t, exbi.Sources[0].Type, binfotypes.SourceTypeDockerImage) - require.Equal(t, exbi.Sources[0].Ref, "docker.io/library/busybox:latest") -} - -// moby/buildkit#2476 -func testBuildInfoInline(t *testing.T, sb integration.Sandbox) { - integration.CheckFeatureCompat(t, sb, integration.FeatureDirectPush) - requiresLinux(t) - c, err := New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - st := llb.Image("busybox:latest").Run( - llb.Args([]string{"/bin/sh", "-c", `echo hello`}), - ) - def, err := st.Marshal(sb.Context()) - require.NoError(t, err) - - registry, err := sb.NewRegistry() - if errors.Is(err, integration.ErrRequirements) { - t.Skip(err.Error()) - } - require.NoError(t, err) - - cdAddress := sb.ContainerdAddress() - if cdAddress == "" { - t.Skip("rest of test requires containerd worker") - } - - client, err := newContainerd(cdAddress) - require.NoError(t, err) - defer client.Close() - - ctx := namespaces.WithNamespace(sb.Context(), "buildkit") - - target := registry + "/buildkit/test-buildinfo:latest" - - _, err = c.Solve(sb.Context(), def, SolveOpt{ - Exports: []ExportEntry{ - { - Type: ExporterImage, - Attrs: map[string]string{ - "name": target, - "push": "true", - }, - }, - }, - }, nil) - require.NoError(t, err) - - img, err := client.GetImage(ctx, target) - require.NoError(t, err) - - desc, err := img.Config(ctx) - require.NoError(t, err) - - dt, err := content.ReadBlob(ctx, img.ContentStore(), desc) - require.NoError(t, err) - - var config binfotypes.ImageConfig - require.NoError(t, json.Unmarshal(dt, &config)) - - dec, err := base64.StdEncoding.DecodeString(config.BuildInfo) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - require.NoError(t, json.Unmarshal(dec, &bi)) - - require.Equal(t, len(bi.Sources), 1) - require.Equal(t, bi.Sources[0].Type, binfotypes.SourceTypeDockerImage) - require.Equal(t, bi.Sources[0].Ref, "docker.io/library/busybox:latest") -} - -func testBuildInfoNoExport(t *testing.T, sb integration.Sandbox) { - requiresLinux(t) - c, err := New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { - st := llb.Image("busybox:latest").Run( - llb.Args([]string{"/bin/sh", "-c", `echo hello`}), - ) - def, err := st.Marshal(sb.Context()) - if err != nil { - return nil, err - } - return c.Solve(ctx, gateway.SolveRequest{ - Definition: def.ToPB(), - }) - } - - res, err := c.Build(sb.Context(), SolveOpt{}, "", frontend, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - decbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var exbi binfotypes.BuildInfo - err = json.Unmarshal(decbi, &exbi) - require.NoError(t, err) - - require.Equal(t, len(exbi.Sources), 1) - require.Equal(t, exbi.Sources[0].Type, binfotypes.SourceTypeDockerImage) - require.Equal(t, exbi.Sources[0].Ref, "docker.io/library/busybox:latest") -} - func testPullWithLayerLimit(t *testing.T, sb integration.Sandbox) { integration.CheckFeatureCompat(t, sb, integration.FeatureDirectPush) requiresLinux(t) diff --git a/docs/build-repro.md b/docs/build-repro.md index fbf99ff9ed71..9b83ec0593ec 100644 --- a/docs/build-repro.md +++ b/docs/build-repro.md @@ -1,134 +1,6 @@ # Build reproducibility -## Build dependencies - -Build dependencies are generated when your image has been built. These -dependencies include versions of used images, git repositories and HTTP URLs -used by LLB `Source` operation as well as build request attributes. - -The structure is base64 encoded and has the following format when decoded: - -```json -{ - "frontend": "dockerfile.v0", - "attrs": { - "build-arg:foo": "bar", - "context": "https://github.com/crazy-max/buildkit-buildsources-test.git#master", - "filename": "Dockerfile", - "platform": "linux/amd64,linux/arm64", - "source": "crazymax/dockerfile:master" - }, - "sources": [ - { - "type": "docker-image", - "ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - "pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0" - }, - { - "type": "docker-image", - "ref": "docker.io/library/alpine:3.13", - "pin": "sha256:1d30d1ba3cb90962067e9b29491fbd56997979d54376f23f01448b5c5cd8b462" - }, - { - "type": "git", - "ref": "https://github.com/crazy-max/buildkit-buildsources-test.git#master", - "pin": "259a5aa5aa5bb3562d12cc631fe399f4788642c1" - }, - { - "type": "http", - "ref": "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - "pin": "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c" - } - ] -} -``` - -* `frontend` defines the frontend used to build. -* `attrs` defines build request attributes. -* `sources` defines build sources. - * `type` defines the source type (`docker-image`, `git` or `http`). - * `ref` is the reference of the source. - * `pin` is the source digest. -* `deps` defines build dependencies of input contexts. - -### Image config - -A new field similar to the one for inline cache has been added to the image -configuration to embed build dependencies: - -```json -{ - "moby.buildkit.buildinfo.v0": "" -} -``` - -By default, the build dependencies are inlined in the image configuration. You -can disable this behavior with the [`buildinfo` attribute](../README.md#imageregistry). - -### Exporter response (metadata) - -The solver response (`ExporterResponse`) also contains a new key -`containerimage.buildinfo` with the same structure as image config encoded in -base64: - -```json -{ - "ExporterResponse": { - "containerimage.buildinfo": "", - "containerimage.digest": "sha256:..." - } -} -``` - -If multi-platforms are specified, they will be suffixed with the corresponding -platform: - -```json -{ - "ExporterResponse": { - "containerimage.buildinfo/linux/amd64": "", - "containerimage.buildinfo/linux/arm64": "", - "containerimage.digest": "sha256:..." - } -} -``` - -### Metadata JSON output - -If you're using the `--metadata-file` flag with [`buildctl`](../README.md#metadata), -[`buildx build`](https://github.com/docker/buildx/blob/master/docs/reference/buildx_build.md) -or [`buildx bake`](https://github.com/docker/buildx/blob/master/docs/reference/buildx_bake.md): - -```shell -jq '.' metadata.json -``` -```json -{ - "containerimage.buildinfo": { - "frontend": "dockerfile.v0", - "attrs": { - "context": "https://github.com/crazy-max/buildkit-buildsources-test.git#master", - "filename": "Dockerfile", - "source": "docker/dockerfile:master" - }, - "sources": [ - { - "type": "docker-image", - "ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - "pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0" - }, - { - "type": "docker-image", - "ref": "docker.io/library/alpine:3.13", - "pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c" - } - ] - }, - "containerimage.digest": "sha256:..." -} -``` - -### Reproducing the pinned dependencies +## Reproducing the pinned dependencies Reproducing the pinned dependencies is supported since BuildKit v0.11. diff --git a/docs/buildinfo.md b/docs/buildinfo.md new file mode 100644 index 000000000000..1615c02ae0e5 --- /dev/null +++ b/docs/buildinfo.md @@ -0,0 +1,4 @@ +# Build information + +Build information has been removed since BuildKit v0.12.0. See the [Deprecated features page](https://github.com/moby/buildkit/blob/master/docs/deprecated.md) +for status and alternative recommendation about this feature. diff --git a/docs/deprecated.md b/docs/deprecated.md new file mode 100644 index 000000000000..6e6baa8c4663 --- /dev/null +++ b/docs/deprecated.md @@ -0,0 +1,46 @@ +# Deprecated features + +This page provides an overview of features that are deprecated in BuildKit. + +As changes are made to BuildKit there may be times when existing features need +to be removed or replaced with newer features. Before an existing feature is +removed it is labeled as "deprecated" within the documentation and remains in +BuildKit for at least one stable release unless specified explicitly otherwise. +After that time it may be removed. + +Users are expected to take note of the list of deprecated features each release +and plan their migration away from those features, and (if applicable) towards +the replacement features as soon as possible. + +The table below provides an overview of the current status of deprecated +features: + +- **Deprecated**: the feature is marked "deprecated" and should no longer be + used. The feature may be removed, disabled, or change behavior in a future + release. The _"Deprecated"_ column contains the release in which the feature + was marked deprecated, whereas the _"Remove"_ column contains a tentative + release in which the feature is to be removed. If no release is included in + the _"Remove"_ column, the release is yet to be decided on. +- **Removed**: the feature was removed, disabled, or hidden. Refer to the linked + section for details. Some features are "soft" deprecated, which means that + they remain functional for backward compatibility, and to allow users to + migrate to alternatives. In such cases, a warning may be printed, and users + should not rely on this feature. + +| Status | Feature | Deprecated | Remove | Recommendation | +|------------|-----------------------------------------|------------|--------|------------------------------------------------------------------| +| Deprecated | [Build information](#build-information) | v0.11 | v0.12 | Use [provenance attestations](./attestations/slsa-provenance.md) | + +## Build information + +[Build information](https://github.com/moby/buildkit/blob/v0.11/docs/buildinfo.md) +structures have been introduced in [BuildKit v0.10.0](https://github.com/moby/buildkit/releases/tag/v0.10.0) +and are generated with build metadata that allows you to see all the sources +(images, git repositories) that were used by the build with their exact +versions and also the configuration that was passed to the build. This +information is also embedded into the image configuration if one is generated. + +With the introduction of [provenance attestations](./attestations/slsa-provenance.md) +in [BuildKit v0.11.0](https://github.com/moby/buildkit/releases/tag/v0.11.0), +the build information feature has been deprecated and removed in v0.12.0 +release. diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 55eaf3ff5803..c69f9d22a705 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -78,7 +78,6 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp RefCfg: cacheconfig.RefConfig{ Compression: compression.New(compression.Default), }, - BuildInfo: true, ForceInlineAttestations: true, }, store: true, diff --git a/exporter/containerimage/exptypes/types.go b/exporter/containerimage/exptypes/types.go index f22344c86a7e..07a6d4730056 100644 --- a/exporter/containerimage/exptypes/types.go +++ b/exporter/containerimage/exptypes/types.go @@ -11,7 +11,6 @@ const ( ExporterImageConfigDigestKey = "containerimage.config.digest" ExporterImageDescriptorKey = "containerimage.descriptor" ExporterInlineCache = "containerimage.inlinecache" - ExporterBuildInfo = "containerimage.buildinfo" ExporterPlatformsKey = "refs.platforms" ExporterEpochKey = "source.date.epoch" ) @@ -21,7 +20,6 @@ const ( var KnownRefMetadataKeys = []string{ ExporterImageConfigKey, ExporterInlineCache, - ExporterBuildInfo, } type Platforms struct { diff --git a/exporter/containerimage/opts.go b/exporter/containerimage/opts.go index 96844aa42e80..dececd6acedc 100644 --- a/exporter/containerimage/opts.go +++ b/exporter/containerimage/opts.go @@ -14,8 +14,6 @@ import ( const ( keyImageName = "name" keyOCITypes = "oci-mediatypes" - keyBuildInfo = "buildinfo" - keyBuildInfoAttrs = "buildinfo-attrs" keyForceInlineAttestations = "attestation-inline" // preferNondistLayersKey is an exporter option which can be used to mark a layer as non-distributable if the layer reference was @@ -25,13 +23,11 @@ const ( ) type ImageCommitOpts struct { - ImageName string - RefCfg cacheconfig.RefConfig - OCITypes bool - BuildInfo bool - BuildInfoAttrs bool - Annotations AnnotationsGroup - Epoch *time.Time + ImageName string + RefCfg cacheconfig.RefConfig + OCITypes bool + Annotations AnnotationsGroup + Epoch *time.Time ForceInlineAttestations bool // force inline attestations to be attached } @@ -61,10 +57,6 @@ func (c *ImageCommitOpts) Load(opt map[string]string) (map[string]string, error) c.ImageName = v case keyOCITypes: err = parseBoolWithDefault(&c.OCITypes, k, v, true) - case keyBuildInfo: - err = parseBoolWithDefault(&c.BuildInfo, k, v, true) - case keyBuildInfoAttrs: - err = parseBoolWithDefault(&c.BuildInfoAttrs, k, v, false) case keyForceInlineAttestations: err = parseBool(&c.ForceInlineAttestations, k, v) case keyPreferNondistLayers: diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index 0c0aef743abf..930fdc61c079 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -26,8 +26,6 @@ import ( "github.com/moby/buildkit/solver/result" attestationTypes "github.com/moby/buildkit/util/attestation" "github.com/moby/buildkit/util/bklog" - "github.com/moby/buildkit/util/buildinfo" - binfotypes "github.com/moby/buildkit/util/buildinfo/types" "github.com/moby/buildkit/util/compression" "github.com/moby/buildkit/util/progress" "github.com/moby/buildkit/util/purl" @@ -127,15 +125,6 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session return nil, err } - var dtbi []byte - if opts.BuildInfo { - if dtbi, err = buildinfo.Format(exptypes.ParseKey(inp.Metadata, exptypes.ExporterBuildInfo, p), buildinfo.FormatOpts{ - RemoveAttrs: !opts.BuildInfoAttrs, - }); err != nil { - return nil, err - } - } - annotations := opts.Annotations.Platform(nil) if len(annotations.Index) > 0 || len(annotations.IndexDescriptor) > 0 { return nil, errors.Errorf("index annotations not supported for single platform export") @@ -143,7 +132,7 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, p) inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) - mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, opts, ref, config, &remotes[0], annotations, inlineCache, dtbi, opts.Epoch, session.NewGroup(sessionID)) + mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, opts, ref, config, &remotes[0], annotations, inlineCache, opts.Epoch, session.NewGroup(sessionID)) if err != nil { return nil, err } @@ -202,15 +191,6 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, p) inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) - var dtbi []byte - if opts.BuildInfo { - if dtbi, err = buildinfo.Format(exptypes.ParseKey(inp.Metadata, exptypes.ExporterBuildInfo, p), buildinfo.FormatOpts{ - RemoveAttrs: !opts.BuildInfoAttrs, - }); err != nil { - return nil, err - } - } - remote := &remotes[remotesMap[p.ID]] if remote == nil { remote = &solver.Remote{ @@ -218,7 +198,7 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session } } - desc, _, err := ic.commitDistributionManifest(ctx, opts, r, config, remote, opts.Annotations.Platform(&p.Platform), inlineCache, dtbi, opts.Epoch, session.NewGroup(sessionID)) + desc, _, err := ic.commitDistributionManifest(ctx, opts, r, config, remote, opts.Annotations.Platform(&p.Platform), inlineCache, opts.Epoch, session.NewGroup(sessionID)) if err != nil { return nil, err } @@ -342,7 +322,7 @@ func (ic *ImageWriter) exportLayers(ctx context.Context, refCfg cacheconfig.RefC return out, err } -func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *ImageCommitOpts, ref cache.ImmutableRef, config []byte, remote *solver.Remote, annotations *Annotations, inlineCache []byte, buildInfo []byte, epoch *time.Time, sg session.Group) (*ocispecs.Descriptor, *ocispecs.Descriptor, error) { +func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *ImageCommitOpts, ref cache.ImmutableRef, config []byte, remote *solver.Remote, annotations *Annotations, inlineCache []byte, epoch *time.Time, sg session.Group) (*ocispecs.Descriptor, *ocispecs.Descriptor, error) { if len(config) == 0 { var err error config, err = defaultImageConfig() @@ -361,7 +341,7 @@ func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *Ima return nil, nil, err } - config, err = patchImageConfig(config, remote.Descriptors, history, inlineCache, buildInfo, epoch) + config, err = patchImageConfig(config, remote.Descriptors, history, inlineCache, epoch) if err != nil { return nil, nil, err } @@ -588,7 +568,7 @@ func parseHistoryFromConfig(dt []byte) ([]ocispecs.History, error) { return config.History, nil } -func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs.History, cache []byte, buildInfo []byte, epoch *time.Time) ([]byte, error) { +func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs.History, cache []byte, epoch *time.Time) ([]byte, error) { m := map[string]json.RawMessage{} if err := json.Unmarshal(dt, &m); err != nil { return nil, errors.Wrap(err, "failed to parse image config for patch") @@ -656,16 +636,6 @@ func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs m["moby.buildkit.cache.v0"] = dt } - if buildInfo != nil { - dt, err := json.Marshal(buildInfo) - if err != nil { - return nil, err - } - m[binfotypes.ImageConfigField] = dt - } else { - delete(m, binfotypes.ImageConfigField) - } - dt, err = json.Marshal(m) return dt, errors.Wrap(err, "failed to marshal config after patch") } diff --git a/exporter/oci/export.go b/exporter/oci/export.go index 60982f4daf3c..da7aace2ba2d 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -67,8 +67,7 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp RefCfg: cacheconfig.RefConfig{ Compression: compression.New(compression.Default), }, - BuildInfo: true, - OCITypes: e.opt.Variant == VariantOCI, + OCITypes: e.opt.Variant == VariantOCI, }, } diff --git a/frontend/dockerfile/cmd/dockerfile-frontend/hack/release b/frontend/dockerfile/cmd/dockerfile-frontend/hack/release index 8d71f83e8def..7a7909c5fca3 100755 --- a/frontend/dockerfile/cmd/dockerfile-frontend/hack/release +++ b/frontend/dockerfile/cmd/dockerfile-frontend/hack/release @@ -93,7 +93,7 @@ case $TYP in buildxCmd build $platformFlag $cacheFromFlags $cacheToFlags $nocacheFilterFlag $(buildAttestFlags) \ --build-arg "CHANNEL=$TAG" \ --build-arg "BUILDTAGS=$buildTags" \ - --output "type=image,name=$REPO:$pushTag,buildinfo-attrs=true,$pushFlag" \ + --output "type=image,name=$REPO:$pushTag,$pushFlag" \ --file "./frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile" \ $currentcontext ;; @@ -110,7 +110,7 @@ case $TYP in buildxCmd build $platformFlag $cacheFromFlags $cacheToFlags $nocacheFilterFlag $(buildAttestFlags) \ --build-arg "CHANNEL=$TAG" \ --build-arg "BUILDTAGS=$buildTags" \ - --output "type=image,\"name=$publishedNames\",buildinfo-attrs=true,$pushFlag" \ + --output "type=image,\"name=$publishedNames\",$pushFlag" \ --file "./frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile" \ $currentcontext ;; @@ -150,7 +150,7 @@ case $TYP in buildxCmd build $platformFlag $cacheFromFlags $cacheToFlags $nocacheFilterFlag $(buildAttestFlags) \ --build-arg "CHANNEL=$TAG" \ --build-arg "BUILDTAGS=$buildTags" \ - --output "type=image,name=$REPO:$dt-$TAG,buildinfo-attrs=true,$pushFlag" \ + --output "type=image,name=$REPO:$dt-$TAG,$pushFlag" \ --file "./frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile" \ $currentcontext rm $tmp/buildid diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index a780d4fd8332..894b56e01634 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -30,7 +30,6 @@ import ( "github.com/moby/buildkit/identity" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/apicaps" - binfotypes "github.com/moby/buildkit/util/buildinfo/types" "github.com/moby/buildkit/util/gitutil" "github.com/moby/buildkit/util/suggest" "github.com/moby/buildkit/util/system" @@ -438,16 +437,6 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS } } } - if !isScratch { - // if image not scratch set original image name as ref - // and actual reference as alias in binfotypes.Source - d.buildInfo.Sources = append(d.buildInfo.Sources, binfotypes.Source{ - Type: binfotypes.SourceTypeDockerImage, - Ref: origName, - Alias: ref.String(), - Pin: dgst.String(), - }) - } d.image = img } if isScratch { @@ -791,7 +780,6 @@ type dispatchState struct { cmdIndex int cmdTotal int prefixPlatform bool - buildInfo binfotypes.BuildInfo outline outlineCapture epoch *time.Time scanStage bool diff --git a/frontend/dockerfile/dockerfile_buildinfo_test.go b/frontend/dockerfile/dockerfile_buildinfo_test.go deleted file mode 100644 index 0a700f09daad..000000000000 --- a/frontend/dockerfile/dockerfile_buildinfo_test.go +++ /dev/null @@ -1,913 +0,0 @@ -package dockerfile - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/containerd/continuity/fs/fstest" - "github.com/moby/buildkit/client" - "github.com/moby/buildkit/exporter/containerimage/exptypes" - "github.com/moby/buildkit/frontend/dockerui" - gateway "github.com/moby/buildkit/frontend/gateway/client" - "github.com/moby/buildkit/solver/pb" - binfotypes "github.com/moby/buildkit/util/buildinfo/types" - "github.com/moby/buildkit/util/testutil/integration" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var buildinfoTests = integration.TestFuncs( - testBuildInfoSources, - testBuildInfoSourcesNoop, - testBuildInfoAttrs, - testBuildInfoMultiPlatform, - testBuildInfoImageContext, - testBuildInfoLocalContext, - testBuildInfoDeps, - testBuildInfoDepsMultiPlatform, - testBuildInfoDepsMainNoSource, -) - -func init() { - allTests = append(allTests, buildinfoTests...) -} - -// moby/buildkit#2311 -func testBuildInfoSources(t *testing.T, sb integration.Sandbox) { - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - gitDir := t.TempDir() - - dockerfile := ` -FROM alpine:latest@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300 AS alpine -FROM busybox:latest -ADD https://user2:pw2@raw.githubusercontent.com/moby/moby/v20.10.21/README.md / -COPY --from=alpine /bin/busybox /alpine-busybox -` - - err := os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte(dockerfile), 0600) - require.NoError(t, err) - - err = runShell(gitDir, - "git init", - "git config --local user.email test", - "git config --local user.name test", - "git add Dockerfile", - "git commit -m initial", - "git branch buildinfo", - "git update-server-info", - ) - require.NoError(t, err) - - server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join(gitDir)))) - defer server.Close() - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - var exports []client.ExportEntry - if integration.IsTestDockerdMoby(sb) { - exports = []client.ExportEntry{{ - Type: "moby", - Attrs: map[string]string{ - "name": "reg.dummy:5000/buildkit/test:latest", - }, - }} - } else { - exports = []client.ExportEntry{{ - Type: client.ExporterOCI, - Attrs: map[string]string{}, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }} - } - - expectedURL := strings.Replace(server.URL, "http://", "http://xxxxx:xxxxx@", 1) - require.NotEqual(t, expectedURL, server.URL) - server.URL = strings.Replace(server.URL, "http://", "http://user:pass@", 1) - - res, err := f.Solve(sb.Context(), c, client.SolveOpt{ - Exports: exports, - FrontendAttrs: map[string]string{ - dockerui.DefaultLocalNameContext: server.URL + "/.git#buildinfo", - dockerui.DefaultLocalNameContext + ":foo": "https://foo:bar@example.invalid/foo.html", - }, - }, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "context") - require.Equal(t, expectedURL+"/.git#buildinfo", *bi.Attrs["context"]) - - require.Equal(t, "https://xxxxx:xxxxx@example.invalid/foo.html", *bi.Attrs["context:foo"]) - - _, isGateway := f.(*gatewayFrontend) - - sources := bi.Sources - if isGateway { - require.Equal(t, 5, len(sources), "%+v", sources) - assert.Equal(t, binfotypes.SourceTypeDockerImage, sources[0].Type) - assert.Contains(t, sources[0].Ref, "buildkit_test") - sources = sources[1:] - } - require.Equal(t, 4, len(sources), "%+v", sources) - assert.Equal(t, binfotypes.SourceTypeDockerImage, sources[0].Type) - assert.Equal(t, "docker.io/library/alpine:latest@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300", sources[0].Ref) - assert.Equal(t, "sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300", sources[0].Pin) - - assert.Equal(t, binfotypes.SourceTypeDockerImage, sources[1].Type) - assert.Equal(t, "docker.io/library/busybox:latest", sources[1].Ref) - assert.NotEmpty(t, sources[1].Pin) - - assert.Equal(t, binfotypes.SourceTypeGit, sources[2].Type) - assert.Equal(t, expectedURL+"/.git#buildinfo", sources[2].Ref) - assert.NotEmpty(t, sources[2].Pin) - - assert.Equal(t, binfotypes.SourceTypeHTTP, sources[3].Type) - assert.Equal(t, "https://xxxxx:xxxxx@raw.githubusercontent.com/moby/moby/v20.10.21/README.md", sources[3].Ref) - assert.Equal(t, "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", sources[3].Pin) -} - -func testBuildInfoSourcesNoop(t *testing.T, sb integration.Sandbox) { - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - dockerfile := ` -FROM busybox:latest -` - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", []byte(dockerfile), 0600), - ) - require.NoError(t, err) - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - var exports []client.ExportEntry - if integration.IsTestDockerdMoby(sb) { - exports = []client.ExportEntry{{ - Type: "moby", - Attrs: map[string]string{ - "name": "reg.dummy:5000/buildkit/test:latest", - }, - }} - } else { - exports = []client.ExportEntry{{ - Type: client.ExporterOCI, - Attrs: map[string]string{}, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }} - } - - res, err := f.Solve(sb.Context(), c, client.SolveOpt{ - Exports: exports, - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - }, - }, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - sources := bi.Sources - - if _, isGateway := f.(*gatewayFrontend); isGateway { - require.Equal(t, 2, len(sources), "%+v", sources) - sources = sources[1:] - } - require.Equal(t, 1, len(sources)) - assert.Equal(t, binfotypes.SourceTypeDockerImage, sources[0].Type) - assert.Equal(t, "docker.io/library/busybox:latest", sources[0].Ref) - assert.NotEmpty(t, sources[0].Pin) -} - -// moby/buildkit#2476 -func testBuildInfoAttrs(t *testing.T, sb integration.Sandbox) { - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - dockerfile := ` -FROM busybox:latest -ARG foo -RUN echo $foo -` - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", []byte(dockerfile), 0600), - ) - require.NoError(t, err) - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - var exports []client.ExportEntry - if integration.IsTestDockerdMoby(sb) { - exports = []client.ExportEntry{{ - Type: "moby", - Attrs: map[string]string{ - "name": "reg.dummy:5000/buildkit/test:latest", - }, - }} - } else { - exports = []client.ExportEntry{{ - Type: client.ExporterOCI, - Attrs: map[string]string{}, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }} - } - - res, err := f.Solve(sb.Context(), c, client.SolveOpt{ - FrontendAttrs: map[string]string{ - "build-arg:foo": "bar", - }, - Exports: exports, - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - }, - }, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "build-arg:foo") - require.Equal(t, "bar", *bi.Attrs["build-arg:foo"]) -} - -// moby/buildkit#2476 -func testBuildInfoMultiPlatform(t *testing.T, sb integration.Sandbox) { - integration.CheckFeatureCompat(t, sb, integration.FeatureOCIExporter, integration.FeatureMultiPlatform) - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - dockerfile := ` -FROM busybox:latest -ARG foo -RUN echo $foo -ADD https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md / -` - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", []byte(dockerfile), 0600), - ) - require.NoError(t, err) - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - platforms := []string{"linux/amd64", "linux/arm64"} - - res, err := f.Solve(sb.Context(), c, client.SolveOpt{ - FrontendAttrs: map[string]string{ - "build-arg:foo": "bar", - "platform": strings.Join(platforms, ","), - }, - Exports: []client.ExportEntry{ - { - Type: client.ExporterOCI, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }, - }, - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - }, - }, nil) - require.NoError(t, err) - - for _, platform := range platforms { - require.Contains(t, res.ExporterResponse, fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, platform)) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, platform)]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "build-arg:foo") - require.Equal(t, "bar", *bi.Attrs["build-arg:foo"]) - - _, isGateway := f.(*gatewayFrontend) - - sources := bi.Sources - if isGateway { - require.Equal(t, 3, len(sources), "%+v", sources) - sources = sources[1:] - } - require.Equal(t, 2, len(sources), "%+v", sources) - - assert.Equal(t, binfotypes.SourceTypeDockerImage, sources[0].Type) - assert.Equal(t, "docker.io/library/busybox:latest", sources[0].Ref) - assert.NotEmpty(t, sources[0].Pin) - - assert.Equal(t, binfotypes.SourceTypeHTTP, sources[1].Type) - assert.Equal(t, "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", sources[1].Ref) - assert.Equal(t, "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", sources[1].Pin) - } -} - -func testBuildInfoImageContext(t *testing.T, sb integration.Sandbox) { - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - dockerfile := ` -FROM busybox AS base -RUN cat /etc/alpine-release > /out -FROM scratch -COPY --from=base /out / -` - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", []byte(dockerfile), 0600), - ) - require.NoError(t, err) - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - var exports []client.ExportEntry - if integration.IsTestDockerdMoby(sb) { - exports = []client.ExportEntry{{ - Type: "moby", - Attrs: map[string]string{ - "name": "reg.dummy:5000/buildkit/test:latest", - }, - }} - } else { - exports = []client.ExportEntry{{ - Type: client.ExporterOCI, - Attrs: map[string]string{}, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }} - } - - res, err := f.Solve(sb.Context(), c, client.SolveOpt{ - FrontendAttrs: map[string]string{ - "build-arg:foo": "bar", - "context:busybox": "docker-image://alpine", - }, - Exports: exports, - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - }, - }, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "context:busybox") - require.Equal(t, "docker-image://alpine", *bi.Attrs["context:busybox"]) - require.Contains(t, bi.Attrs, "build-arg:foo") - require.Equal(t, "bar", *bi.Attrs["build-arg:foo"]) - - _, isGateway := f.(*gatewayFrontend) - - sources := bi.Sources - if isGateway { - require.Equal(t, 2, len(sources), "%+v", sources) - sources = sources[1:] - } else { - require.Equal(t, 1, len(sources)) - } - assert.Equal(t, binfotypes.SourceTypeDockerImage, sources[0].Type) - assert.Equal(t, "docker.io/library/alpine:latest", sources[0].Ref) - assert.NotEmpty(t, sources[0].Pin) -} - -func testBuildInfoLocalContext(t *testing.T, sb integration.Sandbox) { - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - dockerfile := ` -FROM busybox AS base -RUN cat /etc/alpine-release > /out -FROM scratch -COPY --from=base /o* / -` - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", []byte(dockerfile), 0600), - ) - require.NoError(t, err) - - c, err := client.New(sb.Context(), sb.Address()) - require.NoError(t, err) - defer c.Close() - - outf := []byte(`dummy-result`) - - dir2, err := integration.Tmpdir( - t, - fstest.CreateFile("out", outf, 0600), - fstest.CreateFile("out2", outf, 0600), - fstest.CreateFile(".dockerignore", []byte("out2\n"), 0600), - ) - require.NoError(t, err) - - var exports []client.ExportEntry - if integration.IsTestDockerdMoby(sb) { - exports = []client.ExportEntry{{ - Type: "moby", - Attrs: map[string]string{ - "name": "reg.dummy:5000/buildkit/test:latest", - }, - }} - } else { - exports = []client.ExportEntry{{ - Type: client.ExporterOCI, - Attrs: map[string]string{}, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }} - } - - res, err := f.Solve(sb.Context(), c, client.SolveOpt{ - FrontendAttrs: map[string]string{ - "build-arg:foo": "bar", - "context:base": "local:basedir", - }, - Exports: exports, - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - "basedir": dir2, - }, - }, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "context:base") - require.Equal(t, "local:basedir", *bi.Attrs["context:base"]) - require.Contains(t, bi.Attrs, "build-arg:foo") - require.Equal(t, "bar", *bi.Attrs["build-arg:foo"]) - - _, isGateway := f.(*gatewayFrontend) - if isGateway { - require.Equal(t, 1, len(bi.Sources)) - } else { - require.Equal(t, 0, len(bi.Sources)) - } -} - -func testBuildInfoDeps(t *testing.T, sb integration.Sandbox) { - t.Skip("deps temporarily disabled with SLSA provenance support") - - ctx := sb.Context() - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - c, err := client.New(ctx, sb.Address()) - require.NoError(t, err) - defer c.Close() - - dockerfile := []byte(` -FROM alpine -ENV FOO=bar -ADD https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md / -RUN echo first > /out -`) - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile, 0600), - ) - require.NoError(t, err) - - dockerfile2 := []byte(` -FROM base AS build -RUN echo "foo is $FOO" > /foo -FROM busybox -COPY --from=build /foo /out / -`) - - dir2, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile2, 0600), - ) - require.NoError(t, err) - - b := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { - res, err := f.SolveGateway(ctx, c, gateway.SolveRequest{}) - if err != nil { - return nil, err - } - ref, err := res.SingleRef() - if err != nil { - return nil, err - } - st, err := ref.ToState() - if err != nil { - return nil, err - } - - def, err := st.Marshal(ctx) - if err != nil { - return nil, err - } - - dtic, ok := res.Metadata[exptypes.ExporterImageConfigKey] - if !ok { - return nil, errors.Errorf("no containerimage.config in metadata") - } - - dtbi, ok := res.Metadata[exptypes.ExporterBuildInfo] - if !ok { - return nil, errors.Errorf("no containerimage.buildinfo in metadata") - } - - dt, err := json.Marshal(map[string][]byte{ - exptypes.ExporterImageConfigKey: dtic, - exptypes.ExporterBuildInfo: dtbi, - }) - if err != nil { - return nil, err - } - - res, err = f.SolveGateway(ctx, c, gateway.SolveRequest{ - FrontendOpt: map[string]string{ - "dockerfilekey": dockerui.DefaultLocalNameDockerfile + "2", - "context:base": "input:base", - "input-metadata:base": string(dt), - }, - FrontendInputs: map[string]*pb.Definition{ - "base": def.ToPB(), - }, - }) - if err != nil { - return nil, err - } - return res, nil - } - - destDir := t.TempDir() - - res, err := c.Build(ctx, client.SolveOpt{ - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - dockerui.DefaultLocalNameDockerfile + "2": dir2, - }, - Exports: []client.ExportEntry{ - { - Type: client.ExporterLocal, - OutputDir: destDir, - }, - }, - }, "", b, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "context:base") - require.Equal(t, "input:base", *bi.Attrs["context:base"]) - - require.Equal(t, 2, len(bi.Sources)) - - assert.Equal(t, binfotypes.SourceTypeDockerImage, bi.Sources[0].Type) - assert.Equal(t, "docker.io/library/busybox:latest", bi.Sources[0].Ref) - assert.NotEmpty(t, bi.Sources[0].Pin) - - assert.Equal(t, binfotypes.SourceTypeHTTP, bi.Sources[1].Type) - assert.Equal(t, "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", bi.Sources[1].Ref) - assert.Equal(t, "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", bi.Sources[1].Pin) - - require.Contains(t, bi.Deps, "base") - depsrc := bi.Deps["base"].Sources - require.Equal(t, 1, len(depsrc)) - assert.Equal(t, binfotypes.SourceTypeDockerImage, depsrc[0].Type) - assert.Equal(t, "docker.io/library/alpine:latest", depsrc[0].Ref) - assert.NotEmpty(t, depsrc[0].Pin) -} - -func testBuildInfoDepsMultiPlatform(t *testing.T, sb integration.Sandbox) { - t.Skip("deps temporarily disabled with SLSA provenance support") - - ctx := sb.Context() - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - platforms := []string{"linux/amd64", "linux/arm64"} - - c, err := client.New(ctx, sb.Address()) - require.NoError(t, err) - defer c.Close() - - dockerfile := []byte(` -FROM --platform=$BUILDPLATFORM alpine -ARG TARGETARCH -ENV FOO=bar-$TARGETARCH -RUN echo "foo $TARGETARCH" > /out -`) - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile, 0600), - ) - require.NoError(t, err) - - dockerfile2 := []byte(` -FROM base AS build -RUN echo "foo is $FOO" > /foo -FROM busybox -COPY --from=build /foo /out / -`) - - dir2, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile2, 0600), - ) - require.NoError(t, err) - - b := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { - res, err := f.SolveGateway(ctx, c, gateway.SolveRequest{ - FrontendOpt: map[string]string{ - "platform": strings.Join(platforms, ","), - }, - }) - if err != nil { - return nil, err - } - - if len(res.Refs) != 2 { - return nil, errors.Errorf("expected 2 refs, got %d", len(res.Refs)) - } - - frontendOpt := map[string]string{ - "dockerfilekey": dockerui.DefaultLocalNameDockerfile + "2", - "platform": strings.Join(platforms, ","), - } - - inputs := map[string]*pb.Definition{} - for _, platform := range platforms { - frontendOpt["context:base::"+platform] = "input:base::" + platform - - st, err := res.Refs[platform].ToState() - if err != nil { - return nil, err - } - def, err := st.Marshal(ctx) - if err != nil { - return nil, err - } - inputs["base::"+platform] = def.ToPB() - - dtic, ok := res.Metadata[exptypes.ExporterImageConfigKey+"/"+platform] - if !ok { - return nil, errors.Errorf("no containerimage.config/" + platform + " in metadata") - } - dtbi, ok := res.Metadata[exptypes.ExporterBuildInfo+"/"+platform] - if !ok { - return nil, errors.Errorf("no containerimage.buildinfo/" + platform + " in metadata") - } - dt, err := json.Marshal(map[string][]byte{ - exptypes.ExporterImageConfigKey: dtic, - exptypes.ExporterBuildInfo: dtbi, - }) - if err != nil { - return nil, err - } - frontendOpt["input-metadata:base::"+platform] = string(dt) - } - - res, err = f.SolveGateway(ctx, c, gateway.SolveRequest{ - FrontendOpt: frontendOpt, - FrontendInputs: inputs, - }) - if err != nil { - return nil, err - } - return res, nil - } - - destDir := t.TempDir() - - res, err := c.Build(ctx, client.SolveOpt{ - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - dockerui.DefaultLocalNameDockerfile + "2": dir2, - }, - Exports: []client.ExportEntry{ - { - Type: client.ExporterLocal, - OutputDir: destDir, - }, - }, - }, "", b, nil) - require.NoError(t, err) - - for _, platform := range platforms { - require.Contains(t, res.ExporterResponse, fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, platform)) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, platform)]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "context:base") - require.Equal(t, "input:base", *bi.Attrs["context:base"]) - - require.Equal(t, 1, len(bi.Sources)) - assert.Equal(t, binfotypes.SourceTypeDockerImage, bi.Sources[0].Type) - assert.Equal(t, "docker.io/library/busybox:latest", bi.Sources[0].Ref) - assert.NotEmpty(t, bi.Sources[0].Pin) - - require.Contains(t, bi.Deps, "base") - depsrc := bi.Deps["base"].Sources - require.Equal(t, 1, len(depsrc)) - assert.Equal(t, binfotypes.SourceTypeDockerImage, depsrc[0].Type) - assert.Equal(t, "docker.io/library/alpine:latest", depsrc[0].Ref) - assert.NotEmpty(t, depsrc[0].Pin) - } -} - -func testBuildInfoDepsMainNoSource(t *testing.T, sb integration.Sandbox) { - t.Skip("deps temporarily disabled with SLSA provenance support") - - ctx := sb.Context() - f := getFrontend(t, sb) - f.RequiresBuildctl(t) - - c, err := client.New(ctx, sb.Address()) - require.NoError(t, err) - defer c.Close() - - dockerfile := []byte(` -FROM alpine -ENV FOO=bar -ADD https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md / -RUN echo first > /out -`) - - dir, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile, 0600), - ) - require.NoError(t, err) - - dockerfile2 := []byte(` -FROM base AS build -RUN echo "foo is $FOO" > /foo -`) - - dir2, err := integration.Tmpdir( - t, - fstest.CreateFile("Dockerfile", dockerfile2, 0600), - ) - require.NoError(t, err) - - b := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) { - res, err := f.SolveGateway(ctx, c, gateway.SolveRequest{}) - if err != nil { - return nil, err - } - ref, err := res.SingleRef() - if err != nil { - return nil, err - } - st, err := ref.ToState() - if err != nil { - return nil, err - } - - def, err := st.Marshal(ctx) - if err != nil { - return nil, err - } - - dtic, ok := res.Metadata[exptypes.ExporterImageConfigKey] - if !ok { - return nil, errors.Errorf("no containerimage.config in metadata") - } - - dtbi, ok := res.Metadata[exptypes.ExporterBuildInfo] - if !ok { - return nil, errors.Errorf("no containerimage.buildinfo in metadata") - } - - dt, err := json.Marshal(map[string][]byte{ - exptypes.ExporterImageConfigKey: dtic, - exptypes.ExporterBuildInfo: dtbi, - }) - if err != nil { - return nil, err - } - - res, err = f.SolveGateway(ctx, c, gateway.SolveRequest{ - FrontendOpt: map[string]string{ - "dockerfilekey": dockerui.DefaultLocalNameDockerfile + "2", - "context:base": "input:base", - "input-metadata:base": string(dt), - }, - FrontendInputs: map[string]*pb.Definition{ - "base": def.ToPB(), - }, - }) - if err != nil { - return nil, err - } - return res, nil - } - - destDir := t.TempDir() - - res, err := c.Build(ctx, client.SolveOpt{ - LocalDirs: map[string]string{ - dockerui.DefaultLocalNameDockerfile: dir, - dockerui.DefaultLocalNameContext: dir, - dockerui.DefaultLocalNameDockerfile + "2": dir2, - }, - Exports: []client.ExportEntry{ - { - Type: client.ExporterLocal, - OutputDir: destDir, - }, - }, - }, "", b, nil) - require.NoError(t, err) - - require.Contains(t, res.ExporterResponse, exptypes.ExporterBuildInfo) - dtbi, err := base64.StdEncoding.DecodeString(res.ExporterResponse[exptypes.ExporterBuildInfo]) - require.NoError(t, err) - - var bi binfotypes.BuildInfo - err = json.Unmarshal(dtbi, &bi) - require.NoError(t, err) - - require.Contains(t, bi.Attrs, "context:base") - require.Equal(t, "input:base", *bi.Attrs["context:base"]) - - require.Equal(t, 1, len(bi.Sources)) - - assert.Equal(t, binfotypes.SourceTypeHTTP, bi.Sources[0].Type) - assert.Equal(t, "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", bi.Sources[0].Ref) - assert.Equal(t, "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", bi.Sources[0].Pin) - - require.Contains(t, bi.Deps, "base") - depsrc := bi.Deps["base"].Sources - require.Equal(t, 1, len(depsrc)) - assert.Equal(t, binfotypes.SourceTypeDockerImage, depsrc[0].Type) - assert.Equal(t, "docker.io/library/alpine:latest", depsrc[0].Ref) - assert.NotEmpty(t, depsrc[0].Pin) -} diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index c0b846afa333..b50aa7193903 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -6585,11 +6585,7 @@ COPY --from=0 / / }, Exports: []client.ExportEntry{ { - Type: client.ExporterOCI, - Attrs: map[string]string{ - // Remove buildinfo, as it contains the digest of the frontend image - "buildinfo": "false", - }, + Type: client.ExporterOCI, Output: fixedWriteCloser(outW), }, }, diff --git a/frontend/dockerfile/docs/reference.md b/frontend/dockerfile/docs/reference.md index 7c9417926b3c..73c1e00a046a 100644 --- a/frontend/dockerfile/docs/reference.md +++ b/frontend/dockerfile/docs/reference.md @@ -2217,7 +2217,6 @@ RUN echo "I'm building for $TARGETPLATFORM" |---------------------------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `BUILDKIT_CACHE_MOUNT_NS` | String | Set optional cache ID namespace. | | `BUILDKIT_CONTEXT_KEEP_GIT_DIR` | Bool | Trigger git context to keep the `.git` directory. | -| `BUILDKIT_INLINE_BUILDINFO_ATTRS`[^2] | Bool | Inline build info attributes in image config or not. | | `BUILDKIT_INLINE_CACHE`[^2] | Bool | Inline cache metadata to image config or not. | | `BUILDKIT_MULTI_PLATFORM` | Bool | Opt into determnistic output regardless of multi-platform output or not. | | `BUILDKIT_SANDBOX_HOSTNAME` | String | Set the hostname (default `buildkitsandbox`) | diff --git a/hack/images b/hack/images index cbaf0eb16f2c..d1315e6a17ef 100755 --- a/hack/images +++ b/hack/images @@ -44,10 +44,10 @@ attestFlags="$(buildAttestFlags)" outputFlag="--output=type=image,push=false" if [ "$PUSH" = "push" ]; then - outputFlag="--output=type=image,buildinfo-attrs=true,push=true" + outputFlag="--output=type=image,push=true" fi if [ -n "$localmode" ]; then - outputFlag="--output=type=docker,buildinfo-attrs=true" + outputFlag="--output=type=docker" attestFlags="" fi diff --git a/solver/jobs.go b/solver/jobs.go index d72eddcafb20..eab8ee58a76c 100644 --- a/solver/jobs.go +++ b/solver/jobs.go @@ -200,7 +200,6 @@ type subBuilder struct { } func (sb *subBuilder) Build(ctx context.Context, e Edge) (CachedResultWithProvenance, error) { - // TODO(@crazy-max): Handle BuildInfo from subbuild res, err := sb.solver.subBuild(ctx, e, sb.vtx) if err != nil { return nil, err diff --git a/solver/llbsolver/proc/sbom.go b/solver/llbsolver/proc/sbom.go index 2d7e969ba547..f7b83274055a 100644 --- a/solver/llbsolver/proc/sbom.go +++ b/solver/llbsolver/proc/sbom.go @@ -58,7 +58,7 @@ func SBOMProcessor(scannerRef string, useCache bool) llbsolver.Processor { return nil, err } - r, err := s.Bridge(j).Solve(ctx, frontend.SolveRequest{ // TODO: buildinfo + r, err := s.Bridge(j).Solve(ctx, frontend.SolveRequest{ Definition: def.ToPB(), }, j.SessionID) if err != nil { diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 2f7ba61e5f8f..efde7df3aed0 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -2,7 +2,6 @@ package llbsolver import ( "context" - "encoding/base64" "encoding/json" "fmt" "os" @@ -28,7 +27,6 @@ import ( "github.com/moby/buildkit/solver/result" spb "github.com/moby/buildkit/sourcepolicy/pb" "github.com/moby/buildkit/util/attestation" - "github.com/moby/buildkit/util/buildinfo" "github.com/moby/buildkit/util/compression" "github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/grpcerrors" @@ -559,9 +557,6 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro if strings.HasPrefix(k, "frontend.") { exporterResponse[k] = string(v) } - if strings.HasPrefix(k, exptypes.ExporterBuildInfo) { - exporterResponse[k] = base64.StdEncoding.EncodeToString(v) - } } for k, v := range cacheExporterResponse { if strings.HasPrefix(k, "cache.") { @@ -691,9 +686,6 @@ func addProvenanceToResult(res *frontend.Result, br *provenanceBridge) (*Result, if res.Metadata == nil { res.Metadata = map[string][]byte{} } - if err := buildinfo.AddMetadata(res.Metadata, exptypes.ExporterBuildInfo, cp); err != nil { - return nil, err - } } if len(res.Refs) != 0 { @@ -708,9 +700,6 @@ func addProvenanceToResult(res *frontend.Result, br *provenanceBridge) (*Result, if res.Metadata == nil { res.Metadata = map[string][]byte{} } - if err := buildinfo.AddMetadata(res.Metadata, fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, k), cp); err != nil { - return nil, err - } } if len(res.Attestations) != 0 { diff --git a/util/buildinfo/buildinfo.go b/util/buildinfo/buildinfo.go deleted file mode 100644 index 64b9ea48e146..000000000000 --- a/util/buildinfo/buildinfo.go +++ /dev/null @@ -1,451 +0,0 @@ -package buildinfo - -import ( - "context" - "encoding/base64" - "encoding/json" - "sort" - "strings" - - ctnref "github.com/containerd/containerd/reference" - "github.com/docker/distribution/reference" - "github.com/moby/buildkit/exporter/containerimage/exptypes" - "github.com/moby/buildkit/solver/llbsolver/provenance" - "github.com/moby/buildkit/source" - binfotypes "github.com/moby/buildkit/util/buildinfo/types" - "github.com/moby/buildkit/util/urlutil" - "github.com/pkg/errors" -) - -// BuildInfo format has been deprecated and will be removed in a future release. -// Use provenance attestations instead. - -func FromProvenance(c *provenance.Capture) (*binfotypes.BuildInfo, error) { - var bi binfotypes.BuildInfo - - bi.Frontend = c.Frontend - bi.Attrs = map[string]*string{} - for k, v := range c.Args { - v := v - bi.Attrs[k] = &v - } - - for _, s := range c.Sources.Images { - bi.Sources = append(bi.Sources, binfotypes.Source{ - Type: binfotypes.SourceTypeDockerImage, - Ref: s.Ref, - Pin: s.Digest.String(), - }) - } - - for _, s := range c.Sources.HTTP { - bi.Sources = append(bi.Sources, binfotypes.Source{ - Type: binfotypes.SourceTypeHTTP, - Ref: s.URL, - Pin: s.Digest.String(), - }) - } - - for _, s := range c.Sources.Git { - bi.Sources = append(bi.Sources, binfotypes.Source{ - Type: binfotypes.SourceTypeGit, - Ref: s.URL, - Pin: s.Commit, - }) - } - - sort.Slice(bi.Sources, func(i, j int) bool { - return bi.Sources[i].Ref < bi.Sources[j].Ref - }) - - return &bi, nil -} - -func AddMetadata(metadata map[string][]byte, key string, c *provenance.Capture) error { - bi, err := FromProvenance(c) - if err != nil { - return err - } - dt, err := json.Marshal(bi) - if err != nil { - return err - } - metadata[key] = dt - return nil -} - -// Decode decodes a base64 encoded build info. -func Decode(enc string) (bi binfotypes.BuildInfo, _ error) { - dec, err := base64.StdEncoding.DecodeString(enc) - if err != nil { - return bi, err - } - err = json.Unmarshal(dec, &bi) - return bi, err -} - -// Encode encodes build info. -func Encode(ctx context.Context, metadata map[string][]byte, key string, llbSources map[string]string) ([]byte, error) { - var bi binfotypes.BuildInfo - if metadata == nil { - metadata = make(map[string][]byte) - } - if v, ok := metadata[key]; ok && v != nil { - if err := json.Unmarshal(v, &bi); err != nil { - return nil, err - } - } - if sources, err := mergeSources(llbSources, bi.Sources); err == nil { - bi.Sources = sources - } else { - return nil, err - } - bi.Sources = dedupSources(bi.Sources, allDepsSources(bi.Deps, nil)) - return json.Marshal(bi) -} - -// mergeSources combines and fixes build sources from frontend sources. -func mergeSources(llbSources map[string]string, frontendSources []binfotypes.Source) ([]binfotypes.Source, error) { - if llbSources == nil { - llbSources = make(map[string]string) - } - // iterate and combine build sources - mbs := map[string]binfotypes.Source{} - for llbSource, pin := range llbSources { - src, err := source.FromString(llbSource) - if err != nil { - return nil, err - } - switch sourceID := src.(type) { - case *source.ImageIdentifier: - for i, fsrc := range frontendSources { - if fsrc.Type != binfotypes.SourceTypeDockerImage { - continue - } - // use original user input from frontend sources - if fsrc.Alias == sourceID.Reference.String() || fsrc.Pin == pin { - if fsrc.Alias == "" { - fsrc.Alias = sourceID.Reference.String() - } - parsed, err := reference.ParseNormalizedNamed(fsrc.Ref) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse %s", fsrc.Ref) - } - mbs[fsrc.Alias] = binfotypes.Source{ - Type: binfotypes.SourceTypeDockerImage, - Ref: reference.TagNameOnly(parsed).String(), - Pin: pin, - } - frontendSources = append(frontendSources[:i], frontendSources[i+1:]...) - break - } - } - if _, ok := mbs[sourceID.Reference.String()]; !ok { - mbs[sourceID.Reference.String()] = binfotypes.Source{ - Type: binfotypes.SourceTypeDockerImage, - Ref: sourceID.Reference.String(), - Pin: pin, - } - } - case *source.GitIdentifier: - sref := sourceID.Remote - if len(sourceID.Ref) > 0 { - sref += "#" + sourceID.Ref - } - if len(sourceID.Subdir) > 0 { - sref += ":" + sourceID.Subdir - } - if _, ok := mbs[sref]; !ok { - mbs[sref] = binfotypes.Source{ - Type: binfotypes.SourceTypeGit, - Ref: urlutil.RedactCredentials(sref), - Pin: pin, - } - } - case *source.HTTPIdentifier: - if _, ok := mbs[sourceID.URL]; !ok { - mbs[sourceID.URL] = binfotypes.Source{ - Type: binfotypes.SourceTypeHTTP, - Ref: urlutil.RedactCredentials(sourceID.URL), - Pin: pin, - } - } - } - } - - // leftover sources in frontend. Mostly duplicated ones we don't need but - // there is an edge case if no instruction except sources one is defined - // (e.g. FROM ...) that can be valid so take it into account. - for _, fsrc := range frontendSources { - if fsrc.Type != binfotypes.SourceTypeDockerImage { - continue - } - if _, ok := mbs[fsrc.Alias]; !ok { - parsed, err := reference.ParseNormalizedNamed(fsrc.Ref) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse %s", fsrc.Ref) - } - mbs[fsrc.Alias] = binfotypes.Source{ - Type: binfotypes.SourceTypeDockerImage, - Ref: reference.TagNameOnly(parsed).String(), - Pin: fsrc.Pin, - } - } - } - - srcs := make([]binfotypes.Source, 0, len(mbs)) - for _, bs := range mbs { - srcs = append(srcs, bs) - } - sort.Slice(srcs, func(i, j int) bool { - return srcs[i].Ref < srcs[j].Ref - }) - - return srcs, nil -} - -// decodeDeps decodes dependencies (buildinfo) added via the input context. -func decodeDeps(key string, attrs map[string]*string) (map[string]binfotypes.BuildInfo, error) { - var platform string - // extract platform from metadata key - if skey := strings.SplitN(key, "/", 2); len(skey) == 2 { - platform = skey[1] - } - - res := make(map[string]binfotypes.BuildInfo) - for k, v := range attrs { - // dependencies are only handled via the input context - if v == nil || !strings.HasPrefix(k, "input-metadata:") { - continue - } - - // if platform is defined in the key, only decode dependencies - // for that platform and vice versa - hasPlatform := len(strings.SplitN(k, "::", 2)) == 2 - if (platform != "" && !hasPlatform) || (platform == "" && hasPlatform) { - continue - } - - // decode input metadata - var inputresp map[string]string - if err := json.Unmarshal([]byte(*v), &inputresp); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal input-metadata") - } - - // check buildinfo key is present - if _, ok := inputresp[exptypes.ExporterBuildInfo]; !ok { - continue - } - - // decode buildinfo - bi, err := Decode(inputresp[exptypes.ExporterBuildInfo]) - if err != nil { - return nil, errors.Wrap(err, "failed to decode buildinfo from input-metadata") - } - - // set dep key - var depkey string - kl := strings.SplitN(k, ":", 2) - if len(kl) != 2 { - continue - } - depkey = strings.SplitN(kl[1], "::", 2)[0] - if platform != "" { - depkey = strings.TrimSuffix(depkey, "::"+platform) - } - - res[depkey] = bi - } - if len(res) == 0 { - return nil, nil - } - return res, nil -} - -// dedupSources deduplicates regular sources from dependencies ones. -func dedupSources(sources []binfotypes.Source, depsSources []binfotypes.Source) (srcs []binfotypes.Source) { - // dedup sources from deps - msrc := make(map[binfotypes.Source]struct{}) -sourcesloop: - for _, src := range sources { - for _, srcd := range depsSources { - if src == srcd { - continue sourcesloop - } - if src.Type == binfotypes.SourceTypeDockerImage && srcd.Type == binfotypes.SourceTypeDockerImage { - _, dgst := ctnref.SplitObject(src.Ref) - if dgst != "" && src.Pin == srcd.Pin { - continue sourcesloop - } - } - } - if _, ok := msrc[src]; !ok { - msrc[src] = struct{}{} - } - } - for src := range msrc { - srcs = append(srcs, src) - } - sort.Slice(srcs, func(i, j int) bool { - return srcs[i].Ref < srcs[j].Ref - }) - return srcs -} - -// allDepsSources gathers dependencies sources. -func allDepsSources(deps map[string]binfotypes.BuildInfo, visited map[binfotypes.Source]struct{}) (res []binfotypes.Source) { - if visited == nil { - visited = make(map[binfotypes.Source]struct{}) - } - if len(deps) == 0 { - return res - } - for _, dbi := range deps { - for _, dsrc := range dbi.Sources { - if _, ok := visited[dsrc]; ok { - continue - } - visited[dsrc] = struct{}{} - } - res = allDepsSources(dbi.Deps, visited) - } - for src := range visited { - res = append(res, src) - } - return res -} - -// FormatOpts holds build info format options. -type FormatOpts struct { - RemoveAttrs bool -} - -// Format formats build info. -func Format(dt []byte, opts FormatOpts) (_ []byte, err error) { - if len(dt) == 0 { - return dt, nil - } - - var bi binfotypes.BuildInfo - if err := json.Unmarshal(dt, &bi); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal buildinfo for formatting") - } - - if opts.RemoveAttrs { - bi.Attrs = nil - if len(bi.Deps) > 0 { - bi.Sources = dedupSources(append(bi.Sources, allDepsSources(bi.Deps, nil)...), nil) - bi.Deps = nil - } - } - - if dt, err = json.Marshal(bi); err != nil { - return nil, err - } - return dt, nil -} - -var knownAttrs = []string{ - //"cmdline", - "context", - "filename", - "source", - - //"add-hosts", - //"cgroup-parent", - //"force-network-mode", - //"hostname", - //"image-resolve-mode", - //"platform", - "shm-size", - "target", - "ulimit", -} - -// filterAttrs filters frontent opt by picking only those that -// could effectively change the build result. -func filterAttrs(key string, attrs map[string]*string) map[string]*string { - var platform string - // extract platform from metadata key - skey := strings.SplitN(key, "/", 2) - if len(skey) == 2 { - platform = skey[1] - } - filtered := make(map[string]*string) - for k, v := range attrs { - if v == nil { - continue - } - // control args are filtered out - if isControlArg(k) { - continue - } - // always include - if strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") || strings.HasPrefix(k, "vcs:") { - filtered[k] = v - continue - } - // input context key and value has to be cleaned up - // before being included - if strings.HasPrefix(k, "context:") { - ctxkey := strings.SplitN(k, "::", 2) - hasCtxPlatform := len(ctxkey) == 2 - // if platform is set and also defined in key, set context - // for the right one. - if hasCtxPlatform && platform != "" && platform != ctxkey[1] { - continue - } - if platform == "" && hasCtxPlatform { - ctxval := strings.TrimSuffix(*v, "::"+ctxkey[1]) - filtered[strings.TrimSuffix(k, "::"+ctxkey[1])] = &ctxval - continue - } - ctxival := strings.TrimSuffix(*v, "::"+platform) - filtered[strings.TrimSuffix(k, "::"+platform)] = &ctxival - continue - } - // filter only for known attributes - for _, knownAttr := range knownAttrs { - if knownAttr == k { - filtered[k] = v - break - } - } - } - return filtered -} - -var knownControlArgs = []string{ - "BUILDKIT_CACHE_MOUNT_NS", - "BUILDKIT_CONTEXT_KEEP_GIT_DIR", - "BUILDKIT_INLINE_BUILDINFO_ATTRS", - "BUILDKIT_INLINE_CACHE", - "BUILDKIT_MULTI_PLATFORM", - "BUILDKIT_SANDBOX_HOSTNAME", - "BUILDKIT_SYNTAX", -} - -// isControlArg checks if a build attributes is a control arg -func isControlArg(attrKey string) bool { - for _, k := range knownControlArgs { - if strings.HasPrefix(attrKey, "build-arg:"+k) { - return true - } - } - return false -} - -func reduceMapString(m1 map[string]string, m2 map[string]*string) map[string]string { - if m1 == nil && m2 == nil { - return nil - } - if m1 == nil { - m1 = map[string]string{} - } - for k, v := range m2 { - if v != nil { - m1[k] = *v - } - } - return m1 -} diff --git a/util/buildinfo/buildinfo_test.go b/util/buildinfo/buildinfo_test.go deleted file mode 100644 index d2967d01c1aa..000000000000 --- a/util/buildinfo/buildinfo_test.go +++ /dev/null @@ -1,520 +0,0 @@ -package buildinfo - -import ( - "encoding/json" - "testing" - - "github.com/moby/buildkit/exporter/containerimage/exptypes" - binfotypes "github.com/moby/buildkit/util/buildinfo/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestDecode(t *testing.T) { - bi, err := Decode("eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJidWlsZC1hcmc6QlVJTERLSVRfSU5MSU5FX0JVSUxESU5GT19BVFRSUyI6IjEiLCJjbWRsaW5lIjoiY3JhenltYXgvZG9ja2VyZmlsZTpidWlsZGF0dHJzIiwiY29udGV4dCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9jcmF6eS1tYXgvYnVpbGRraXQtYnVpbGRzb3VyY2VzLXRlc3QuZ2l0I21hc3RlciIsImZpbGVuYW1lIjoiRG9ja2VyZmlsZSIsInNvdXJjZSI6ImNyYXp5bWF4L2RvY2tlcmZpbGU6YnVpbGRhdHRycyJ9LCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2RvY2tlci9idWlsZHgtYmluOjAuNi4xQHNoYTI1NjphNjUyY2VkNGE0MTQxOTc3YzdkYWFlZDBhMDc0ZGNkOTg0NGE3OGQ3ZDI2MTU0NjViMTJmNDMzYWU2ZGQyOWYwIiwicGluIjoic2hhMjU2OmE2NTJjZWQ0YTQxNDE5NzdjN2RhYWVkMGEwNzRkY2Q5ODQ0YTc4ZDdkMjYxNTQ2NWIxMmY0MzNhZTZkZDI5ZjAifSx7InR5cGUiOiJkb2NrZXItaW1hZ2UiLCJyZWYiOiJkb2NrZXIuaW8vbGlicmFyeS9hbHBpbmU6My4xMyIsInBpbiI6InNoYTI1NjowMjZmNzIxYWY0Y2YyODQzZTA3YmJhNjQ4ZTE1OGZiMzVlY2M4NzZkODIyMTMwNjMzY2M0OWY3MDdmMGZjODhjIn0seyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL21vYnkvYnVpbGRraXQ6djAuOS4wIiwicGluIjoic2hhMjU2OjhkYzY2OGU3ZjY2ZGIxYzA0NGFhZGJlZDMwNjAyMDc0MzUxNmE5NDg0ODc5M2UwZjgxZjk0YTA4N2VlNzhjYWIifSx7InR5cGUiOiJkb2NrZXItaW1hZ2UiLCJyZWYiOiJkb2NrZXIuaW8vdG9uaXN0aWlnaS94eEBzaGEyNTY6MjFhNjFiZTQ3NDRmNjUzMWNiNWYzM2IwZTZmNDBlZGU0MWZhM2ExYjhjODJkNTk0NjE3OGY4MGNjODRiZmMwNCIsInBpbiI6InNoYTI1NjoyMWE2MWJlNDc0NGY2NTMxY2I1ZjMzYjBlNmY0MGVkZTQxZmEzYTFiOGM4MmQ1OTQ2MTc4ZjgwY2M4NGJmYzA0In0seyJ0eXBlIjoiZ2l0IiwicmVmIjoiaHR0cHM6Ly9naXRodWIuY29tL2NyYXp5LW1heC9idWlsZGtpdC1idWlsZHNvdXJjZXMtdGVzdC5naXQjbWFzdGVyIiwicGluIjoiNDNhOGJmOWMzNTFhYmY2NGIwODY1YTZhMDU0OGExZGUxZGVkNDBhOCJ9LHsidHlwZSI6Imh0dHAiLCJyZWYiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbW9ieS9tb2J5L21hc3Rlci9SRUFETUUubWQiLCJwaW4iOiJzaGEyNTY6NDE5NDU1MjAyYjBlZjk3ZTQ4MGQ3ZjgxOTliMjZhNzIxYTQxNzgxOGJjMGUyZDEwNjk3NWY3NDMyM2YyNWU2YyJ9XX0=") - require.NoError(t, err) - assert.Equal(t, 5, len(bi.Attrs)) - assert.Equal(t, 6, len(bi.Sources)) -} - -func TestMergeSources(t *testing.T) { - buildSourcesLLB := map[string]string{ - "docker-image://docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - "docker-image://docker.io/library/alpine:3.13@sha256:1d30d1ba3cb90962067e9b29491fbd56997979d54376f23f01448b5c5cd8b462": "sha256:1d30d1ba3cb90962067e9b29491fbd56997979d54376f23f01448b5c5cd8b462", - "docker-image://docker.io/moby/buildkit:v0.9.0@sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab": "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab", - "docker-image://docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04": "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04", - "git://https://github.com/crazy-max/buildkit-buildsources-test.git#master": "259a5aa5aa5bb3562d12cc631fe399f4788642c1", - "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md": "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - } - - frontendSources := []binfotypes.Source{ - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - Alias: "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - Pin: "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - }, - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/library/alpine:3.13", - Alias: "docker.io/library/alpine:3.13@sha256:1d30d1ba3cb90962067e9b29491fbd56997979d54376f23f01448b5c5cd8b462", - Pin: "sha256:1d30d1ba3cb90962067e9b29491fbd56997979d54376f23f01448b5c5cd8b462", - }, - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/moby/buildkit:v0.9.0", - Alias: "docker.io/moby/buildkit:v0.9.0@sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab", - Pin: "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab", - }, - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04", - Alias: "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04", - Pin: "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04", - }, - } - - srcs, err := mergeSources(buildSourcesLLB, frontendSources) - require.NoError(t, err) - - assert.Equal(t, []binfotypes.Source{ - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - Pin: "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0", - }, - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/library/alpine:3.13", - Pin: "sha256:1d30d1ba3cb90962067e9b29491fbd56997979d54376f23f01448b5c5cd8b462", - }, - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/moby/buildkit:v0.9.0", - Pin: "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab", - }, - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04", - Pin: "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04", - }, - { - Type: binfotypes.SourceTypeGit, - Ref: "https://github.com/crazy-max/buildkit-buildsources-test.git#master", - Pin: "259a5aa5aa5bb3562d12cc631fe399f4788642c1", - }, - { - Type: binfotypes.SourceTypeHTTP, - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, srcs) -} - -func TestDecodeDeps(t *testing.T) { - cases := []struct { - name string - key string - attrs map[string]*string - want map[string]binfotypes.BuildInfo - }{ - { - name: "simple", - key: exptypes.ExporterBuildInfo, - attrs: map[string]*string{ - "build-arg:bar": stringPtr("foo"), - "build-arg:foo": stringPtr("bar"), - "context:baseapp": stringPtr("input:0-base"), - "filename": stringPtr("Dockerfile"), - "input-metadata:0-base": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJidWlsZC1hcmc6YmFyIjoiZm9vIiwiYnVpbGQtYXJnOmZvbyI6ImJhciIsImZpbGVuYW1lIjoiYmFzZWFwcC5Eb2NrZXJmaWxlIn0sInNvdXJjZXMiOlt7InR5cGUiOiJkb2NrZXItaW1hZ2UiLCJyZWYiOiJkb2NrZXIuaW8vbGlicmFyeS9idXN5Ym94OmxhdGVzdCIsInBpbiI6InNoYTI1NjphZmNjN2YxYWMxYjQ5ZGIzMTdhNzE5NmM5MDJlNjFjNmMzYzQ2MDdkNjM1OTllZTFhODJkNzAyZDI0OWEwY2NiIn1dfQ==\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1NjpkMzE1MDVmZDUwNTBmNmI5NmNhMzI2OGQxZGI1OGZjOTFhZTU2MWRkZjE0ZWFhYmM0MWQ2M2VhMmVmOGMxYzZkIl19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMi0wMi0wNFQyMToyMDoxMi4zMTg5MTc4MjJaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjFjODUwN2UzZTliMjJiOTc3OGYyZWRiYjk1MDA2MWUwNmJkZTZhMWY1M2I2OWUxYzYxMDI1MDAyOWMzNzNiNzIgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIyLTAyLTA0VDIxOjIwOjEyLjQ5Nzc5NDgwOVoiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCJzaFwiXSIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJXT1JLRElSIC9zcmMiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9XSwiY29uZmlnIjp7IkVudiI6WyJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiJdLCJDbWQiOlsic2giXSwiV29ya2luZ0RpciI6Ii9zcmMiLCJPbkJ1aWxkIjpudWxsfX0=\"}"), - }, - want: map[string]binfotypes.BuildInfo{ - "0-base": { - Frontend: "dockerfile.v0", - Attrs: map[string]*string{ - "build-arg:bar": stringPtr("foo"), - "build-arg:foo": stringPtr("bar"), - "filename": stringPtr("baseapp.Dockerfile"), - }, - Sources: []binfotypes.Source{ - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb", - }, - }, - }, - }, - }, - { - name: "multiplatform", - key: exptypes.ExporterBuildInfo + "/linux/amd64", - attrs: map[string]*string{ - "context:base::linux/amd64": stringPtr("input:base::linux/amd64"), - "context:base::linux/arm64": stringPtr("input:base::linux/arm64"), - "dockerfilekey": stringPtr("dockerfile2"), - "input-metadata:base::linux/amd64": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2xpYnJhcnkvYWxwaW5lOmxhdGVzdCIsInBpbiI6InNoYTI1NjplN2Q4OGRlNzNkYjNkM2ZkOWIyZDYzYWE3ZjQ0N2ExMGZkMDIyMGI3Y2JmMzk4MDNjODAzZjJhZjliYTI1NmIzIn1dfQ==\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo4ZDNhYzM0ODk5OTY0MjNmNTNkNjA4N2M4MTE4MDAwNjI2M2I3OWYyMDZkM2ZkZWM5ZTY2ZjBlMjdjZWI4NzU5Il19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0xMS0yNFQyMDoxOTo0MC4xOTk3MDA5NDZaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjkyMzNmNmYyMjM3ZDc5NjU5YTk1MjFmN2UzOTBkZjIxN2NlYzQ5ZjFhOGFhM2ExMjE0N2JiY2ExOTU2YWNkYjkgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIxLTExLTI0VDIwOjE5OjQwLjQ4MzM2NzU0NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCIvYmluL3NoXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9LHsiY3JlYXRlZF9ieSI6IkFSRyBUQVJHRVRBUkNIIiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAiLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkX2J5IjoiRU5WIEZPTz1iYXItYW1kNjQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJSVU4gfDEgVEFSR0VUQVJDSD1hbWQ2NCAvYmluL3NoIC1jIGVjaG8gXCJmb28gJFRBUkdFVEFSQ0hcIiBcdTAwM2UgL291dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJGT089YmFyLWFtZDY0Il0sIkNtZCI6WyIvYmluL3NoIl0sIk9uQnVpbGQiOm51bGx9fQ==\"}"), - "input-metadata:base::linux/arm64": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2xpYnJhcnkvYWxwaW5lOmxhdGVzdCIsInBpbiI6InNoYTI1NjplN2Q4OGRlNzNkYjNkM2ZkOWIyZDYzYWE3ZjQ0N2ExMGZkMDIyMGI3Y2JmMzk4MDNjODAzZjJhZjliYTI1NmIzIn1dfQ==\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhcm02NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo4ZDNhYzM0ODk5OTY0MjNmNTNkNjA4N2M4MTE4MDAwNjI2M2I3OWYyMDZkM2ZkZWM5ZTY2ZjBlMjdjZWI4NzU5Il19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0xMS0yNFQyMDoxOTo0MC4xOTk3MDA5NDZaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjkyMzNmNmYyMjM3ZDc5NjU5YTk1MjFmN2UzOTBkZjIxN2NlYzQ5ZjFhOGFhM2ExMjE0N2JiY2ExOTU2YWNkYjkgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIxLTExLTI0VDIwOjE5OjQwLjQ4MzM2NzU0NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCIvYmluL3NoXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9LHsiY3JlYXRlZF9ieSI6IkFSRyBUQVJHRVRBUkNIIiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAiLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkX2J5IjoiRU5WIEZPTz1iYXItYXJtNjQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJSVU4gfDEgVEFSR0VUQVJDSD1hcm02NCAvYmluL3NoIC1jIGVjaG8gXCJmb28gJFRBUkdFVEFSQ0hcIiBcdTAwM2UgL291dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJGT089YmFyLWFybTY0Il0sIkNtZCI6WyIvYmluL3NoIl0sIk9uQnVpbGQiOm51bGx9fQ==\"}"), - "platform": stringPtr("linux/amd64,linux/arm64"), - }, - want: map[string]binfotypes.BuildInfo{ - "base": { - Frontend: "dockerfile.v0", - Attrs: nil, - Sources: []binfotypes.Source{ - { - Type: binfotypes.SourceTypeDockerImage, - Ref: "docker.io/library/alpine:latest", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - }, - }, - }, - }, - } - for _, tt := range cases { - tt := tt - t.Run(tt.name, func(t *testing.T) { - deps, err := decodeDeps(tt.key, tt.attrs) - require.NoError(t, err) - assert.Equal(t, tt.want, deps) - }) - } -} - -func TestDedupSources(t *testing.T) { - cases := []struct { - name string - sources []binfotypes.Source - deps map[string]binfotypes.BuildInfo - want []binfotypes.Source - }{ - { - name: "deps", - sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "http", - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, - deps: map[string]binfotypes.BuildInfo{ - "base": { - Frontend: "dockerfile.v0", - Sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine:latest", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - }, - }, - }, - want: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "http", - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, - }, - { - name: "multideps", - sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:1.35.0@sha256:20246233b52de844fa516f8c51234f1441e55e71ecdd1a1d91ebb252e1fd4603", - Pin: "sha256:20246233b52de844fa516f8c51234f1441e55e71ecdd1a1d91ebb252e1fd4603", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "http", - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, - deps: map[string]binfotypes.BuildInfo{ - "base": { - Frontend: "dockerfile.v0", - Sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine:latest", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:1.35.0", - Pin: "sha256:20246233b52de844fa516f8c51234f1441e55e71ecdd1a1d91ebb252e1fd4603", - }, - }, - }, - }, - want: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "http", - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, - }, - { - name: "regular", - sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - }, - deps: map[string]binfotypes.BuildInfo{ - "base": { - Frontend: "dockerfile.v0", - Sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine:latest", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - }, - }, - }, - want: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - }, - }, - } - for _, tt := range cases { - tt := tt - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, dedupSources(tt.sources, allDepsSources(tt.deps, nil))) - }) - } -} - -func TestFilterAttrs(t *testing.T) { - cases := []struct { - name string - key string - attrs map[string]*string - want map[string]*string - }{ - { - name: "simple", - key: exptypes.ExporterBuildInfo, - attrs: map[string]*string{ - "build-arg:foo": stringPtr("bar"), - "cmdline": stringPtr("crazymax/dockerfile:buildattrs"), - "context": stringPtr("https://github.com/crazy-max/buildkit-buildsources-test.git#master"), - "filename": stringPtr("Dockerfile"), - "source": stringPtr("crazymax/dockerfile:master"), - }, - want: map[string]*string{ - "build-arg:foo": stringPtr("bar"), - "context": stringPtr("https://github.com/crazy-max/buildkit-buildsources-test.git#master"), - "filename": stringPtr("Dockerfile"), - "source": stringPtr("crazymax/dockerfile:master"), - }, - }, - { - name: "multiplatform", - key: exptypes.ExporterBuildInfo + "/linux/amd64", - attrs: map[string]*string{ - "build-arg:bar": stringPtr("foo"), - "build-arg:foo": stringPtr("bar"), - "context:base::linux/amd64": stringPtr("input:base::linux/amd64"), - "context:base::linux/arm64": stringPtr("input:base::linux/arm64"), - "dockerfilekey": stringPtr("dockerfile2"), - "input-metadata:base::linux/amd64": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2xpYnJhcnkvYWxwaW5lOmxhdGVzdCIsInBpbiI6InNoYTI1NjplN2Q4OGRlNzNkYjNkM2ZkOWIyZDYzYWE3ZjQ0N2ExMGZkMDIyMGI3Y2JmMzk4MDNjODAzZjJhZjliYTI1NmIzIn1dfQ==\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo4ZDNhYzM0ODk5OTY0MjNmNTNkNjA4N2M4MTE4MDAwNjI2M2I3OWYyMDZkM2ZkZWM5ZTY2ZjBlMjdjZWI4NzU5Il19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0xMS0yNFQyMDoxOTo0MC4xOTk3MDA5NDZaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjkyMzNmNmYyMjM3ZDc5NjU5YTk1MjFmN2UzOTBkZjIxN2NlYzQ5ZjFhOGFhM2ExMjE0N2JiY2ExOTU2YWNkYjkgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIxLTExLTI0VDIwOjE5OjQwLjQ4MzM2NzU0NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCIvYmluL3NoXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9LHsiY3JlYXRlZF9ieSI6IkFSRyBUQVJHRVRBUkNIIiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAiLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkX2J5IjoiRU5WIEZPTz1iYXItYW1kNjQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJSVU4gfDEgVEFSR0VUQVJDSD1hbWQ2NCAvYmluL3NoIC1jIGVjaG8gXCJmb28gJFRBUkdFVEFSQ0hcIiBcdTAwM2UgL291dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJGT089YmFyLWFtZDY0Il0sIkNtZCI6WyIvYmluL3NoIl0sIk9uQnVpbGQiOm51bGx9fQ==\"}"), - "input-metadata:base::linux/arm64": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2xpYnJhcnkvYWxwaW5lOmxhdGVzdCIsInBpbiI6InNoYTI1NjplN2Q4OGRlNzNkYjNkM2ZkOWIyZDYzYWE3ZjQ0N2ExMGZkMDIyMGI3Y2JmMzk4MDNjODAzZjJhZjliYTI1NmIzIn1dfQ==\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhcm02NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo4ZDNhYzM0ODk5OTY0MjNmNTNkNjA4N2M4MTE4MDAwNjI2M2I3OWYyMDZkM2ZkZWM5ZTY2ZjBlMjdjZWI4NzU5Il19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0xMS0yNFQyMDoxOTo0MC4xOTk3MDA5NDZaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjkyMzNmNmYyMjM3ZDc5NjU5YTk1MjFmN2UzOTBkZjIxN2NlYzQ5ZjFhOGFhM2ExMjE0N2JiY2ExOTU2YWNkYjkgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIxLTExLTI0VDIwOjE5OjQwLjQ4MzM2NzU0NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCIvYmluL3NoXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9LHsiY3JlYXRlZF9ieSI6IkFSRyBUQVJHRVRBUkNIIiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAiLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkX2J5IjoiRU5WIEZPTz1iYXItYXJtNjQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJSVU4gfDEgVEFSR0VUQVJDSD1hcm02NCAvYmluL3NoIC1jIGVjaG8gXCJmb28gJFRBUkdFVEFSQ0hcIiBcdTAwM2UgL291dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJGT089YmFyLWFybTY0Il0sIkNtZCI6WyIvYmluL3NoIl0sIk9uQnVpbGQiOm51bGx9fQ==\"}"), - "platform": stringPtr("linux/amd64,linux/arm64"), - }, - want: map[string]*string{ - "build-arg:bar": stringPtr("foo"), - "build-arg:foo": stringPtr("bar"), - "context:base": stringPtr("input:base"), - }, - }, - } - for _, tt := range cases { - tt := tt - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, filterAttrs(tt.key, tt.attrs)) - }) - } -} - -func TestFormat(t *testing.T) { - bi := binfotypes.BuildInfo{ - Frontend: "dockerfile.v0", - Attrs: map[string]*string{ - "build-arg:foo": stringPtr("bar"), - "context:base": stringPtr("input:base"), - "filename": stringPtr("Dockerfile"), - "source": stringPtr("crazymax/dockerfile:master"), - }, - Sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "http", - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, - Deps: map[string]binfotypes.BuildInfo{ - "base": { - Frontend: "dockerfile.v0", - Attrs: map[string]*string{ - "build-arg:foo": stringPtr("bar"), - "filename": stringPtr("Dockerfile2"), - "source": stringPtr("crazymax/dockerfile:master"), - }, - Sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine:latest", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:1.35.0", - Pin: "sha256:20246233b52de844fa516f8c51234f1441e55e71ecdd1a1d91ebb252e1fd4603", - }, - }, - }, - }, - } - - cases := []struct { - name string - formatopts FormatOpts - want binfotypes.BuildInfo - }{ - { - name: "unchanged", - formatopts: FormatOpts{RemoveAttrs: false}, - want: bi, - }, - { - name: "remove attrs", - formatopts: FormatOpts{RemoveAttrs: true}, - want: binfotypes.BuildInfo{ - Frontend: "dockerfile.v0", - Sources: []binfotypes.Source{ - { - Type: "docker-image", - Ref: "docker.io/library/alpine:latest", - Pin: "sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:1.35.0", - Pin: "sha256:20246233b52de844fa516f8c51234f1441e55e71ecdd1a1d91ebb252e1fd4603", - }, - { - Type: "docker-image", - Ref: "docker.io/library/busybox:latest", - Pin: "sha256:b69959407d21e8a062e0416bf13405bb2b71ed7a84dde4158ebafacfa06f5578", - }, - { - Type: "http", - Ref: "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", - Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", - }, - }, - }, - }, - } - - for _, tt := range cases { - tt := tt - t.Run(tt.name, func(t *testing.T) { - dt, err := json.Marshal(bi) - require.NoError(t, err) - dt, err = Format(dt, tt.formatopts) - require.NoError(t, err) - var res binfotypes.BuildInfo - err = json.Unmarshal(dt, &res) - require.NoError(t, err) - assert.Equal(t, tt.want, res) - }) - } -} - -func TestReduceMapString(t *testing.T) { - cases := []struct { - name string - m1 map[string]*string - m2 map[string]string - expected map[string]string - }{ - { - name: "first", - m1: map[string]*string{"foo": stringPtr("bar"), "abc": stringPtr("def")}, - m2: map[string]string{"bar": "foo", "abc": "ghi"}, - expected: map[string]string{"foo": "bar", "abc": "def", "bar": "foo"}, - }, - { - name: "last", - m1: map[string]*string{"bar": stringPtr("foo"), "abc": stringPtr("ghi")}, - m2: map[string]string{"foo": "bar", "abc": "def"}, - expected: map[string]string{"bar": "foo", "abc": "ghi", "foo": "bar"}, - }, - { - name: "nilattr", - m1: map[string]*string{"foo": stringPtr("bar"), "abc": stringPtr("fgh"), "baz": nil}, - m2: map[string]string{"foo": "bar", "baz": "fuu"}, - expected: map[string]string{"foo": "bar", "abc": "fgh", "baz": "fuu"}, - }, - { - name: "null1", - m1: nil, - m2: map[string]string{"foo": "bar", "abc": "def"}, - expected: map[string]string{"foo": "bar", "abc": "def"}, - }, - { - name: "null2", - m1: map[string]*string{"foo": stringPtr("bar"), "abc": stringPtr("def")}, - m2: nil, - expected: map[string]string{"foo": "bar", "abc": "def"}, - }, - } - for _, tt := range cases { - tt := tt - t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expected, reduceMapString(tt.m2, tt.m1)) - }) - } -} - -// stringPtr returns a pointer to the string value passed in. -func stringPtr(v string) *string { - return &v -} diff --git a/util/buildinfo/types/types.go b/util/buildinfo/types/types.go deleted file mode 100644 index 93abcd1b4f12..000000000000 --- a/util/buildinfo/types/types.go +++ /dev/null @@ -1,52 +0,0 @@ -package binfotypes - -import ( - srctypes "github.com/moby/buildkit/source/types" -) - -// ImageConfigField defines the key of build dependencies. -const ImageConfigField = "moby.buildkit.buildinfo.v1" - -// ImageConfig defines the structure of build dependencies -// inside image config. -type ImageConfig struct { - BuildInfo string `json:"moby.buildkit.buildinfo.v1,omitempty"` -} - -// BuildInfo defines the main structure added to image config as -// ImageConfigField key and returned in solver ExporterResponse as -// exptypes.ExporterBuildInfo key. -type BuildInfo struct { - // Frontend defines the frontend used to build. - Frontend string `json:"frontend,omitempty"` - // Attrs defines build request attributes. - Attrs map[string]*string `json:"attrs,omitempty"` - // Sources defines build dependencies. - Sources []Source `json:"sources,omitempty"` - // Deps defines context dependencies. - Deps map[string]BuildInfo `json:"deps,omitempty"` -} - -// Source defines a build dependency. -type Source struct { - // Type defines the SourceType source type (docker-image, git, http). - Type SourceType `json:"type,omitempty"` - // Ref is the reference of the source. - Ref string `json:"ref,omitempty"` - // Alias is a special field used to match with the actual source ref - // because frontend might have already transformed a string user typed - // before generating LLB. - Alias string `json:"alias,omitempty"` - // Pin is the source digest. - Pin string `json:"pin,omitempty"` -} - -// SourceType contains source type. -type SourceType string - -// List of source types. -const ( - SourceTypeDockerImage SourceType = srctypes.DockerImageScheme - SourceTypeGit SourceType = srctypes.GitScheme - SourceTypeHTTP SourceType = srctypes.HTTPScheme -) diff --git a/util/imageutil/buildinfo.go b/util/imageutil/buildinfo.go deleted file mode 100644 index 2ef1e75cfc0b..000000000000 --- a/util/imageutil/buildinfo.go +++ /dev/null @@ -1,32 +0,0 @@ -package imageutil - -import ( - "encoding/base64" - "encoding/json" - - binfotypes "github.com/moby/buildkit/util/buildinfo/types" - "github.com/pkg/errors" -) - -// BuildInfo returns build info from image config. -func BuildInfo(dt []byte) (*binfotypes.BuildInfo, error) { - if len(dt) == 0 { - return nil, nil - } - var config binfotypes.ImageConfig - if err := json.Unmarshal(dt, &config); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal image config") - } - if len(config.BuildInfo) == 0 { - return nil, nil - } - dtbi, err := base64.StdEncoding.DecodeString(config.BuildInfo) - if err != nil { - return nil, err - } - var bi binfotypes.BuildInfo - if err = json.Unmarshal(dtbi, &bi); err != nil { - return nil, errors.Wrap(err, "failed to decode buildinfo from image config") - } - return &bi, nil -}