Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CDI devices to device flag #2280

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

elezar
Copy link

@elezar elezar commented Jun 7, 2023

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.

This mirrors the support recently added to ctr (see containerd/containerd#8525) and under review for the Docker CLI (see docker/cli#4084).

Note that this assumes that the CLI is used to inject devices and additional work would be required for other mechanisms such as compose files.

@elezar
Copy link
Author

elezar commented Jun 7, 2023

/cc @mikebrow

Do you know who should have a look at this? Changes are similar to containerd/containerd#8525.

go.mod Outdated
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we minimize these dependencies?

Copy link
Author

@elezar elezar Jun 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code for loading the CDI specifications reads YAML files -- and contains the functionaility to set up watchers on the filesystem that updates the spec in daemonized cases. We are looking to reduce the set of dependencies required, but that work has not yet been started.

Are there any dependencies in particular that you are concerned about? (To be clear, this should be a similar set to those for containerd which includes the same feature when using CRI).

if err != nil {
return
}
for _, device := range allDevices {
if cdi.IsQualifiedName(device) {
opt.CDIDevices = append(opt.CDIDevices, device)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have an integration test too?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I will add an integration test and also gate this behind an experimental flag.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker CLI PR has been merged, so I will pick up this PR again in the next couple of days.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elezar ping?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. I will look at this again this week.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AkihiroSuda do we still need to make this feature experimental?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is consistent with Docker's behavior, it does not need to stay experimental

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is consistent, but requries an op-in feature flag in the docker config.

The --cdi-spec-dirs folder is also not a docker command line option, but I assume that that is ok.

@AkihiroSuda
Copy link
Member

Needs rebase

@AkihiroSuda
Copy link
Member

Sorry, needs another rebase

@AkihiroSuda
Copy link
Member

ping @elezar

@elezar
Copy link
Author

elezar commented Jul 12, 2024

I will look into this today.

@elezar elezar force-pushed the add-cdi-support branch 2 times, most recently from e44d28d to 0fdf318 Compare July 12, 2024 11:53
assert.NilError(t, err)
cdiSpecDir := filepath.Join(cwd, "testdata", "cdi")

os.Setenv("CDI_SPEC_DIRS", cdiSpecDir)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a basic test that sets the CDI_SPEC_DIRS option via an envvar. Do you want me to add one that sets it via the command line argument too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be nice.
It should be also supported via TOML: https://github.com/containerd/nerdctl/blob/main/docs/config.md

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have an example of a test that sets the config via toml?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TestNerdctlConfig validates the configuration precedence [CLI, Env, TOML, Default].
func TestNerdctlConfig(t *testing.T) {
testutil.DockerIncompatible(t)
t.Parallel()
tomlPath := filepath.Join(t.TempDir(), "nerdctl.toml")
err := os.WriteFile(tomlPath, []byte(`
snapshotter = "dummy-snapshotter-via-toml"
`), 0400)
assert.NilError(t, err)
base := testutil.NewBase(t)
// [Default]
base.Cmd("info", "-f", "{{.Driver}}").AssertOutExactly(containerd.DefaultSnapshotter + "\n")
// [TOML, Default]
base.Env = append(base.Env, "NERDCTL_TOML="+tomlPath)
base.Cmd("info", "-f", "{{.Driver}}").AssertOutExactly("dummy-snapshotter-via-toml\n")
// [CLI, TOML, Default]
base.Cmd("info", "-f", "{{.Driver}}", "--snapshotter=dummy-snapshotter-via-cli").AssertOutExactly("dummy-snapshotter-via-cli\n")
// [Env, TOML, Default]
base.Env = append(base.Env, "CONTAINERD_SNAPSHOTTER=dummy-snapshotter-via-env")
base.Cmd("info", "-f", "{{.Driver}}").AssertOutExactly("dummy-snapshotter-via-env\n")
// [CLI, Env, TOML, Default]
base.Cmd("info", "-f", "{{.Driver}}", "--snapshotter=dummy-snapshotter-via-cli").AssertOutExactly("dummy-snapshotter-via-cli\n")
}

@@ -175,6 +175,7 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet,
// Experimental enable experimental feature, see in https://github.com/containerd/nerdctl/blob/main/docs/experimental.md
AddPersistentBoolFlag(rootCmd, "experimental", nil, nil, cfg.Experimental, "NERDCTL_EXPERIMENTAL", "Control experimental: https://github.com/containerd/nerdctl/blob/main/docs/experimental.md")
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")
AddPersistentStringSliceFlag(rootCmd, "cdi-spec-dirs", nil, nil, cfg.CDISpecDirs, "CDI_SPEC_DIRS", "The directories to search for CDI spec files. Defaults to /etc/cdi,/var/run/cdi")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to use the existing AddPersistentStringArrayFlag here instead, but then we need to change the command line argument to --cdi-spec-dir and make it clear that it's repeatable. I'm not sure whether the envvar would be split on commas then either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this env var specific to the nerdctl implementation, or is it part of the CDI specification?
I guess the env var should use : on Linux and ; on Windows as the separator, just like $PATH (%PATH%)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The envvar is not part of the CDI specification and is a nerdctl-specific implementation detail. I'm happy to defer to you on how to implement this though. Should both the command line argument --cdi-spec-dirs and the CDI_SPEC_DIRS envvar accept a : (or ;)-separated list of paths instead of a comma-separated list?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLI flag should not use colon separators.

Also I wonder if the env var is really necessary. Maybe it should be revisited in a separate PR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I can remove the envvar for now and only use the CLI flags. It is probably cleaner for the time being.

docs/config.md Outdated
@@ -45,6 +45,7 @@ experimental = true
| `hosts_dir` | `--hosts-dir` | | `certs.d` directory | Since 0.16.0 |
| `experimental` | `--experimental` | `NERDCTL_EXPERIMENTAL` | Enable [experimental features](experimental.md) | Since 0.22.3 |
| `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 |
| `cdi_spec_dirs` | `--cdi-spec-dirs` | `CDI_SPEC_DIRS` | The folders to use when searching for CDI specifications.| experimental |
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could drop the experimental.

go.mod Outdated
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
tags.cncf.io/container-device-interface v0.8.0
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is see that there are multiple require sections in the module file. Are there specific sections where I should add these?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sections are being unified:

@elezar elezar force-pushed the add-cdi-support branch from 0fdf318 to 85444d9 Compare July 12, 2024 12:07
@elezar elezar requested a review from AkihiroSuda July 12, 2024 12:22
@elezar
Copy link
Author

elezar commented Jul 12, 2024

Looking at the failing tests these are:

  1. Rootless tests
  2. Docker compatibility tests

For the first the question I would have is whether the testdata/cdi directory is NOT accessible in some way when running rootless.

For Docker compatibility, we would need to enable the cdi feature that has been available in Docker since v25. We would also need to configure the Docker daemon to use the same CDI folder as for the tests.

Do you have any guidance on how to address these failures?

@AkihiroSuda
Copy link
Member

For the first the question I would have is whether the testdata/cdi directory is NOT accessible in some way when running rootless.

This might be being fixed in:

For Docker compatibility, we would need to enable the cdi feature that has been available in Docker since v25. We would also need to configure the Docker daemon to use the same CDI folder as for the tests.

Yes, you can update /etc/docker/daemon.json and sudo systemctl restart docker here

test-integration-docker-compatibility:

@elezar elezar force-pushed the add-cdi-support branch from 85444d9 to cbce6e7 Compare July 16, 2024 12:49
@elezar elezar force-pushed the add-cdi-support branch 2 times, most recently from 39182f4 to ffecb94 Compare July 16, 2024 13:09
- name: Enable CDI in the Docker daemon.
run: |
sudo mkdir -p /etc/docker
echo '{ "features": {"cdi": true} }' | sudo tee /etc/docker/daemon.json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be merged into daemon.json without overwriting its existing content (if any)

@@ -58,5 +60,6 @@ func New() *Config {
HostsDir: ncdefaults.HostsDirs(),
Experimental: true,
HostGatewayIP: ncdefaults.HostGatewayIP(),
CDISpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -39,6 +39,8 @@ type Config struct {
HostsDir []string `toml:"hosts_dir"`
Experimental bool `toml:"experimental"`
HostGatewayIP string `toml:"host_gateway_ip"`
// CDISpecDirs is a list of directories in which CDI specifications can be found.
CDISpecDirs []string `toml:"cdi_spec_dirs,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bike shedding: "Dirs" vs "Path"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"CDIPath" might be more consistent with the existing "CNIPath"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point is that these are specifically directories and not files. Furthermore, the naming is consistent with this setting in other projects: containerd, cri-o, docker, singularity.

I'm happy to change if you feel strongly about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the naming is consistent with this setting in other projects: containerd, cri-o, docker, singularity.

👍

@AkihiroSuda
Copy link
Member

Needs rebase

@AkihiroSuda
Copy link
Member

ping @elezar

@AkihiroSuda
Copy link
Member

@elezar Do you plan to rebase this ?

@elezar
Copy link
Author

elezar commented Oct 9, 2024

@elezar Do you plan to rebase this ?

Yes, I do need to get to it. I have been focussing on a security issue of late. I will pick this up again in the next couple of days.

@AkihiroSuda
Copy link
Member

CI failing

@AkihiroSuda
Copy link
Member

Needs rebase

@AkihiroSuda AkihiroSuda modified the milestones: v2.0.0, v2.1.0 (tentative) Nov 5, 2024
@AkihiroSuda
Copy link
Member

Moved to v2.1 milestone

@djdongjin
Copy link
Member

Hi @elezar 👋 any plan to pick this up? If not I can probably carry it over (no rush).

@AkihiroSuda AkihiroSuda modified the milestones: v2.0.4, v2.x.x Feb 24, 2025
@elezar
Copy link
Author

elezar commented Feb 25, 2025

Hi @djdongjin I have to pick this up again, but have been busy with a couple of high-priority tasks otherwise.

I will make time for this over the next month though.

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 <[email protected]>
Signed-off-by: Evan Lezar <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants