From f40df3d72ba85700c737aca2cb21212d8f0caaed Mon Sep 17 00:00:00 2001 From: Phil Estes Date: Thu, 9 Sep 2021 13:17:42 -0400 Subject: [PATCH] Enable image config labels in ctr and CRI container creation Signed-off-by: Phil Estes --- cmd/ctr/commands/run/run.go | 13 ++++++++++ cmd/ctr/commands/run/run_unix.go | 6 ++++- cmd/ctr/commands/run/run_windows.go | 12 ++++++--- container_opts.go | 38 +++++++++++++++++++++++++++++ pkg/cri/server/container_create.go | 2 +- pkg/cri/server/helpers.go | 6 ++++- pkg/cri/server/helpers_test.go | 9 +++++-- pkg/cri/server/sandbox_run.go | 2 +- 8 files changed, 78 insertions(+), 10 deletions(-) diff --git a/cmd/ctr/commands/run/run.go b/cmd/ctr/commands/run/run.go index 0c8088a1c2c4..1e5191324ca1 100644 --- a/cmd/ctr/commands/run/run.go +++ b/cmd/ctr/commands/run/run.go @@ -249,3 +249,16 @@ func fullID(ctx context.Context, c containerd.Container) string { } return fmt.Sprintf("%s-%s", ns, id) } + +// buildLabel builds the labels from command line labels and the image labels +func buildLabels(cmdLabels, imageLabels map[string]string) map[string]string { + labels := make(map[string]string) + for k, v := range imageLabels { + labels[k] = v + } + // labels from the command line will override image and the initial image config labels + for k, v := range cmdLabels { + labels[k] = v + } + return labels +} diff --git a/cmd/ctr/commands/run/run_unix.go b/cmd/ctr/commands/run/run_unix.go index a92cf27589da..caf863018ba2 100644 --- a/cmd/ctr/commands/run/run_unix.go +++ b/cmd/ctr/commands/run/run_unix.go @@ -103,8 +103,8 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli spec containerd.NewContainerOpts ) - cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) if config { + cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("labels")))) opts = append(opts, oci.WithSpecFromFile(context.String("config"))) } else { var ( @@ -125,6 +125,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli return nil, err } opts = append(opts, oci.WithRootFSPath(rootfs)) + cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("labels")))) } else { snapshotter := context.String("snapshotter") var image containerd.Image @@ -151,9 +152,12 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli return nil, err } } + labels := buildLabels(commands.LabelArgs(context.StringSlice("label")), image.Labels()) opts = append(opts, oci.WithImageConfig(image)) cOpts = append(cOpts, containerd.WithImage(image), + containerd.WithImageConfigLabels(image), + containerd.WithAdditionalContainerLabels(labels), containerd.WithSnapshotter(snapshotter)) if uidmap, gidmap := context.String("uidmap"), context.String("gidmap"); uidmap != "" && gidmap != "" { uidMap, err := parseIDMapping(uidmap) diff --git a/cmd/ctr/commands/run/run_windows.go b/cmd/ctr/commands/run/run_windows.go index 1fc9904abc7c..292d60e41e1e 100644 --- a/cmd/ctr/commands/run/run_windows.go +++ b/cmd/ctr/commands/run/run_windows.go @@ -51,6 +51,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli if config { id = context.Args().First() opts = append(opts, oci.WithSpecFromFile(context.String("config"))) + cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) } else { var ( ref = context.Args().First() @@ -88,9 +89,13 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli } } opts = append(opts, oci.WithImageConfig(image)) - cOpts = append(cOpts, containerd.WithImage(image)) - cOpts = append(cOpts, containerd.WithSnapshotter(snapshotter)) - cOpts = append(cOpts, containerd.WithNewSnapshot(id, image)) + labels := buildLabels(commands.LabelArgs(context.StringSlice("label")), image.Labels()) + cOpts = append(cOpts, + containerd.WithImage(image), + containerd.WithImageConfigLabels(image), + containerd.WithSnapshotter(snapshotter), + containerd.WithNewSnapshot(id, image), + containerd.WithAdditionalContainerLabels(labels)) if len(args) > 0 { opts = append(opts, oci.WithProcessArgs(args...)) @@ -124,7 +129,6 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli } } - cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) runtime := context.String("runtime") var runtimeOpts interface{} if runtime == "io.containerd.runhcs.v1" { diff --git a/container_opts.go b/container_opts.go index 3a851fd2a034..024d6e10b676 100644 --- a/container_opts.go +++ b/container_opts.go @@ -18,14 +18,19 @@ package containerd import ( "context" + "encoding/json" + "fmt" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/oci" "github.com/containerd/containerd/snapshots" "github.com/containerd/typeurl" "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -95,6 +100,39 @@ func WithContainerLabels(labels map[string]string) NewContainerOpts { } } +// WithImageConfigLabels sets the image config labels on the container. +// The existing labels are cleared as this is expected to be the first +// operation in setting up a container's labels. Use WithAdditionalContainerLabels +// to add/overwrite the existing image config labels. +func WithImageConfigLabels(image Image) NewContainerOpts { + return func(ctx context.Context, _ *Client, c *containers.Container) error { + ic, err := image.Config(ctx) + if err != nil { + return err + } + var ( + ociimage v1.Image + config v1.ImageConfig + ) + switch ic.MediaType { + case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) + if err != nil { + return err + } + + if err := json.Unmarshal(p, &ociimage); err != nil { + return err + } + config = ociimage.Config + default: + return fmt.Errorf("unknown image config media type %s", ic.MediaType) + } + c.Labels = config.Labels + return nil + } +} + // WithAdditionalContainerLabels adds the provided labels to the container // The existing labels are preserved as long as they do not conflict with the added labels. func WithAdditionalContainerLabels(labels map[string]string) NewContainerOpts { diff --git a/pkg/cri/server/container_create.go b/pkg/cri/server/container_create.go index 7a81a302ab22..5b158d80d825 100644 --- a/pkg/cri/server/container_create.go +++ b/pkg/cri/server/container_create.go @@ -230,7 +230,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta return nil, errors.Wrap(err, "failed to get container spec opts") } - containerLabels := buildLabels(config.Labels, containerKindContainer) + containerLabels := buildLabels(config.Labels, image.ImageSpec.Config.Labels, containerKindContainer) runtimeOptions, err := getRuntimeOptions(sandboxInfo) if err != nil { diff --git a/pkg/cri/server/helpers.go b/pkg/cri/server/helpers.go index 2f1b46e401ee..64c4af6d6b3f 100644 --- a/pkg/cri/server/helpers.go +++ b/pkg/cri/server/helpers.go @@ -283,8 +283,12 @@ func filterLabel(k, v string) string { } // buildLabel builds the labels from config to be passed to containerd -func buildLabels(configLabels map[string]string, containerType string) map[string]string { +func buildLabels(configLabels, imageConfigLabels map[string]string, containerType string) map[string]string { labels := make(map[string]string) + for k, v := range imageConfigLabels { + labels[k] = v + } + // labels from the CRI request (config) will override labels in the image config for k, v := range configLabels { labels[k] = v } diff --git a/pkg/cri/server/helpers_test.go b/pkg/cri/server/helpers_test.go index 7cd9314e10c3..7a2c3478201c 100644 --- a/pkg/cri/server/helpers_test.go +++ b/pkg/cri/server/helpers_test.go @@ -118,14 +118,19 @@ func TestGetRepoDigestAndTag(t *testing.T) { } func TestBuildLabels(t *testing.T) { + imageConfigLabels := map[string]string{ + "a": "z", + "d": "y", + } configLabels := map[string]string{ "a": "b", "c": "d", } - newLabels := buildLabels(configLabels, containerKindSandbox) - assert.Len(t, newLabels, 3) + newLabels := buildLabels(configLabels, imageConfigLabels, containerKindSandbox) + assert.Len(t, newLabels, 4) assert.Equal(t, "b", newLabels["a"]) assert.Equal(t, "d", newLabels["c"]) + assert.Equal(t, "y", newLabels["d"]) assert.Equal(t, containerKindSandbox, newLabels[containerKindLabel]) newLabels["a"] = "e" diff --git a/pkg/cri/server/sandbox_run.go b/pkg/cri/server/sandbox_run.go index d8953026bf52..41c0fa2b7738 100644 --- a/pkg/cri/server/sandbox_run.go +++ b/pkg/cri/server/sandbox_run.go @@ -198,7 +198,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox return nil, errors.Wrap(err, "failed to generate sanbdox container spec options") } - sandboxLabels := buildLabels(config.Labels, containerKindSandbox) + sandboxLabels := buildLabels(config.Labels, image.ImageSpec.Config.Labels, containerKindSandbox) runtimeOpts, err := generateRuntimeOptions(ociRuntime, c.config) if err != nil {