Skip to content

Commit

Permalink
buildinfo: opt-in build attributes
Browse files Browse the repository at this point in the history
Signed-off-by: CrazyMax <[email protected]>
  • Loading branch information
crazy-max committed Jan 11, 2022
1 parent a136d08 commit c190de4
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 10 deletions.
14 changes: 13 additions & 1 deletion exporter/containerimage/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const (
keyLayerCompression = "compression"
keyForceCompression = "force-compression"
keyBuildInfo = "buildinfo"
keyBuildInfoAttrs = "buildinfo-attrs"
ociTypes = "oci-mediatypes"
)

Expand Down Expand Up @@ -174,6 +175,16 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp
return nil, err
}
i.buildInfoMode = bimode
case keyBuildInfoAttrs:
if v == "" {
i.buildInfoAttrs = false
continue
}
b, err := strconv.ParseBool(v)
if err != nil {
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
}
i.buildInfoAttrs = b
default:
if i.meta == nil {
i.meta = make(map[string][]byte)
Expand Down Expand Up @@ -201,6 +212,7 @@ type imageExporterInstance struct {
layerCompression compression.Type
forceCompression bool
buildInfoMode buildinfo.ExportMode
buildInfoAttrs bool
meta map[string][]byte
}

Expand Down Expand Up @@ -231,7 +243,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source,
}
defer done(context.TODO())

desc, err := e.opt.ImageWriter.Commit(ctx, src, e.ociTypes, e.layerCompression, e.buildInfoMode, e.forceCompression, sessionID)
desc, err := e.opt.ImageWriter.Commit(ctx, src, e.ociTypes, e.layerCompression, e.buildInfoMode, e.buildInfoAttrs, e.forceCompression, sessionID)
if err != nil {
return nil, err
}
Expand Down
13 changes: 10 additions & 3 deletions exporter/containerimage/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type ImageWriter struct {
opt WriterOpt
}

func (ic *ImageWriter) Commit(ctx context.Context, inp exporter.Source, oci bool, compressionType compression.Type, buildInfoMode buildinfo.ExportMode, forceCompression bool, sessionID string) (*ocispecs.Descriptor, error) {
func (ic *ImageWriter) Commit(ctx context.Context, inp exporter.Source, oci bool, compressionType compression.Type, buildInfoMode buildinfo.ExportMode, buildInfoAttrs bool, forceCompression bool, sessionID string) (*ocispecs.Descriptor, error) {
platformsBytes, ok := inp.Metadata[exptypes.ExporterPlatformsKey]

if len(inp.Refs) > 0 && !ok {
Expand All @@ -64,7 +64,12 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp exporter.Source, oci bool

var buildInfo []byte
if buildInfoMode&buildinfo.ExportImageConfig > 0 {
buildInfo = inp.Metadata[exptypes.ExporterBuildInfo]
buildInfo, err = buildinfo.Format(inp.Metadata[exptypes.ExporterBuildInfo], buildinfo.FormatOpts{
RemoveAttrs: !buildInfoAttrs,
})
if err != nil {
return nil, err
}
}

mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, inp.Ref, inp.Metadata[exptypes.ExporterImageConfigKey], &remotes[0], oci, inp.Metadata[exptypes.ExporterInlineCache], buildInfo)
Expand Down Expand Up @@ -131,7 +136,9 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp exporter.Source, oci bool

var buildInfo []byte
if buildInfoMode&buildinfo.ExportImageConfig > 0 {
buildInfo = inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, p.ID)]
buildInfo, err = buildinfo.Format(inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, p.ID)], buildinfo.FormatOpts{
RemoveAttrs: !buildInfoAttrs,
})
}

desc, _, err := ic.commitDistributionManifest(ctx, r, config, &remotes[remotesMap[p.ID]], oci, inlineCache, buildInfo)
Expand Down
14 changes: 13 additions & 1 deletion exporter/oci/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const (
ociTypes = "oci-mediatypes"
keyForceCompression = "force-compression"
keyBuildInfo = "buildinfo"
keyBuildInfoAttrs = "buildinfo-attrs"
)

type Opt struct {
Expand Down Expand Up @@ -112,6 +113,16 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp
return nil, err
}
i.buildInfoMode = bimode
case keyBuildInfoAttrs:
if v == "" {
i.buildInfoAttrs = false
continue
}
b, err := strconv.ParseBool(v)
if err != nil {
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
}
i.buildInfoAttrs = b
default:
if i.meta == nil {
i.meta = make(map[string][]byte)
Expand Down Expand Up @@ -139,6 +150,7 @@ type imageExporterInstance struct {
layerCompression compression.Type
forceCompression bool
buildInfoMode buildinfo.ExportMode
buildInfoAttrs bool
}

func (e *imageExporterInstance) Name() string {
Expand Down Expand Up @@ -172,7 +184,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source,
}
defer done(context.TODO())

desc, err := e.opt.ImageWriter.Commit(ctx, src, e.ociTypes, e.layerCompression, e.buildInfoMode, e.forceCompression, sessionID)
desc, err := e.opt.ImageWriter.Commit(ctx, src, e.ociTypes, e.layerCompression, e.buildInfoMode, e.buildInfoAttrs, e.forceCompression, sessionID)
if err != nil {
return nil, err
}
Expand Down
5 changes: 4 additions & 1 deletion frontend/dockerfile/docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,10 @@ RUN FOO=abc ash /app/script.sh

* `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_CACHE=<bool>` inline cache metadata to image configuration or not (for Docker-integrated BuildKit (`DOCKER_BUILDKIT=1 docker build`) and `docker buildx`)
* `BUILDKIT_INLINE_BUILDINFO_ATTRS=<bool>`¹ inline build info attributes in image config or not
* `BUILDKIT_INLINE_CACHE=<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`)
* `BUILDKIT_SYNTAX=<image>` set frontend image

> **¹** For Docker-integrated BuildKit (`DOCKER_BUILDKIT=1 docker build`) and `docker buildx`
29 changes: 26 additions & 3 deletions util/buildinfo/buildinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func Decode(enc string) (bi binfotypes.BuildInfo, _ error) {
}

// Encode encodes build info.
func Encode(ctx context.Context, solveReq frontend.SolveRequest, buildSources map[string]string, imageConfig []byte) ([]byte, error) {
func Encode(ctx context.Context, req frontend.SolveRequest, buildSources map[string]string, imageConfig []byte) ([]byte, error) {
icbi, err := FromImageConfig(imageConfig)
if err != nil {
return nil, err
Expand All @@ -36,8 +36,8 @@ func Encode(ctx context.Context, solveReq frontend.SolveRequest, buildSources ma
return nil, err
}
return json.Marshal(binfotypes.BuildInfo{
Frontend: solveReq.Frontend,
Attrs: filterAttrs(solveReq.FrontendOpt),
Frontend: req.Frontend,
Attrs: filterAttrs(req.FrontendOpt),
Sources: srcs,
})
}
Expand Down Expand Up @@ -154,6 +154,29 @@ func FromImageConfig(imageConfig []byte) (bi binfotypes.BuildInfo, _ error) {
return bi, nil
}

// FormatOpts holds build info format options.
type FormatOpts struct {
RemoveAttrs bool
}

// Format formats build info.
func Format(dt []byte, format 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 format.RemoveAttrs {
bi.Attrs = nil
}
if dt, err = json.Marshal(bi); err != nil {
return nil, err
}
return dt, nil
}

var knownAttrs = []string{
"cmdline",
"context",
Expand Down
126 changes: 125 additions & 1 deletion util/buildinfo/buildinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,63 @@ package buildinfo

import (
"context"
"encoding/json"
"testing"

binfotypes "github.com/moby/buildkit/util/buildinfo/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var bifixture = binfotypes.BuildInfo{
Frontend: "dockerfile.v0",
Attrs: map[string]string{
"build-arg:BUILDKIT_INLINE_BUILDINFO_ATTRS": "1",
"build-arg:foo": "bar",
"cmdline": "crazymax/dockerfile:master",
"context": "https://github.com/crazy-max/buildkit-buildsources-test.git#master",
"filename": "Dockerfile",
"platform": "linux/amd64,linux/arm64",
"source": "crazymax/dockerfile:master",
},
Sources: []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/master/README.md",
Pin: "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c",
},
},
}

func TestDecode(t *testing.T) {
bi, err := Decode("eyJzb3VyY2VzIjpbeyJ0eXBlIjoiaW1hZ2UiLCJyZWYiOiJkb2NrZXIuaW8vZG9ja2VyL2J1aWxkeC1iaW46MC42LjFAc2hhMjU2OmE2NTJjZWQ0YTQxNDE5NzdjN2RhYWVkMGEwNzRkY2Q5ODQ0YTc4ZDdkMjYxNTQ2NWIxMmY0MzNhZTZkZDI5ZjAiLCJwaW4iOiJzaGEyNTY6YTY1MmNlZDRhNDE0MTk3N2M3ZGFhZWQwYTA3NGRjZDk4NDRhNzhkN2QyNjE1NDY1YjEyZjQzM2FlNmRkMjlmMCJ9LHsidHlwZSI6ImltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2xpYnJhcnkvYWxwaW5lOjMuMTMiLCJwaW4iOiJzaGEyNTY6MWQzMGQxYmEzY2I5MDk2MjA2N2U5YjI5NDkxZmJkNTY5OTc5NzlkNTQzNzZmMjNmMDE0NDhiNWM1Y2Q4YjQ2MiJ9LHsidHlwZSI6ImltYWdlIiwicmVmIjoiZG9ja2VyLmlvL21vYnkvYnVpbGRraXQ6djAuOS4wIiwicGluIjoic2hhMjU2OjhkYzY2OGU3ZjY2ZGIxYzA0NGFhZGJlZDMwNjAyMDc0MzUxNmE5NDg0ODc5M2UwZjgxZjk0YTA4N2VlNzhjYWIifSx7InR5cGUiOiJpbWFnZSIsInJlZiI6ImRvY2tlci5pby90b25pc3RpaWdpL3h4QHNoYTI1NjoyMWE2MWJlNDc0NGY2NTMxY2I1ZjMzYjBlNmY0MGVkZTQxZmEzYTFiOGM4MmQ1OTQ2MTc4ZjgwY2M4NGJmYzA0IiwicGluIjoic2hhMjU2OjIxYTYxYmU0NzQ0ZjY1MzFjYjVmMzNiMGU2ZjQwZWRlNDFmYTNhMWI4YzgyZDU5NDYxNzhmODBjYzg0YmZjMDQifSx7InR5cGUiOiJnaXQiLCJyZWYiOiJodHRwczovL2dpdGh1Yi5jb20vY3JhenktbWF4L2J1aWxka2l0LWJ1aWxkc291cmNlcy10ZXN0LmdpdCNtYXN0ZXIiLCJwaW4iOiIyNTlhNWFhNWFhNWJiMzU2MmQxMmNjNjMxZmUzOTlmNDc4ODY0MmMxIn0seyJ0eXBlIjoiaHR0cCIsInJlZiI6Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9tb2J5L21vYnkvbWFzdGVyL1JFQURNRS5tZCIsInBpbiI6InNoYTI1Njo0MTk0NTUyMDJiMGVmOTdlNDgwZDdmODE5OWIyNmE3MjFhNDE3ODE4YmMwZTJkMTA2OTc1Zjc0MzIzZjI1ZTZjIn1dfQ==")
bi, err := Decode("eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJidWlsZC1hcmc6QlVJTERLSVRfSU5MSU5FX0JVSUxESU5GT19BVFRSUyI6IjEiLCJjbWRsaW5lIjoiY3JhenltYXgvZG9ja2VyZmlsZTpidWlsZGF0dHJzIiwiY29udGV4dCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9jcmF6eS1tYXgvYnVpbGRraXQtYnVpbGRzb3VyY2VzLXRlc3QuZ2l0I21hc3RlciIsImZpbGVuYW1lIjoiRG9ja2VyZmlsZSIsInNvdXJjZSI6ImNyYXp5bWF4L2RvY2tlcmZpbGU6YnVpbGRhdHRycyJ9LCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2RvY2tlci9idWlsZHgtYmluOjAuNi4xQHNoYTI1NjphNjUyY2VkNGE0MTQxOTc3YzdkYWFlZDBhMDc0ZGNkOTg0NGE3OGQ3ZDI2MTU0NjViMTJmNDMzYWU2ZGQyOWYwIiwicGluIjoic2hhMjU2OmE2NTJjZWQ0YTQxNDE5NzdjN2RhYWVkMGEwNzRkY2Q5ODQ0YTc4ZDdkMjYxNTQ2NWIxMmY0MzNhZTZkZDI5ZjAifSx7InR5cGUiOiJkb2NrZXItaW1hZ2UiLCJyZWYiOiJkb2NrZXIuaW8vbGlicmFyeS9hbHBpbmU6My4xMyIsInBpbiI6InNoYTI1NjowMjZmNzIxYWY0Y2YyODQzZTA3YmJhNjQ4ZTE1OGZiMzVlY2M4NzZkODIyMTMwNjMzY2M0OWY3MDdmMGZjODhjIn0seyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL21vYnkvYnVpbGRraXQ6djAuOS4wIiwicGluIjoic2hhMjU2OjhkYzY2OGU3ZjY2ZGIxYzA0NGFhZGJlZDMwNjAyMDc0MzUxNmE5NDg0ODc5M2UwZjgxZjk0YTA4N2VlNzhjYWIifSx7InR5cGUiOiJkb2NrZXItaW1hZ2UiLCJyZWYiOiJkb2NrZXIuaW8vdG9uaXN0aWlnaS94eEBzaGEyNTY6MjFhNjFiZTQ3NDRmNjUzMWNiNWYzM2IwZTZmNDBlZGU0MWZhM2ExYjhjODJkNTk0NjE3OGY4MGNjODRiZmMwNCIsInBpbiI6InNoYTI1NjoyMWE2MWJlNDc0NGY2NTMxY2I1ZjMzYjBlNmY0MGVkZTQxZmEzYTFiOGM4MmQ1OTQ2MTc4ZjgwY2M4NGJmYzA0In0seyJ0eXBlIjoiZ2l0IiwicmVmIjoiaHR0cHM6Ly9naXRodWIuY29tL2NyYXp5LW1heC9idWlsZGtpdC1idWlsZHNvdXJjZXMtdGVzdC5naXQjbWFzdGVyIiwicGluIjoiNDNhOGJmOWMzNTFhYmY2NGIwODY1YTZhMDU0OGExZGUxZGVkNDBhOCJ9LHsidHlwZSI6Imh0dHAiLCJyZWYiOiJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbW9ieS9tb2J5L21hc3Rlci9SRUFETUUubWQiLCJwaW4iOiJzaGEyNTY6NDE5NDU1MjAyYjBlZjk3ZTQ4MGQ3ZjgxOTliMjZhNzIxYTQxNzgxOGJjMGUyZDEwNjk3NWY3NDMyM2YyNWU2YyJ9XX0=")
require.NoError(t, err)
assert.Equal(t, 5, len(bi.Attrs))
assert.Equal(t, 6, len(bi.Sources))
}

Expand Down Expand Up @@ -90,3 +137,80 @@ func TestMergeSources(t *testing.T) {
},
}, srcs)
}

func TestFromImageConfig(t *testing.T) {
dtbi, err := json.Marshal(bifixture)
require.NoError(t, err)
dtic, err := json.Marshal(struct {
BuildInfo []byte `json:"moby.buildkit.buildinfo.v1"`
}{
BuildInfo: dtbi,
})
require.NoError(t, err)
bi, err := FromImageConfig(dtic)
require.NoError(t, err)
assert.Equal(t, 7, len(bi.Attrs))
assert.Equal(t, 6, len(bi.Sources))
}

func TestFormat(t *testing.T) {
bi := binfotypes.BuildInfo{
Frontend: "dockerfile.v0",
Attrs: map[string]string{
"build-arg:foo": "bar",
"cmdline": "crazymax/dockerfile:master",
"context": "https://github.com/crazy-max/buildkit-buildsources-test.git#master",
"filename": "Dockerfile",
"platform": "linux/amd64,linux/arm64",
"source": "crazymax/dockerfile:master",
},
Sources: []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",
},
},
}

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: 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",
},
},
},
},
}
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)
})
}
}

0 comments on commit c190de4

Please sign in to comment.