Skip to content

Commit

Permalink
daemon: add annotations to container HostConfig
Browse files Browse the repository at this point in the history
Allow clients to set annotations on a container which will applied to
the container's OCI spec.

Signed-off-by: Cory Snider <csnider@mirantis.com>
  • Loading branch information
corhere committed Feb 23, 2023
1 parent 50d7164 commit 0ffaa6c
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 11 deletions.
5 changes: 5 additions & 0 deletions api/server/router/container/container_routes.go
Expand Up @@ -563,6 +563,11 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
hostConfig.ConsoleSize = [2]uint{0, 0}
}

if hostConfig != nil && versions.LessThan(version, "1.43") {
// Ignore Annotations because it was added in API v1.43.
hostConfig.Annotations = nil
}

var platform *specs.Platform
if versions.GreaterThanOrEqualTo(version, "1.41") {
if v := r.Form.Get("platform"); v != "" {
Expand Down
21 changes: 11 additions & 10 deletions api/types/container/hostconfig.go
Expand Up @@ -377,16 +377,17 @@ type UpdateConfig struct {
// Portable information *should* appear in Config.
type HostConfig struct {
// Applicable to all platforms
Binds []string // List of volume bindings for this container
ContainerIDFile string // File (path) where the containerId is written
LogConfig LogConfig // Configuration of the logs for this container
NetworkMode NetworkMode // Network mode to use for the container
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
RestartPolicy RestartPolicy // Restart policy to be used for the container
AutoRemove bool // Automatically remove container when it exits
VolumeDriver string // Name of the volume driver used to mount volumes
VolumesFrom []string // List of volumes to take from other container
ConsoleSize [2]uint // Initial console size (height,width)
Binds []string // List of volume bindings for this container
ContainerIDFile string // File (path) where the containerId is written
LogConfig LogConfig // Configuration of the logs for this container
NetworkMode NetworkMode // Network mode to use for the container
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
RestartPolicy RestartPolicy // Restart policy to be used for the container
AutoRemove bool // Automatically remove container when it exits
VolumeDriver string // Name of the volume driver used to mount volumes
VolumesFrom []string // List of volumes to take from other container
ConsoleSize [2]uint // Initial console size (height,width)
Annotations map[string]string `json:",omitempty"` // Arbitrary non-identifying metadata attached to container and provided to the runtime

// Applicable to UNIX platforms
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
Expand Down
5 changes: 5 additions & 0 deletions daemon/container.go
Expand Up @@ -305,6 +305,11 @@ func validateHostConfig(hostConfig *containertypes.HostConfig) error {
if !hostConfig.Isolation.IsValid() {
return errors.Errorf("invalid isolation '%s' on %s", hostConfig.Isolation, runtime.GOOS)
}
for k := range hostConfig.Annotations {
if k == "" {
return errors.Errorf("invalid Annotations: the empty string is not permitted as an annotation key")
}
}
return nil
}

Expand Down
1 change: 1 addition & 0 deletions daemon/oci_linux.go
Expand Up @@ -1026,6 +1026,7 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (r
WithApparmor(c),
WithSelinux(c),
WithOOMScore(&c.HostConfig.OomScoreAdj),
coci.WithAnnotations(c.HostConfig.Annotations),
)
if daemon.UsesSnapshotter() {
s.Root = &specs.Root{
Expand Down
5 changes: 5 additions & 0 deletions daemon/oci_windows.go
Expand Up @@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"

coci "github.com/containerd/containerd/oci"
containertypes "github.com/docker/docker/api/types/container"
imagetypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/container"
Expand Down Expand Up @@ -37,6 +38,10 @@ func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (*

s := oci.DefaultSpec()

if err := coci.WithAnnotations(c.HostConfig.Annotations)(ctx, nil, nil, &s); err != nil {
return nil, err
}

linkedEnv, err := daemon.setupLinkedContainers(c)
if err != nil {
return nil, err
Expand Down
7 changes: 7 additions & 0 deletions docs/api/v1.43.yaml
Expand Up @@ -976,6 +976,13 @@ definitions:
items:
type: "integer"
minimum: 0
Annotations:
type: "object"
description: |
Arbitrary non-identifying metadata attached to container and
provided to the runtime when the container is started.
additionalProperties:
type: "string"

# Applicable to UNIX platforms
CapAdd:
Expand Down
4 changes: 3 additions & 1 deletion docs/api/version-history.md
Expand Up @@ -17,7 +17,9 @@ keywords: "API, Docker, rcli, REST, documentation"

[Docker Engine API v1.43](https://docs.docker.com/engine/api/v1.43/) documentation

* TODO add API changes for v1.43 here when they arrive.
* `POST /containers/create` now accepts `Annotations` as part of `HostConfig`.
Can be used to attach arbitrary metadata to the container, which will also be
passed to the runtime when the container is started.

## v1.42 API changes

Expand Down
5 changes: 5 additions & 0 deletions integration/container/create_test.go
Expand Up @@ -561,6 +561,11 @@ func TestCreateInvalidHostConfig(t *testing.T) {
hc: containertypes.HostConfig{UTSMode: "invalid"},
expectedErr: "Error response from daemon: invalid UTS mode: invalid",
},
{
doc: "invalid Annotations",
hc: containertypes.HostConfig{Annotations: map[string]string{"": "a"}},
expectedErr: "Error response from daemon: invalid Annotations: the empty string is not permitted as an annotation key",
},
}

for _, tc := range testCases {
Expand Down
24 changes: 24 additions & 0 deletions integration/container/inspect_test.go
Expand Up @@ -47,3 +47,27 @@ func TestInspectCpusetInConfigPre120(t *testing.T) {
_, ok = cfg["Cpuset"]
assert.Check(t, is.Equal(true, ok), "API version 1.19 expected to include Cpuset in 'Config'")
}

func TestInspectAnnotations(t *testing.T) {
defer setupTest(t)()
client := request.NewAPIClient(t)
ctx := context.Background()

annotations := map[string]string{
"hello": "world",
"foo": "bar",
}

name := strings.ToLower(t.Name())
id := container.Create(ctx, t, client,
container.WithName(name),
container.WithCmd("true"),
func(c *container.TestContainerConfig) {
c.HostConfig.Annotations = annotations
},
)

inspect, err := client.ContainerInspect(ctx, id)
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(inspect.HostConfig.Annotations, annotations))
}

0 comments on commit 0ffaa6c

Please sign in to comment.