From 71c646f1478041479d15d29b5b522d342125a726 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Wed, 7 Jun 2023 16:25:22 +0200 Subject: [PATCH] Add support for CDI devices to device flag This change adds support for specifying fully-qualified CDI device names in the --device flag. This allows the Container Device Interface (CDI) to be used to inject devices into container being run. Signed-off-by: Evan Lezar Enable cdi feature for Docker Signed-off-by: Evan Lezar Update go mod Signed-off-by: Evan Lezar Signed-off-by: Jin Dong use containerd cdi.WithCDIDevices Signed-off-by: Jin Dong --- .github/workflows/workflow-test.yml | 5 ++ cmd/nerdctl/container/container_create.go | 12 +++- .../container/container_run_linux_test.go | 55 +++++++++++++++++++ cmd/nerdctl/helpers/flagutil.go | 6 ++ cmd/nerdctl/main.go | 1 + docs/config.md | 1 + go.mod | 6 ++ go.sum | 30 ++++++++++ pkg/api/types/container_types.go | 2 + pkg/cmd/container/create.go | 2 + pkg/cmd/container/run_cdi.go | 46 ++++++++++++++++ pkg/config/config.go | 3 + pkg/defaults/defaults_darwin.go | 4 ++ pkg/defaults/defaults_freebsd.go | 4 ++ pkg/defaults/defaults_linux.go | 21 +++++++ pkg/defaults/defaults_windows.go | 4 ++ 16 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 pkg/cmd/container/run_cdi.go diff --git a/.github/workflows/workflow-test.yml b/.github/workflows/workflow-test.yml index 03c7a94247e..2a618746cce 100644 --- a/.github/workflows/workflow-test.yml +++ b/.github/workflows/workflow-test.yml @@ -328,6 +328,11 @@ jobs: # FIXME: remove expect when we are done removing unbuffer from tests sudo apt-get install -qq expect make install-dev-tools + - name: Enable CDI in the Docker daemon. + run: | + sudo mkdir -p /etc/docker + sudo jq '.features.cdi = true' /etc/docker/daemon.json | sudo tee /etc/docker/daemon.json.tmp && sudo mv /etc/docker/daemon.json.tmp /etc/docker/daemon.json + sudo systemctl restart docker - name: "Ensure that the integration test suite is compatible with Docker" run: WITH_SUDO=true ./hack/test-integration.sh -test.target=docker - name: "Ensure that the IPv6 integration test suite is compatible with Docker" diff --git a/cmd/nerdctl/container/container_create.go b/cmd/nerdctl/container/container_create.go index 67db1a4f94a..81956fe0019 100644 --- a/cmd/nerdctl/container/container_create.go +++ b/cmd/nerdctl/container/container_create.go @@ -21,6 +21,7 @@ import ( "runtime" "github.com/spf13/cobra" + cdiparser "tags.cncf.io/container-device-interface/pkg/parser" "github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers" "github.com/containerd/nerdctl/v2/pkg/api/types" @@ -55,6 +56,7 @@ func CreateCommand() *cobra.Command { return cmd } +//revive:disable:function-length func createOptions(cmd *cobra.Command) (types.ContainerCreateOptions, error) { var err error opt := types.ContainerCreateOptions{ @@ -215,10 +217,18 @@ func createOptions(cmd *cobra.Command) (types.ContainerCreateOptions, error) { if err != nil { return opt, err } - opt.Device, err = cmd.Flags().GetStringSlice("device") + + allDevices, err := cmd.Flags().GetStringSlice("device") if err != nil { return opt, err } + for _, device := range allDevices { + if cdiparser.IsQualifiedName(device) { + opt.CDIDevices = append(opt.CDIDevices, device) + } else { + opt.Device = append(opt.Device, device) + } + } // #endregion // #region for blkio flags diff --git a/cmd/nerdctl/container/container_run_linux_test.go b/cmd/nerdctl/container/container_run_linux_test.go index 71f2a0c1be6..aaa4b124f99 100644 --- a/cmd/nerdctl/container/container_run_linux_test.go +++ b/cmd/nerdctl/container/container_run_linux_test.go @@ -661,3 +661,58 @@ func TestPortBindingWithCustomHost(t *testing.T) { testCase.Run(t) } + +func TestRunDeviceCDI(t *testing.T) { + t.Parallel() + // Although CDI injection is supported by Docker, specifying the --cdi-spec-dirs on the command line is not. + testutil.DockerIncompatible(t) + cdiSpecDir := filepath.Join(t.TempDir(), "cdi") + writeTestCDISpec(t, cdiSpecDir) + + base := testutil.NewBase(t) + base.Cmd("--cdi-spec-dirs", cdiSpecDir, "run", + "--rm", + "--device", "vendor1.com/device=foo", + testutil.AlpineImage, "env", + ).AssertOutContains("FOO=injected") +} + +func TestRunDeviceCDIWithNerdctlConfig(t *testing.T) { + t.Parallel() + // Although CDI injection is supported by Docker, specifying the --cdi-spec-dirs on the command line is not. + testutil.DockerIncompatible(t) + cdiSpecDir := filepath.Join(t.TempDir(), "cdi") + writeTestCDISpec(t, cdiSpecDir) + + tomlPath := filepath.Join(t.TempDir(), "nerdctl.toml") + err := os.WriteFile(tomlPath, []byte(fmt.Sprintf(` +cdi_spec_dirs = ["%s"] +`, cdiSpecDir)), 0400) + assert.NilError(t, err) + + base := testutil.NewBase(t) + base.Env = append(base.Env, "NERDCTL_TOML="+tomlPath) + base.Cmd("run", + "--rm", + "--device", "vendor1.com/device=foo", + testutil.AlpineImage, "env", + ).AssertOutContains("FOO=injected") +} + +func writeTestCDISpec(t *testing.T, cdiSpecDir string) { + const testCDIVendor1 = ` +cdiVersion: "0.3.0" +kind: "vendor1.com/device" +devices: +- name: foo + containerEdits: + env: + - FOO=injected +` + + err := os.MkdirAll(cdiSpecDir, 0700) + assert.NilError(t, err) + cdiSpecPath := filepath.Join(cdiSpecDir, "vendor1.yaml") + err = os.WriteFile(cdiSpecPath, []byte(testCDIVendor1), 0400) + assert.NilError(t, err) +} diff --git a/cmd/nerdctl/helpers/flagutil.go b/cmd/nerdctl/helpers/flagutil.go index f1137ac5ebc..4f9261c0cf3 100644 --- a/cmd/nerdctl/helpers/flagutil.go +++ b/cmd/nerdctl/helpers/flagutil.go @@ -107,6 +107,11 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error) if err != nil { return types.GlobalCommandOptions{}, err } + cdiSpecDirs, err := cmd.Flags().GetStringSlice("cdi-spec-dirs") + if err != nil { + return types.GlobalCommandOptions{}, err + } + return types.GlobalCommandOptions{ Debug: debug, DebugFull: debugFull, @@ -123,6 +128,7 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error) HostGatewayIP: hostGatewayIP, BridgeIP: bridgeIP, KubeHideDupe: kubeHideDupe, + CDISpecDirs: cdiSpecDirs, }, nil } diff --git a/cmd/nerdctl/main.go b/cmd/nerdctl/main.go index 1efadbe99d3..2b81f65b879 100644 --- a/cmd/nerdctl/main.go +++ b/cmd/nerdctl/main.go @@ -186,6 +186,7 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet, helpers.AddPersistentStringFlag(rootCmd, "host-gateway-ip", nil, nil, nil, aliasToBeInherited, cfg.HostGatewayIP, "NERDCTL_HOST_GATEWAY_IP", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the host. It has no effect without setting --add-host") helpers.AddPersistentStringFlag(rootCmd, "bridge-ip", nil, nil, nil, aliasToBeInherited, cfg.BridgeIP, "NERDCTL_BRIDGE_IP", "IP address for the default nerdctl bridge network") rootCmd.PersistentFlags().Bool("kube-hide-dupe", cfg.KubeHideDupe, "Deduplicate images for Kubernetes with namespace k8s.io") + rootCmd.PersistentFlags().StringSlice("cdi-spec-dirs", cfg.CDISpecDirs, "The directories to search for CDI spec files. Defaults to /etc/cdi,/var/run/cdi") return aliasToBeInherited, nil } diff --git a/docs/config.md b/docs/config.md index b4c6a20a381..c7bccf72ad5 100644 --- a/docs/config.md +++ b/docs/config.md @@ -47,6 +47,7 @@ experimental = true | `host_gateway_ip` | `--host-gateway-ip` | `NERDCTL_HOST_GATEWAY_IP` | IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the host. It has no effect without setting --add-host | Since 1.3.0 | | `bridge_ip` | `--bridge-ip` | `NERDCTL_BRIDGE_IP` | IP address for the default nerdctl bridge network, e.g., 10.1.100.1/24 | Since 2.0.1 | | `kube_hide_dupe` | `--kube-hide-dupe` | | Deduplicate images for Kubernetes with namespace k8s.io, no more redundant ones are displayed | Since 2.0.3 | +| `cdi_spec_dirs` | `--cdi-spec-dirs` | | The folders to use when searching for CDI ([container-device-interface](https://github.com/cncf-tags/container-device-interface)) specifications. | Since 2.1.0 | The properties are parsed in the following precedence: 1. CLI flag diff --git a/go.mod b/go.mod index d263e36bec5..572affdd6d4 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( golang.org/x/text v0.24.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 + tags.cncf.io/container-device-interface v1.0.1 ) require ( @@ -115,6 +116,7 @@ require ( github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect github.com/opencontainers/selinux v1.12.0 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect @@ -125,6 +127,7 @@ require ( github.com/smallstep/pkcs7 v0.1.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect + github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/tinylib/msgp v1.2.0 // indirect github.com/vbatts/tar-split v0.11.6 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -137,12 +140,15 @@ require ( go.opentelemetry.io/otel/metric v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/mod v0.22.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect //gomodjail:unconfined google.golang.org/grpc v1.69.4 // indirect //gomodjail:unconfined google.golang.org/protobuf v1.36.5 // indirect lukechampine.com/blake3 v1.3.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect + tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect ) replace github.com/containerd/nerdctl/mod/tigron v0.0.0 => ./mod/tigron diff --git a/go.sum b/go.sum index 19f9d5499d4..339839d3fcf 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg= github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -152,14 +154,21 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= @@ -195,6 +204,7 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -217,6 +227,7 @@ github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= @@ -239,8 +250,12 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= +github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= @@ -266,6 +281,7 @@ github.com/rootless-containers/rootlesskit/v2 v2.3.4/go.mod h1:AJNM4jS0jFIF8GvBf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU= @@ -282,6 +298,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -289,8 +306,11 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tinylib/msgp v1.2.0 h1:0uKB/662twsVBpYUPbokj4sTSKhWFKB7LopO2kWK8lY= github.com/tinylib/msgp v1.2.0/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= +github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8 h1:Y4egeTrP7sccowz2GWTJVtHlwkZippgBTpUmMteFUWQ= @@ -355,6 +375,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -390,6 +412,8 @@ golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -489,3 +513,9 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc= +tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0= +tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8= +tags.cncf.io/container-device-interface/specs-go v1.0.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ= diff --git a/pkg/api/types/container_types.go b/pkg/api/types/container_types.go index aabe8dbc23c..0781b88b83f 100644 --- a/pkg/api/types/container_types.go +++ b/pkg/api/types/container_types.go @@ -150,6 +150,8 @@ type ContainerCreateOptions struct { CgroupParent string // Device specifies add a host device to the container Device []string + // CDIDevices specifies the CDI devices to add to the container + CDIDevices []string // #endregion // #region for blkio related flags diff --git a/pkg/cmd/container/create.go b/pkg/cmd/container/create.go index 61e7686aebd..2b3b885d2e6 100644 --- a/pkg/cmd/container/create.go +++ b/pkg/cmd/container/create.go @@ -124,6 +124,8 @@ func Create(ctx context.Context, client *containerd.Client, args []string, netMa } opts = append(opts, platformOpts...) + opts = append(opts, withCDIDevices(options.GOptions.CDISpecDirs, options.CDIDevices...)) + if _, err := referenceutil.Parse(args[0]); errors.Is(err, referenceutil.ErrLoadOCIArchiveRequired) { imageRef := args[0] diff --git a/pkg/cmd/container/run_cdi.go b/pkg/cmd/container/run_cdi.go new file mode 100644 index 00000000000..da49ffb9925 --- /dev/null +++ b/pkg/cmd/container/run_cdi.go @@ -0,0 +1,46 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package container + +import ( + "context" + + "tags.cncf.io/container-device-interface/pkg/cdi" + + "github.com/containerd/containerd/v2/core/containers" + cdispec "github.com/containerd/containerd/v2/pkg/cdi" + "github.com/containerd/containerd/v2/pkg/oci" +) + +// withCDIDevices creates the OCI runtime spec options for injecting CDI devices. +// Two options are returned: The first ensures that the CDI registry is initialized with +// refresh disabled, and the second injects the devices into the container. +func withCDIDevices(cdiSpecDirs []string, devices ...string) oci.SpecOpts { + return func(ctx context.Context, client oci.Client, c *containers.Container, s *oci.Spec) error { + if len(devices) == 0 { + return nil + } + + // We configure the CDI registry with the configured spec dirs and disable refresh. + cdi.Configure( + cdi.WithSpecDirs(cdiSpecDirs...), + cdi.WithAutoRefresh(false), + ) + + return cdispec.WithCDIDevices(devices...)(ctx, client, c, s) + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 1666ab61a0e..f8a002ff752 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -41,6 +41,8 @@ type Config struct { HostGatewayIP string `toml:"host_gateway_ip"` BridgeIP string `toml:"bridge_ip, omitempty"` KubeHideDupe bool `toml:"kube_hide_dupe"` + // CDISpecDirs is a list of directories in which CDI specifications can be found. + CDISpecDirs []string `toml:"cdi_spec_dirs,omitempty"` } // New creates a default Config object statically, @@ -61,5 +63,6 @@ func New() *Config { Experimental: true, HostGatewayIP: ncdefaults.HostGatewayIP(), KubeHideDupe: false, + CDISpecDirs: ncdefaults.CDISpecDirs(), } } diff --git a/pkg/defaults/defaults_darwin.go b/pkg/defaults/defaults_darwin.go index 88aef1073e4..2779a26ec1b 100644 --- a/pkg/defaults/defaults_darwin.go +++ b/pkg/defaults/defaults_darwin.go @@ -63,3 +63,7 @@ func HostsDirs() []string { func HostGatewayIP() string { return "" } + +func CDISpecDirs() []string { + return []string{"/etc/cdi", "/var/run/cdi"} +} diff --git a/pkg/defaults/defaults_freebsd.go b/pkg/defaults/defaults_freebsd.go index cf768925f1c..4abd0d4cca4 100644 --- a/pkg/defaults/defaults_freebsd.go +++ b/pkg/defaults/defaults_freebsd.go @@ -62,3 +62,7 @@ func HostsDirs() []string { func HostGatewayIP() string { return "" } + +func CDISpecDirs() []string { + return []string{"/etc/cdi", "/var/run/cdi"} +} diff --git a/pkg/defaults/defaults_linux.go b/pkg/defaults/defaults_linux.go index a8acdf35bf3..02975aff3b0 100644 --- a/pkg/defaults/defaults_linux.go +++ b/pkg/defaults/defaults_linux.go @@ -142,3 +142,24 @@ func HostGatewayIP() string { } return "" } + +func CDISpecDirs() []string { + if !rootlessutil.IsRootless() { + return []string{"/etc/cdi", "/var/run/cdi"} + } + xch, err := rootlessutil.XDGConfigHome() + if err != nil { + panic(err) + } + xdr, err := rootlessutil.XDGRuntimeDir() + if err != nil { + if rootlessutil.IsRootlessChild() { + panic(err) + } + xdr = fmt.Sprintf("/run/user/%d", os.Geteuid()) + } + return []string{ + filepath.Join(xch, "cdi"), + filepath.Join(xdr, "cdi"), + } +} diff --git a/pkg/defaults/defaults_windows.go b/pkg/defaults/defaults_windows.go index cbb74c828c7..06a82c0285f 100644 --- a/pkg/defaults/defaults_windows.go +++ b/pkg/defaults/defaults_windows.go @@ -77,3 +77,7 @@ func HostsDirs() []string { func HostGatewayIP() string { return "" } + +func CDISpecDirs() []string { + return []string{} +}