Add CDISpecDirs to Info output

This change adds the configured CDI spec directories to the
system info output.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
Evan Lezar 2023-07-18 01:50:08 +02:00
parent bbb9255562
commit 7a59913b1a
5 changed files with 135 additions and 5 deletions

View file

@ -5301,7 +5301,25 @@ definitions:
- "WARNING: No memory limit support"
- "WARNING: bridge-nf-call-iptables is disabled"
- "WARNING: bridge-nf-call-ip6tables is disabled"
CDISpecDirs:
description: |
List of directories where (Container Device Interface) CDI
specifications are located.
These specifications define vendor-specific modifications to an OCI
runtime specification for a container being created.
An empty list indicates that CDI device injection is disabled.
Note that since using CDI device injection requires the daemon to have
experimental enabled. For non-experimental daemons an empty list will
always be returned.
type: "array"
items:
type: "string"
example:
- "/etc/cdi"
- "/var/run/cdi"
# PluginsInfo is a temp struct holding Plugins name
# registered with docker daemon. It is used by Info struct

View file

@ -73,6 +73,7 @@ type Info struct {
SecurityOptions []string
ProductLicense string `json:",omitempty"`
DefaultAddressPools []NetworkAddressPool `json:",omitempty"`
CDISpecDirs []string
// Legacy API fields for older API versions.
legacyFields

View file

@ -62,6 +62,7 @@ func (daemon *Daemon) SystemInfo() *system.Info {
NoProxy: getConfigOrEnv(cfg.NoProxy, "NO_PROXY", "no_proxy"),
LiveRestoreEnabled: cfg.LiveRestoreEnabled,
Isolation: daemon.defaultIsolation,
CDISpecDirs: promoteNil(cfg.CDISpecDirs),
}
daemon.fillContainerStates(v)
@ -309,3 +310,12 @@ func getConfigOrEnv(config string, env ...string) string {
}
return getEnvAny(env...)
}
// promoteNil converts a nil slice to an empty slice of that type.
// A non-nil slice is returned as is.
func promoteNil[S ~[]E, E any](s S) S {
if s == nil {
return S{}
}
return s
}

View file

@ -34,6 +34,10 @@ keywords: "API, Docker, rcli, REST, documentation"
`BindOptions.ReadOnlyNonRecursive` and `BindOptions.ReadOnlyForceRecursive` to customize the behavior.
* `POST /containers/create` now accepts a `HealthConfig.StartInterval` to set the
interval for health checks during the start period.
* `GET /info` now includes a `CDISpecDirs` field indicating the configured CDI
specifications directories. The use of the applied setting requires the daemon
to have expermental enabled, and for non-experimental daemons an empty list is
always returned.
## v1.43 API changes
@ -103,7 +107,7 @@ keywords: "API, Docker, rcli, REST, documentation"
a default.
This change is not versioned, and affects all API versions if the daemon has
this patch.
this patch.
* `GET /_ping` and `HEAD /_ping` now return a `Swarm` header, which allows a
client to detect if Swarm is enabled on the daemon, without having to call
additional endpoints.
@ -126,7 +130,7 @@ keywords: "API, Docker, rcli, REST, documentation"
versioned, and affects all API versions if the daemon has this patch.
* `GET /containers/{id}/attach`, `GET /exec/{id}/start`, `GET /containers/{id}/logs`
`GET /services/{id}/logs` and `GET /tasks/{id}/logs` now set Content-Type header
to `application/vnd.docker.multiplexed-stream` when a multiplexed stdout/stderr
to `application/vnd.docker.multiplexed-stream` when a multiplexed stdout/stderr
stream is sent to client, `application/vnd.docker.raw-stream` otherwise.
* `POST /volumes/create` now accepts a new `ClusterVolumeSpec` to create a cluster
volume (CNI). This option can only be used if the daemon is a Swarm manager.
@ -139,7 +143,7 @@ keywords: "API, Docker, rcli, REST, documentation"
* Volume information returned by `GET /volumes/{name}`, `GET /volumes` and
`GET /system/df` can now contain a `ClusterVolume` if the volume is a cluster
volume (requires the daemon to be a Swarm manager).
* The `Volume` type, as returned by `Added new `ClusterVolume` fields
* The `Volume` type, as returned by `Added new `ClusterVolume` fields
* Added a new `PUT /volumes{name}` endpoint to update cluster volumes (CNI).
Cluster volumes are only supported if the daemon is a Swarm manager.
* `GET /containers/{name}/attach/ws` endpoint now accepts `stdin`, `stdout` and
@ -355,7 +359,7 @@ keywords: "API, Docker, rcli, REST, documentation"
[Docker Engine API v1.36](https://docs.docker.com/engine/api/v1.36/) documentation
* `Get /events` now return `exec_die` event when an exec process terminates.
* `Get /events` now return `exec_die` event when an exec process terminates.
## v1.35 API changes
@ -563,7 +567,7 @@ keywords: "API, Docker, rcli, REST, documentation"
* `POST /services/create` and `POST /services/(id or name)/update` now accept the `TTY` parameter, which allocate a pseudo-TTY in container.
* `POST /services/create` and `POST /services/(id or name)/update` now accept the `DNSConfig` parameter, which specifies DNS related configurations in resolver configuration file (resolv.conf) through `Nameservers`, `Search`, and `Options`.
* `POST /services/create` and `POST /services/(id or name)/update` now support
`node.platform.arch` and `node.platform.os` constraints in the services
`node.platform.arch` and `node.platform.os` constraints in the services
`TaskSpec.Placement.Constraints` field.
* `GET /networks/(id or name)` now includes IP and name of all peers nodes for swarm mode overlay networks.
* `GET /plugins` list plugins.

View file

@ -3,6 +3,7 @@ package container // import "github.com/docker/docker/integration/container"
import (
"bytes"
"context"
"encoding/json"
"io"
"os"
"path/filepath"
@ -62,3 +63,99 @@ func TestCreateWithCDIDevices(t *testing.T) {
outlines := strings.Split(actualStdout.String(), "\n")
assert.Assert(t, is.Contains(outlines, "FOO=injected"))
}
func TestCDISpecDirsAreInSystemInfo(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows") // d.Start fails on Windows with `protocol not available`
// TODO: This restriction can be relaxed with https://github.com/moby/moby/pull/46158
skip.If(t, testEnv.IsRootless, "the t.TempDir test creates a folder with incorrect permissions for rootless")
testCases := []struct {
description string
config map[string]interface{}
experimental bool
specDirs []string
expectedInfoCDISpecDirs []string
}{
{
description: "experimental no spec dirs specified returns default",
experimental: true,
specDirs: nil,
expectedInfoCDISpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
},
{
description: "experimental specified spec dirs are returned",
experimental: true,
specDirs: []string{"/foo/bar", "/baz/qux"},
expectedInfoCDISpecDirs: []string{"/foo/bar", "/baz/qux"},
},
{
description: "experimental empty string as spec dir returns empty slice",
experimental: true,
specDirs: []string{""},
expectedInfoCDISpecDirs: []string{},
},
{
description: "experimental empty config option returns empty slice",
experimental: true,
config: map[string]interface{}{"cdi-spec-dirs": []string{}},
expectedInfoCDISpecDirs: []string{},
},
{
description: "non-experimental no spec dirs specified returns empty slice",
experimental: false,
specDirs: nil,
expectedInfoCDISpecDirs: []string{},
},
{
description: "non-experimental specified spec dirs returns empty slice",
experimental: false,
specDirs: []string{"/foo/bar", "/baz/qux"},
expectedInfoCDISpecDirs: []string{},
},
{
description: "non-experimental empty string as spec dir returns empty slice",
experimental: false,
specDirs: []string{""},
expectedInfoCDISpecDirs: []string{},
},
{
description: "non-experimental empty config option returns empty slice",
experimental: false,
config: map[string]interface{}{"cdi-spec-dirs": []string{}},
expectedInfoCDISpecDirs: []string{},
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
var opts []daemon.Option
if tc.experimental {
opts = append(opts, daemon.WithExperimental())
}
d := daemon.New(t, opts...)
var args []string
for _, specDir := range tc.specDirs {
args = append(args, "--cdi-spec-dir="+specDir)
}
if tc.config != nil {
configPath := filepath.Join(t.TempDir(), "daemon.json")
configFile, err := os.Create(configPath)
assert.NilError(t, err)
defer configFile.Close()
err = json.NewEncoder(configFile).Encode(tc.config)
assert.NilError(t, err)
args = append(args, "--config-file="+configPath)
}
d.Start(t, args...)
defer d.Stop(t)
info := d.Info(t)
assert.Check(t, is.DeepEqual(tc.expectedInfoCDISpecDirs, info.CDISpecDirs))
})
}
}