diff --git a/frontend/dockerfile/builder/build.go b/frontend/dockerfile/builder/build.go index 60d30c58..fd9adb47 100644 --- a/frontend/dockerfile/builder/build.go +++ b/frontend/dockerfile/builder/build.go @@ -24,6 +24,7 @@ const ( defaultDockerfileName = "Dockerfile" dockerignoreFilename = ".dockerignore" buildArgPrefix = "build-arg:" + labelPrefix = "label:" gitPrefix = "git://" ) @@ -103,7 +104,8 @@ func Build(ctx context.Context, c client.Client) error { st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{ Target: opts[keyTarget], MetaResolver: c, - BuildArgs: filterBuildArgs(opts), + BuildArgs: filter(opts, buildArgPrefix), + Labels: filter(opts, labelPrefix), SessionID: c.SessionID(), BuildContext: buildContext, Excludes: excludes, @@ -132,11 +134,11 @@ func Build(ctx context.Context, c client.Client) error { return nil } -func filterBuildArgs(opt map[string]string) map[string]string { +func filter(opt map[string]string, key string) map[string]string { m := map[string]string{} for k, v := range opt { - if strings.HasPrefix(k, buildArgPrefix) { - m[strings.TrimPrefix(k, buildArgPrefix)] = v + if strings.HasPrefix(k, key) { + m[strings.TrimPrefix(k, key)] = v } } return m diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index 09754a4d..7b177125 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -36,6 +36,7 @@ type ConvertOpt struct { Target string MetaResolver llb.ImageMetaResolver BuildArgs map[string]string + Labels map[string]string SessionID string BuildContext *llb.State Excludes []string @@ -226,6 +227,10 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, } } + for k, v := range opt.Labels { + target.image.Config.Labels[k] = v + } + opts := []llb.LocalOption{ llb.SessionID(opt.SessionID), llb.ExcludePatterns(opt.Excludes), diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 6b8901e2..6cb9aff3 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -51,6 +51,7 @@ func TestIntegration(t *testing.T) { testMultiStageImplicitFrom, testCopyVarSubstitution, testMultiStageCaseInsensitive, + testLabels, }) } @@ -1195,6 +1196,81 @@ COPY --from=stage1 baz bax require.Contains(t, string(dt), "foo-contents") } +func testLabels(t *testing.T, sb integration.Sandbox) { + t.Parallel() + + dockerfile := []byte(` +FROM scratch +LABEL foo=bar +`) + dir, err := tmpdir( + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + require.NoError(t, err) + defer os.RemoveAll(dir) + + c, err := client.New(sb.Address()) + require.NoError(t, err) + defer c.Close() + + destDir, err := ioutil.TempDir("", "buildkit") + require.NoError(t, err) + defer os.RemoveAll(destDir) + + target := "example.com/moby/dockerfilelabels:test" + err = c.Solve(context.TODO(), nil, client.SolveOpt{ + Frontend: "dockerfile.v0", + FrontendAttrs: map[string]string{ + "label:bar": "baz", + }, + Exporter: client.ExporterImage, + ExporterAttrs: map[string]string{ + "name": target, + }, + LocalDirs: map[string]string{ + builder.LocalNameDockerfile: dir, + builder.LocalNameContext: dir, + }, + }, nil) + require.NoError(t, err) + + var cdAddress string + if cd, ok := sb.(interface { + ContainerdAddress() string + }); !ok { + t.Skip("only for containerd worker") + } else { + cdAddress = cd.ContainerdAddress() + } + + client, err := containerd.New(cdAddress) + require.NoError(t, err) + defer client.Close() + + ctx := namespaces.WithNamespace(context.Background(), "buildkit") + + img, err := client.ImageService().Get(ctx, target) + require.NoError(t, err) + + desc, err := img.Config(ctx, client.ContentStore(), platforms.Default()) + require.NoError(t, err) + + dt, err := content.ReadBlob(ctx, client.ContentStore(), desc.Digest) + require.NoError(t, err) + + var ociimg ocispec.Image + err = json.Unmarshal(dt, &ociimg) + require.NoError(t, err) + + v, ok := ociimg.Config.Labels["foo"] + require.True(t, ok) + require.Equal(t, v, "bar") + + v, ok = ociimg.Config.Labels["bar"] + require.True(t, ok) + require.Equal(t, v, "baz") +} + func tmpdir(appliers ...fstest.Applier) (string, error) { tmpdir, err := ioutil.TempDir("", "buildkit-dockerfile") if err != nil {