Merge pull request #2654 from crazy-max/buildinfo-contexts
buildinfo: named input contexts supportmaster
commit
b0e56cd563
|
@ -5350,12 +5350,13 @@ func testBuildInfoInline(t *testing.T, sb integration.Sandbox) {
|
|||
require.NoError(t, err)
|
||||
|
||||
var config binfotypes.ImageConfig
|
||||
err = json.Unmarshal(dt, &config)
|
||||
require.NoError(t, json.Unmarshal(dt, &config))
|
||||
|
||||
dec, err := base64.StdEncoding.DecodeString(config.BuildInfo)
|
||||
require.NoError(t, err)
|
||||
|
||||
var bi binfotypes.BuildInfo
|
||||
err = json.Unmarshal(config.BuildInfo, &bi)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, json.Unmarshal(dec, &bi))
|
||||
|
||||
if tt.buildAttrs {
|
||||
attrval := "bar"
|
||||
|
|
|
@ -6,18 +6,6 @@ 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.
|
||||
|
||||
By default, the build dependencies are inlined in the image configuration. You
|
||||
can disable this behavior with the [`buildinfo` attribute](../README.md#imageregistry).
|
||||
|
||||
### Image config
|
||||
|
||||
A new field similar to the one for inline cache has been added to the image
|
||||
configuration to embed build dependencies:
|
||||
|
||||
```text
|
||||
"moby.buildkit.buildinfo.v1": <base64>
|
||||
```
|
||||
|
||||
The structure is base64 encoded and has the following format when decoded:
|
||||
|
||||
```json
|
||||
|
@ -57,10 +45,25 @@ The structure is base64 encoded and has the following format when decoded:
|
|||
|
||||
* `frontend` defines the frontend used to build.
|
||||
* `attrs` defines build request attributes.
|
||||
* `sources` defines build dependencies.
|
||||
* `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": "<base64>"
|
||||
}
|
||||
```
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -382,16 +382,16 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
|||
|
||||
buildContext := &mutableOutput{}
|
||||
ctxPaths := map[string]struct{}{}
|
||||
buildinfo := &binfotypes.BuildInfo{}
|
||||
buildInfo := &binfotypes.BuildInfo{}
|
||||
|
||||
for _, d := range allDispatchStates.states {
|
||||
if !isReachable(target, d) {
|
||||
continue
|
||||
}
|
||||
|
||||
// collect build dependencies
|
||||
// collect build sources and dependencies
|
||||
if d.buildSource != nil {
|
||||
buildinfo.Sources = append(buildinfo.Sources, *d.buildSource)
|
||||
buildInfo.Sources = append(buildInfo.Sources, *d.buildSource)
|
||||
}
|
||||
|
||||
if d.base != nil {
|
||||
|
@ -469,9 +469,9 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
|||
}
|
||||
|
||||
// sort build sources
|
||||
if len(buildinfo.Sources) > 0 {
|
||||
sort.Slice(buildinfo.Sources, func(i, j int) bool {
|
||||
return buildinfo.Sources[i].Ref < buildinfo.Sources[j].Ref
|
||||
if len(buildInfo.Sources) > 0 {
|
||||
sort.Slice(buildInfo.Sources, func(i, j int) bool {
|
||||
return buildInfo.Sources[i].Ref < buildInfo.Sources[j].Ref
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -512,7 +512,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
|
|||
target.image.Variant = platformOpt.targetPlatform.Variant
|
||||
}
|
||||
|
||||
return &st, &target.image, buildinfo, nil
|
||||
return &st, &target.image, buildInfo, nil
|
||||
}
|
||||
|
||||
func metaArgsToMap(metaArgs []instructions.KeyValuePairOptional) map[string]string {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dockerfile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -16,16 +17,21 @@ import (
|
|||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/builder"
|
||||
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(
|
||||
testBuildSources,
|
||||
testBuildAttrs,
|
||||
testBuildInfoSources,
|
||||
testBuildInfoAttrs,
|
||||
testBuildInfoMultiPlatform,
|
||||
testBuildInfoDeps,
|
||||
testBuildInfoDepsMultiPlatform,
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -33,7 +39,7 @@ func init() {
|
|||
}
|
||||
|
||||
// moby/buildkit#2311
|
||||
func testBuildSources(t *testing.T, sb integration.Sandbox) {
|
||||
func testBuildInfoSources(t *testing.T, sb integration.Sandbox) {
|
||||
f := getFrontend(t, sb)
|
||||
|
||||
gitDir, err := ioutil.TempDir("", "buildkit")
|
||||
|
@ -114,7 +120,7 @@ COPY --from=alpine /bin/busybox /alpine-busybox
|
|||
}
|
||||
|
||||
// moby/buildkit#2476
|
||||
func testBuildAttrs(t *testing.T, sb integration.Sandbox) {
|
||||
func testBuildInfoAttrs(t *testing.T, sb integration.Sandbox) {
|
||||
f := getFrontend(t, sb)
|
||||
f.RequiresBuildctl(t)
|
||||
|
||||
|
@ -245,3 +251,276 @@ ADD https://raw.githubusercontent.com/moby/moby/master/README.md /
|
|||
assert.Equal(t, "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", sources[1].Pin)
|
||||
}
|
||||
}
|
||||
|
||||
func testBuildInfoDeps(t *testing.T, sb integration.Sandbox) {
|
||||
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
|
||||
RUN echo first > /out
|
||||
`)
|
||||
|
||||
dir, err := tmpdir(
|
||||
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
dockerfile2 := []byte(`
|
||||
FROM base AS build
|
||||
RUN echo "foo is $FOO" > /foo
|
||||
FROM busybox
|
||||
COPY --from=build /foo /out /
|
||||
`)
|
||||
|
||||
dir2, err := tmpdir(
|
||||
fstest.CreateFile("Dockerfile", dockerfile2, 0600),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
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": builder.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, err := ioutil.TempDir("", "buildkit")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(destDir)
|
||||
|
||||
res, err := c.Build(ctx, client.SolveOpt{
|
||||
LocalDirs: map[string]string{
|
||||
builder.DefaultLocalNameDockerfile: dir,
|
||||
builder.DefaultLocalNameContext: dir,
|
||||
builder.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.Equal(t, 2, len(bi.Sources))
|
||||
assert.Equal(t, binfotypes.SourceTypeDockerImage, bi.Sources[0].Type)
|
||||
assert.True(t, strings.HasPrefix(bi.Sources[0].Ref, "docker.io/library/alpine"))
|
||||
assert.NotEmpty(t, bi.Sources[0].Pin)
|
||||
assert.Equal(t, binfotypes.SourceTypeDockerImage, bi.Sources[1].Type)
|
||||
assert.Equal(t, "docker.io/library/busybox:latest", bi.Sources[1].Ref)
|
||||
assert.NotEmpty(t, 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, "alpine", depsrc[0].Ref)
|
||||
assert.NotEmpty(t, depsrc[0].Pin)
|
||||
}
|
||||
|
||||
func testBuildInfoDepsMultiPlatform(t *testing.T, sb integration.Sandbox) {
|
||||
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 := tmpdir(
|
||||
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
dockerfile2 := []byte(`
|
||||
FROM base AS build
|
||||
RUN echo "foo is $FOO" > /foo
|
||||
FROM busybox
|
||||
COPY --from=build /foo /out /
|
||||
`)
|
||||
|
||||
dir2, err := tmpdir(
|
||||
fstest.CreateFile("Dockerfile", dockerfile2, 0600),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
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": builder.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, err := ioutil.TempDir("", "buildkit")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(destDir)
|
||||
|
||||
res, err := c.Build(ctx, client.SolveOpt{
|
||||
LocalDirs: map[string]string{
|
||||
builder.DefaultLocalNameDockerfile: dir,
|
||||
builder.DefaultLocalNameContext: dir,
|
||||
builder.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.Equal(t, 2, len(bi.Sources))
|
||||
assert.Equal(t, binfotypes.SourceTypeDockerImage, bi.Sources[0].Type)
|
||||
assert.True(t, strings.HasPrefix(bi.Sources[0].Ref, "docker.io/library/alpine"))
|
||||
assert.NotEmpty(t, bi.Sources[0].Pin)
|
||||
assert.Equal(t, binfotypes.SourceTypeDockerImage, bi.Sources[1].Type)
|
||||
assert.Equal(t, "docker.io/library/busybox:latest", bi.Sources[1].Ref)
|
||||
assert.NotEmpty(t, 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, "alpine", depsrc[0].Ref)
|
||||
assert.NotEmpty(t, depsrc[0].Pin)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
|
|||
res.Metadata = make(map[string][]byte)
|
||||
}
|
||||
if r := res.Ref; r != nil {
|
||||
dtbi, err := buildinfo.Encode(ctx, res.Metadata[exptypes.ExporterBuildInfo], r.BuildSources())
|
||||
dtbi, err := buildinfo.Encode(ctx, res.Metadata, exptypes.ExporterBuildInfo, r.BuildSources())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
|
|||
if r == nil {
|
||||
continue
|
||||
}
|
||||
dtbi, err := buildinfo.Encode(ctx, res.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, k)], r.BuildSources())
|
||||
dtbi, err := buildinfo.Encode(ctx, res.Metadata, fmt.Sprintf("%s/%s", exptypes.ExporterBuildInfo, k), r.BuildSources())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
"github.com/moby/buildkit/source"
|
||||
binfotypes "github.com/moby/buildkit/util/buildinfo/types"
|
||||
"github.com/moby/buildkit/util/urlutil"
|
||||
|
@ -25,22 +26,28 @@ func Decode(enc string) (bi binfotypes.BuildInfo, _ error) {
|
|||
}
|
||||
|
||||
// Encode encodes build info.
|
||||
func Encode(ctx context.Context, buildInfo []byte, buildSources map[string]string) ([]byte, error) {
|
||||
func Encode(ctx context.Context, metadata map[string][]byte, key string, buildSources map[string]string) ([]byte, error) {
|
||||
var bi binfotypes.BuildInfo
|
||||
if buildInfo != nil {
|
||||
if err := json.Unmarshal(buildInfo, &bi); err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
msources, err := mergeSources(ctx, buildSources, bi.Sources)
|
||||
if err != nil {
|
||||
if deps, err := decodeDeps(key, bi.Attrs); err == nil {
|
||||
bi.Deps = reduceMapBuildInfo(deps, bi.Deps)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(binfotypes.BuildInfo{
|
||||
Frontend: bi.Frontend,
|
||||
Attrs: filterAttrs(bi.Attrs),
|
||||
Sources: msources,
|
||||
})
|
||||
if sources, err := mergeSources(ctx, buildSources, bi.Sources); err == nil {
|
||||
bi.Sources = sources
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
bi.Attrs = filterAttrs(key, bi.Attrs)
|
||||
return json.Marshal(bi)
|
||||
}
|
||||
|
||||
// mergeSources combines and fixes build sources from frontend sources.
|
||||
|
@ -136,6 +143,60 @@ func mergeSources(ctx context.Context, buildSources map[string]string, frontendS
|
|||
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
|
||||
skey := strings.SplitN(key, "/", 2)
|
||||
if 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, only decode dependencies for that platform
|
||||
if platform != "" && !strings.HasSuffix(k, "::"+platform) {
|
||||
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)
|
||||
depkey = kl[1]
|
||||
if platform != "" {
|
||||
depkey = strings.TrimSuffix(depkey, "::"+platform)
|
||||
}
|
||||
|
||||
res[depkey] = bi
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// FormatOpts holds build info format options.
|
||||
type FormatOpts struct {
|
||||
RemoveAttrs bool
|
||||
|
@ -178,22 +239,43 @@ var knownAttrs = []string{
|
|||
|
||||
// filterAttrs filters frontent opt by picking only those that
|
||||
// could effectively change the build result.
|
||||
func filterAttrs(attrs map[string]*string) map[string]*string {
|
||||
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
|
||||
// control args are filtered out
|
||||
if isControlArg(k) {
|
||||
continue
|
||||
}
|
||||
// Always include args and labels
|
||||
// always include
|
||||
if strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") {
|
||||
filtered[k] = v
|
||||
continue
|
||||
}
|
||||
// Filter only for known attributes
|
||||
// input context key and value has to be cleaned up
|
||||
// before being included
|
||||
if strings.HasPrefix(k, "context:") {
|
||||
if platform != "" {
|
||||
// if platform is defined, only include the relevant platform
|
||||
if !strings.HasSuffix(k, "::"+platform) {
|
||||
continue
|
||||
}
|
||||
ctxival := strings.TrimSuffix(*v, "::"+platform)
|
||||
filtered[strings.TrimSuffix(k, "::"+platform)] = &ctxival
|
||||
continue
|
||||
}
|
||||
filtered[k] = v
|
||||
continue
|
||||
}
|
||||
// filter only for known attributes
|
||||
for _, knownAttr := range knownAttrs {
|
||||
if knownAttr == k {
|
||||
filtered[k] = v
|
||||
|
@ -227,11 +309,11 @@ func isControlArg(attrKey string) bool {
|
|||
// GetMetadata returns buildinfo metadata for the specified key. If the key
|
||||
// is already there, result will be merged.
|
||||
func GetMetadata(metadata map[string][]byte, key string, reqFrontend string, reqAttrs map[string]string) ([]byte, error) {
|
||||
var (
|
||||
dtbi []byte
|
||||
err error
|
||||
)
|
||||
if v, ok := metadata[key]; ok {
|
||||
if metadata == nil {
|
||||
metadata = make(map[string][]byte)
|
||||
}
|
||||
var dtbi []byte
|
||||
if v, ok := metadata[key]; ok && v != nil {
|
||||
var mbi binfotypes.BuildInfo
|
||||
if errm := json.Unmarshal(v, &mbi); errm != nil {
|
||||
return nil, errors.Wrapf(errm, "failed to unmarshal build info for %q", key)
|
||||
|
@ -239,15 +321,26 @@ func GetMetadata(metadata map[string][]byte, key string, reqFrontend string, req
|
|||
if reqFrontend != "" {
|
||||
mbi.Frontend = reqFrontend
|
||||
}
|
||||
mbi.Attrs = convertMap(reduceMap(reqAttrs, mbi.Attrs))
|
||||
if deps, err := decodeDeps(key, convertMap(reduceMapString(reqAttrs, mbi.Attrs))); err == nil {
|
||||
mbi.Deps = reduceMapBuildInfo(deps, mbi.Deps)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
mbi.Attrs = filterAttrs(key, convertMap(reduceMapString(reqAttrs, mbi.Attrs)))
|
||||
var err error
|
||||
dtbi, err = json.Marshal(mbi)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal build info for %q", key)
|
||||
}
|
||||
} else {
|
||||
deps, err := decodeDeps(key, convertMap(reqAttrs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dtbi, err = json.Marshal(binfotypes.BuildInfo{
|
||||
Frontend: reqFrontend,
|
||||
Attrs: convertMap(reqAttrs),
|
||||
Attrs: filterAttrs(key, convertMap(reqAttrs)),
|
||||
Deps: deps,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal build info for %q", key)
|
||||
|
@ -256,7 +349,26 @@ func GetMetadata(metadata map[string][]byte, key string, reqFrontend string, req
|
|||
return dtbi, nil
|
||||
}
|
||||
|
||||
func reduceMap(m1 map[string]string, m2 map[string]*string) map[string]string {
|
||||
// FromImageConfig returns build info from image config.
|
||||
func FromImageConfig(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
|
||||
}
|
||||
bi, err := Decode(config.BuildInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode build info from image config")
|
||||
}
|
||||
return &bi, nil
|
||||
}
|
||||
|
||||
func reduceMapString(m1 map[string]string, m2 map[string]*string) map[string]string {
|
||||
if m1 == nil && m2 == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -271,6 +383,19 @@ func reduceMap(m1 map[string]string, m2 map[string]*string) map[string]string {
|
|||
return m1
|
||||
}
|
||||
|
||||
func reduceMapBuildInfo(m1 map[string]binfotypes.BuildInfo, m2 map[string]binfotypes.BuildInfo) map[string]binfotypes.BuildInfo {
|
||||
if m1 == nil && m2 == nil {
|
||||
return nil
|
||||
}
|
||||
if m1 == nil {
|
||||
m1 = map[string]binfotypes.BuildInfo{}
|
||||
}
|
||||
for k, v := range m2 {
|
||||
m1[k] = v
|
||||
}
|
||||
return m1
|
||||
}
|
||||
|
||||
func convertMap(m map[string]string) map[string]*string {
|
||||
res := make(map[string]*string)
|
||||
for k, v := range m {
|
||||
|
|
|
@ -5,6 +5,7 @@ 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"
|
||||
|
@ -91,6 +92,131 @@ func TestMergeSources(t *testing.T) {
|
|||
}, 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\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJidWlsZC1hcmc6YmFyIjoiZm9vIiwiYnVpbGQtYXJnOmZvbyI6ImJhciIsImZpbGVuYW1lIjoiYmFzZWFwcC5Eb2NrZXJmaWxlIn0sInNvdXJjZXMiOlt7InR5cGUiOiJkb2NrZXItaW1hZ2UiLCJyZWYiOiJidXN5Ym94IiwiYWxpYXMiOiJkb2NrZXIuaW8vbGlicmFyeS9idXN5Ym94QHNoYTI1NjphZmNjN2YxYWMxYjQ5ZGIzMTdhNzE5NmM5MDJlNjFjNmMzYzQ2MDdkNjM1OTllZTFhODJkNzAyZDI0OWEwY2NiIiwicGluIjoic2hhMjU2OmFmY2M3ZjFhYzFiNDlkYjMxN2E3MTk2YzkwMmU2MWM2YzNjNDYwN2Q2MzU5OWVlMWE4MmQ3MDJkMjQ5YTBjY2IifV19\",\"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: "busybox",
|
||||
Alias: "docker.io/library/busybox@sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb",
|
||||
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\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiYWxwaW5lIiwiYWxpYXMiOiJkb2NrZXIuaW8vbGlicmFyeS9hbHBpbmVAc2hhMjU2OmU3ZDg4ZGU3M2RiM2QzZmQ5YjJkNjNhYTdmNDQ3YTEwZmQwMjIwYjdjYmYzOTgwM2M4MDNmMmFmOWJhMjU2YjMiLCJwaW4iOiJzaGEyNTY6ZTdkODhkZTczZGIzZDNmZDliMmQ2M2FhN2Y0NDdhMTBmZDAyMjBiN2NiZjM5ODAzYzgwM2YyYWY5YmEyNTZiMyJ9XX0=\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo4ZDNhYzM0ODk5OTY0MjNmNTNkNjA4N2M4MTE4MDAwNjI2M2I3OWYyMDZkM2ZkZWM5ZTY2ZjBlMjdjZWI4NzU5Il19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0xMS0yNFQyMDoxOTo0MC4xOTk3MDA5NDZaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjkyMzNmNmYyMjM3ZDc5NjU5YTk1MjFmN2UzOTBkZjIxN2NlYzQ5ZjFhOGFhM2ExMjE0N2JiY2ExOTU2YWNkYjkgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIxLTExLTI0VDIwOjE5OjQwLjQ4MzM2NzU0NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCIvYmluL3NoXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9LHsiY3JlYXRlZF9ieSI6IkFSRyBUQVJHRVRBUkNIIiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAiLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkX2J5IjoiRU5WIEZPTz1iYXItYW1kNjQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJSVU4gfDEgVEFSR0VUQVJDSD1hbWQ2NCAvYmluL3NoIC1jIGVjaG8gXCJmb28gJFRBUkdFVEFSQ0hcIiBcdTAwM2UgL291dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJGT089YmFyLWFtZDY0Il0sIkNtZCI6WyIvYmluL3NoIl0sIk9uQnVpbGQiOm51bGx9fQ==\"}"),
|
||||
"input-metadata:base::linux/arm64": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiYWxwaW5lIiwiYWxpYXMiOiJkb2NrZXIuaW8vbGlicmFyeS9hbHBpbmVAc2hhMjU2OmU3ZDg4ZGU3M2RiM2QzZmQ5YjJkNjNhYTdmNDQ3YTEwZmQwMjIwYjdjYmYzOTgwM2M4MDNmMmFmOWJhMjU2YjMiLCJwaW4iOiJzaGEyNTY6ZTdkODhkZTczZGIzZDNmZDliMmQ2M2FhN2Y0NDdhMTBmZDAyMjBiN2NiZjM5ODAzYzgwM2YyYWY5YmEyNTZiMyJ9XX0=\",\"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: "alpine",
|
||||
Alias: "docker.io/library/alpine@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3",
|
||||
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 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\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiYWxwaW5lIiwiYWxpYXMiOiJkb2NrZXIuaW8vbGlicmFyeS9hbHBpbmVAc2hhMjU2OmU3ZDg4ZGU3M2RiM2QzZmQ5YjJkNjNhYTdmNDQ3YTEwZmQwMjIwYjdjYmYzOTgwM2M4MDNmMmFmOWJhMjU2YjMiLCJwaW4iOiJzaGEyNTY6ZTdkODhkZTczZGIzZDNmZDliMmQ2M2FhN2Y0NDdhMTBmZDAyMjBiN2NiZjM5ODAzYzgwM2YyYWY5YmEyNTZiMyJ9XX0=\",\"containerimage.config\":\"eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo4ZDNhYzM0ODk5OTY0MjNmNTNkNjA4N2M4MTE4MDAwNjI2M2I3OWYyMDZkM2ZkZWM5ZTY2ZjBlMjdjZWI4NzU5Il19LCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0xMS0yNFQyMDoxOTo0MC4xOTk3MDA5NDZaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOjkyMzNmNmYyMjM3ZDc5NjU5YTk1MjFmN2UzOTBkZjIxN2NlYzQ5ZjFhOGFhM2ExMjE0N2JiY2ExOTU2YWNkYjkgaW4gLyAifSx7ImNyZWF0ZWQiOiIyMDIxLTExLTI0VDIwOjE5OjQwLjQ4MzM2NzU0NloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgIENNRCBbXCIvYmluL3NoXCJdIiwiZW1wdHlfbGF5ZXIiOnRydWV9LHsiY3JlYXRlZF9ieSI6IkFSRyBUQVJHRVRBUkNIIiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAiLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkX2J5IjoiRU5WIEZPTz1iYXItYW1kNjQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCIsImVtcHR5X2xheWVyIjp0cnVlfSx7ImNyZWF0ZWRfYnkiOiJSVU4gfDEgVEFSR0VUQVJDSD1hbWQ2NCAvYmluL3NoIC1jIGVjaG8gXCJmb28gJFRBUkdFVEFSQ0hcIiBcdTAwM2UgL291dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJGT089YmFyLWFtZDY0Il0sIkNtZCI6WyIvYmluL3NoIl0sIk9uQnVpbGQiOm51bGx9fQ==\"}"),
|
||||
"input-metadata:base::linux/arm64": stringPtr("{\"containerimage.buildinfo\":\"eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJzb3VyY2VzIjpbeyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiYWxwaW5lIiwiYWxpYXMiOiJkb2NrZXIuaW8vbGlicmFyeS9hbHBpbmVAc2hhMjU2OmU3ZDg4ZGU3M2RiM2QzZmQ5YjJkNjNhYTdmNDQ3YTEwZmQwMjIwYjdjYmYzOTgwM2M4MDNmMmFmOWJhMjU2YjMiLCJwaW4iOiJzaGEyNTY6ZTdkODhkZTczZGIzZDNmZDliMmQ2M2FhN2Y0NDdhMTBmZDAyMjBiN2NiZjM5ODAzYzgwM2YyYWY5YmEyNTZiMyJ9XX0=\",\"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",
|
||||
|
@ -152,7 +278,7 @@ func TestFormat(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestReduceMap(t *testing.T) {
|
||||
func TestReduceMapString(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m1 map[string]*string
|
||||
|
@ -193,7 +319,7 @@ func TestReduceMap(t *testing.T) {
|
|||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.expected, reduceMap(tt.m2, tt.m1))
|
||||
require.Equal(t, tt.expected, reduceMapString(tt.m2, tt.m1))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ const ImageConfigField = "moby.buildkit.buildinfo.v1"
|
|||
// ImageConfig defines the structure of build dependencies
|
||||
// inside image config.
|
||||
type ImageConfig struct {
|
||||
BuildInfo []byte `json:"moby.buildkit.buildinfo.v1,omitempty"`
|
||||
BuildInfo string `json:"moby.buildkit.buildinfo.v1,omitempty"`
|
||||
}
|
||||
|
||||
// BuildInfo defines the main structure added to image config as
|
||||
|
@ -23,6 +23,8 @@ type BuildInfo struct {
|
|||
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.
|
||||
|
|
Loading…
Reference in New Issue