From 812f319a578ebec5987ee261410c149cbf841ba6 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Sun, 28 Jan 2024 19:41:41 +0000 Subject: [PATCH 1/2] Add containerd connection info to info endpoint (API v1.46) This will be used in the next commit to test that changes are propagated to the containerd store. It is also just generally useful for debugging purposes. - docs/api: update version history - daemon: add fillContainerdInfo utility - api: update swagger file with new types Signed-off-by: Brian Goff Signed-off-by: Bjorn Neergaard Signed-off-by: Sebastiaan van Stijn --- api/server/router/system/system_routes.go | 4 ++ api/swagger.yaml | 52 +++++++++++++++++++++++ api/types/system/info.go | 39 +++++++++++++++++ daemon/info.go | 18 ++++++++ docs/api/version-history.md | 3 ++ 5 files changed, 116 insertions(+) diff --git a/api/server/router/system/system_routes.go b/api/server/router/system/system_routes.go index fca29cb07e..3e98d76e7f 100644 --- a/api/server/router/system/system_routes.go +++ b/api/server/router/system/system_routes.go @@ -97,6 +97,10 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht info.Runtimes[k] = system.RuntimeWithStatus{Runtime: rt.Runtime} } } + if versions.LessThan(version, "1.46") { + // Containerd field introduced in API v1.46. + info.Containerd = nil + } if versions.GreaterThanOrEqualTo(version, "1.42") { info.KernelMemory = false } diff --git a/api/swagger.yaml b/api/swagger.yaml index 2f33afeff9..2392b7b691 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -5824,6 +5824,58 @@ definitions: example: - "/etc/cdi" - "/var/run/cdi" + Containerd: + $ref: "#/definitions/ContainerdInfo" + x-nullable: true + + ContainerdInfo: + description: | + Information for connecting to the containerd instance that is used by the daemon. + This is included for debugging purposes only. + type: "object" + properties: + Address: + description: "The address of the containerd socket." + type: "string" + example: "/run/containerd/containerd.sock" + Namespaces: + description: | + The namespaces that the daemon uses for running containers and + plugins in containerd. These namespaces can be configured in the + daemon configuration, and are considered to be used exclusively + by the daemon, Tampering with the containerd instance may cause + unexpected behavior. + + As these namespaces are considered to be exclusively accessed + by the daemon, it is not recommended to change these values, + or to change them to a value that is used by other systems, + such as cri-containerd. + type: "object" + properties: + Containers: + description: | + The default containerd namespace used for containers managed + by the daemon. + + The default namespace for containers is "moby", but will be + suffixed with the `.` of the remapped `root` if + user-namespaces are enabled and the containerd image-store + is used. + type: "string" + default: "moby" + example: "moby" + Plugins: + description: | + The default containerd namespace used for plugins managed by + the daemon. + + The default namespace for plugins is "plugins.moby", but will be + suffixed with the `.` of the remapped `root` if + user-namespaces are enabled and the containerd image-store + is used. + type: "string" + default: "plugins.moby" + example: "plugins.moby" # PluginsInfo is a temp struct holding Plugins name # registered with docker daemon. It is used by Info struct diff --git a/api/types/system/info.go b/api/types/system/info.go index 89d4a0098e..6791cf3284 100644 --- a/api/types/system/info.go +++ b/api/types/system/info.go @@ -75,6 +75,8 @@ type Info struct { DefaultAddressPools []NetworkAddressPool `json:",omitempty"` CDISpecDirs []string + Containerd *ContainerdInfo `json:",omitempty"` + // Legacy API fields for older API versions. legacyFields @@ -85,6 +87,43 @@ type Info struct { Warnings []string } +// ContainerdInfo holds information about the containerd instance used by the daemon. +type ContainerdInfo struct { + // Address is the path to the containerd socket. + Address string `json:",omitempty"` + // Namespaces is the containerd namespaces used by the daemon. + Namespaces ContainerdNamespaces +} + +// ContainerdNamespaces reflects the containerd namespaces used by the daemon. +// +// These namespaces can be configured in the daemon configuration, and are +// considered to be used exclusively by the daemon, +// +// As these namespaces are considered to be exclusively accessed +// by the daemon, it is not recommended to change these values, +// or to change them to a value that is used by other systems, +// such as cri-containerd. +type ContainerdNamespaces struct { + // Containers holds the default containerd namespace used for + // containers managed by the daemon. + // + // The default namespace for containers is "moby", but will be + // suffixed with the `.` of the remapped `root` if + // user-namespaces are enabled and the containerd image-store + // is used. + Containers string + + // Plugins holds the default containerd namespace used for + // plugins managed by the daemon. + // + // The default namespace for plugins is "moby", but will be + // suffixed with the `.` of the remapped `root` if + // user-namespaces are enabled and the containerd image-store + // is used. + Plugins string +} + type legacyFields struct { ExecutionDriver string `json:",omitempty"` // Deprecated: deprecated since API v1.25, but returned for older versions. } diff --git a/daemon/info.go b/daemon/info.go index 6c9d4844b7..6669d7644e 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -82,7 +82,9 @@ func (daemon *Daemon) SystemInfo(ctx context.Context) (*system.Info, error) { daemon.fillContainerStates(v) daemon.fillDebugInfo(ctx, v) + daemon.fillContainerdInfo(v, &cfg.Config) daemon.fillAPIInfo(v, &cfg.Config) + // Retrieve platform specific info if err := daemon.fillPlatformInfo(ctx, v, sysInfo, cfg); err != nil { return nil, err @@ -227,6 +229,22 @@ func (daemon *Daemon) fillDebugInfo(ctx context.Context, v *system.Info) { v.NFd = fileutils.GetTotalUsedFds(ctx) v.NGoroutines = runtime.NumGoroutine() v.NEventsListener = daemon.EventsService.SubscribersCount() + +} + +// fillContainerdInfo provides information about the containerd configuration +// for debugging purposes. +func (daemon *Daemon) fillContainerdInfo(v *system.Info, cfg *config.Config) { + if cfg.ContainerdAddr == "" { + return + } + v.Containerd = &system.ContainerdInfo{ + Address: cfg.ContainerdAddr, + Namespaces: system.ContainerdNamespaces{ + Containers: cfg.ContainerdNamespace, + Plugins: cfg.ContainerdPluginNamespace, + }, + } } func (daemon *Daemon) fillAPIInfo(v *system.Info, cfg *config.Config) { diff --git a/docs/api/version-history.md b/docs/api/version-history.md index d2b771bfcb..bb7dc6c97f 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -17,6 +17,9 @@ keywords: "API, Docker, rcli, REST, documentation" [Docker Engine API v1.46](https://docs.docker.com/engine/api/v1.46/) documentation +* `GET /info` now includes a `Containerd` field containing information about + the location of the containerd API socket and containerd namespaces used + by the daemon to run containers and plugins. * `POST /containers/create` field `NetworkingConfig.EndpointsConfig.DriverOpts`, and `POST /networks/{id}/connect` field `EndpointsConfig.DriverOpts`, now support label `com.docker.network.endpoint.sysctls` for setting per-interface From 2851ddc44c183a971fe1d63d7240cc20f7a20f85 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Sun, 28 Jan 2024 19:43:44 +0000 Subject: [PATCH 2/2] Add containerd image ref to created containers This populates the "Image" field on containerd containers, but only when using the containerd image store. This allows containerd clients to look up the image information. Signed-off-by: Brian Goff Signed-off-by: Bjorn Neergaard Signed-off-by: Sebastiaan van Stijn --- daemon/containerd/image.go | 5 ++++ daemon/start.go | 20 ++++++++++++++- integration/container/create_test.go | 38 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/daemon/containerd/image.go b/daemon/containerd/image.go index 70a72f674b..81ae4711c8 100644 --- a/daemon/containerd/image.go +++ b/daemon/containerd/image.go @@ -273,6 +273,11 @@ func (i *ImageService) resolveDescriptor(ctx context.Context, refOrID string) (o return img.Target, nil } +// ResolveImage looks up an image by reference or identifier in the image store. +func (i *ImageService) ResolveImage(ctx context.Context, refOrID string) (containerdimages.Image, error) { + return i.resolveImage(ctx, refOrID) +} + func (i *ImageService) resolveImage(ctx context.Context, refOrID string) (containerdimages.Image, error) { parsed, err := reference.ParseAnyReference(refOrID) if err != nil { diff --git a/daemon/start.go b/daemon/start.go index 9eb212efb1..027f08a6dc 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -4,10 +4,13 @@ import ( "context" "time" + "github.com/containerd/containerd" + "github.com/containerd/containerd/containers" "github.com/containerd/log" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/events" "github.com/docker/docker/container" + mobyc8dstore "github.com/docker/docker/daemon/containerd" "github.com/docker/docker/errdefs" "github.com/docker/docker/libcontainerd" "github.com/pkg/errors" @@ -173,7 +176,22 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore return err } - ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions) + ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions, func(ctx context.Context, client *containerd.Client, c *containers.Container) error { + // Only set the image if we are using containerd for image storage. + // This is for metadata purposes only. + // Other lower-level components may make use of this information. + is, ok := daemon.imageService.(*mobyc8dstore.ImageService) + if !ok { + return nil + } + img, err := is.ResolveImage(ctx, container.Config.Image) + if err != nil { + log.G(ctx).WithError(err).WithField("container", container.ID).Warn("Failed to resolve containerd image reference") + return nil + } + c.Image = img.Name + return nil + }) if err != nil { return setExitCodeFromError(container.SetExitCode, err) } diff --git a/integration/container/create_test.go b/integration/container/create_test.go index a4541d12a1..bed416e3fd 100644 --- a/integration/container/create_test.go +++ b/integration/container/create_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/containerd/containerd" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/versions" @@ -668,3 +669,40 @@ func TestCreateWithCustomMACs(t *testing.T) { assert.Equal(t, mac, "02:32:1c:23:00:04") } } + +// Tests that when using containerd backed storage the containerd container has the image referenced stored. +func TestContainerdContainerImageInfo(t *testing.T) { + skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.46"), "requires API v1.46") + + ctx := setupTest(t) + + apiClient := testEnv.APIClient() + defer apiClient.Close() + + info, err := apiClient.Info(ctx) + assert.NilError(t, err) + + skip.If(t, info.Containerd == nil, "requires containerd") + + // Currently a containerd container is only created when the container is started. + // So start the container and then inspect the containerd container to verify the image info. + id := ctr.Run(ctx, t, apiClient, func(cfg *ctr.TestContainerConfig) { + // busybox is the default (as of this writing) used by the test client, but lets be explicit here. + cfg.Config.Image = "busybox" + }) + defer apiClient.ContainerRemove(ctx, id, container.RemoveOptions{Force: true}) + + client, err := containerd.New(info.Containerd.Address, containerd.WithDefaultNamespace(info.Containerd.Namespaces.Containers)) + assert.NilError(t, err) + defer client.Close() + + ctr, err := client.ContainerService().Get(ctx, id) + assert.NilError(t, err) + + if testEnv.UsingSnapshotter() { + assert.Equal(t, ctr.Image, "docker.io/library/busybox:latest") + } else { + // This field is not set when not using contianerd backed storage. + assert.Equal(t, ctr.Image, "") + } +}