mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Compare commits
135 Commits
v1.15.0-rc
...
v1.16.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2eb4ea9ba | ||
|
|
4686f9499c | ||
|
|
3f481cd20a | ||
|
|
cd52be86e6 | ||
|
|
b5743da52f | ||
|
|
03ccd64f33 | ||
|
|
33369861fc | ||
|
|
d9a1106e00 | ||
|
|
f26425d3fd | ||
|
|
272585d261 | ||
|
|
fe5a44cb35 | ||
|
|
5f2be72335 | ||
|
|
ae074e7ba2 | ||
|
|
876d479308 | ||
|
|
abd638add9 | ||
|
|
1dd59101c7 | ||
|
|
55630bc2c0 | ||
|
|
4f0de9f1ef | ||
|
|
bced007f87 | ||
|
|
ac90b7963d | ||
|
|
2e947edbe4 | ||
|
|
9fde4b21df | ||
|
|
84e0060fe8 | ||
|
|
024dd3126d | ||
|
|
86b272cc7b | ||
|
|
2bc24970e0 | ||
|
|
00dc0daecc | ||
|
|
e3120cbe64 | ||
|
|
00d82dd540 | ||
|
|
8fe366683e | ||
|
|
7320fcd86d | ||
|
|
01f212b7a8 | ||
|
|
71e0b8590f | ||
|
|
e841c6256a | ||
|
|
c2411e644e | ||
|
|
dffce25637 | ||
|
|
f5a4b23041 | ||
|
|
dfc8e22e12 | ||
|
|
155fe66575 | ||
|
|
9208159263 | ||
|
|
9b83d09f18 | ||
|
|
c5eda7af8e | ||
|
|
572b0401a4 | ||
|
|
0d70052105 | ||
|
|
bead6f98f3 | ||
|
|
533d7119db | ||
|
|
e4b46a09a7 | ||
|
|
8fc4b9c742 | ||
|
|
ef57c07199 | ||
|
|
b407109bdf | ||
|
|
abb5abaea4 | ||
|
|
e55e6abc09 | ||
|
|
17c044eef8 | ||
|
|
edda11d647 | ||
|
|
52d0383b47 | ||
|
|
3defc6babb | ||
|
|
7b988f15ab | ||
|
|
179d8655f9 | ||
|
|
2d7b2360d2 | ||
|
|
a61dc148b2 | ||
|
|
3f6b916a85 | ||
|
|
cf388e7e63 | ||
|
|
b435b797af | ||
|
|
c86c3aeeaf | ||
|
|
f13f1bdba4 | ||
|
|
55440f40b3 | ||
|
|
cc34996684 | ||
|
|
5a3eda4cba | ||
|
|
973a6633b3 | ||
|
|
f4d0cfb687 | ||
|
|
35b23c5a2c | ||
|
|
0dc87e5d69 | ||
|
|
edc50f6e49 | ||
|
|
7de7444b0f | ||
|
|
8d3ffcd122 | ||
|
|
d72481cbd7 | ||
|
|
a442a5ed1f | ||
|
|
7de58b4af4 | ||
|
|
fde099d25b | ||
|
|
0a3eb67df8 | ||
|
|
78f250a6b0 | ||
|
|
0aed9a16ad | ||
|
|
f46b99c2f7 | ||
|
|
d5f6e6f868 | ||
|
|
082ce066ed | ||
|
|
bbaf543537 | ||
|
|
50dd460eaa | ||
|
|
b3af77166b | ||
|
|
d8cb812c8e | ||
|
|
80386a7fb2 | ||
|
|
c0a5bbe7db | ||
|
|
ddeeca392c | ||
|
|
9944feee45 | ||
|
|
762b14b6cd | ||
|
|
e76e10fb36 | ||
|
|
fcdf565586 | ||
|
|
7a9bc14d98 | ||
|
|
5788e622f4 | ||
|
|
29c0f82ed2 | ||
|
|
e1417bee64 | ||
|
|
5f9e49705c | ||
|
|
1d2b61ee11 | ||
|
|
271987d448 | ||
|
|
6cac2f5848 | ||
|
|
ef4eb0d3c6 | ||
|
|
04ab0595fa | ||
|
|
9d3418d603 | ||
|
|
57acd85fb1 | ||
|
|
6d69ca81de | ||
|
|
be73581489 | ||
|
|
5682ce3149 | ||
|
|
cb2b000ddc | ||
|
|
cbc6ff73a4 | ||
|
|
4cd86caf67 | ||
|
|
885313af3b | ||
|
|
26e52b8013 | ||
|
|
011c658945 | ||
|
|
413da20838 | ||
|
|
09341a0934 | ||
|
|
2a9e3537ec | ||
|
|
c374520b64 | ||
|
|
e982b9798c | ||
|
|
7eb031919c | ||
|
|
97950d6b8d | ||
|
|
1613f35bf5 | ||
|
|
a78a7f866f | ||
|
|
643b89e539 | ||
|
|
bdfa525a75 | ||
|
|
93763d25f0 | ||
|
|
5800e55027 | ||
|
|
c572c3b787 | ||
|
|
3f7ed7c8db | ||
|
|
cc6cbd4a89 | ||
|
|
98ad835a77 | ||
|
|
3a1ac85020 |
@@ -33,6 +33,7 @@ stages:
|
||||
- test
|
||||
- scan
|
||||
- release
|
||||
- sign
|
||||
|
||||
.pipeline-trigger-rules:
|
||||
rules:
|
||||
@@ -144,7 +145,7 @@ trigger-pipeline:
|
||||
- docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
|
||||
- docker pull "${IMAGE_NAME}:${VERSION}-${DIST}"
|
||||
script:
|
||||
- make -f build/container/Makefile test-${DIST}
|
||||
- make -f deployments/container/Makefile test-${DIST}
|
||||
|
||||
# Define the test targets
|
||||
test-packaging:
|
||||
@@ -194,7 +195,7 @@ test-packaging:
|
||||
|
||||
# Since OUT_IMAGE_NAME and OUT_IMAGE_VERSION are set, this will push the CI image to the
|
||||
# Target
|
||||
- make -f build/container/Makefile push-${DIST}
|
||||
- make -f deployments/container/Makefile push-${DIST}
|
||||
|
||||
# Define a staging release step that pushes an image to an internal "staging" repository
|
||||
# This is triggered for all pipelines (i.e. not only tags) to test the pipeline steps
|
||||
|
||||
18
.github/dependabot.yml
vendored
18
.github/dependabot.yml
vendored
@@ -14,9 +14,15 @@ updates:
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: main
|
||||
directory: "/deployments/container"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
# This defines a specific dependabot rule for the latest release-* branch.
|
||||
target-branch: release-1.14
|
||||
target-branch: release-1.15
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -25,6 +31,16 @@ updates:
|
||||
- dependency-name: k8s.io/*
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: release-1.15
|
||||
directory: "/deployments/container"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
|
||||
26
.github/workflows/golang.yaml
vendored
26
.github/workflows/golang.yaml
vendored
@@ -16,6 +16,9 @@ name: Golang
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
@@ -29,28 +32,45 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
name: Checkout code
|
||||
- name: Get Golang version
|
||||
id: vars
|
||||
run: |
|
||||
GOLANG_VERSION=$( grep "GOLANG_VERSION :=" versions.mk )
|
||||
echo "GOLANG_VERSION=${GOLANG_VERSION##GOLANG_VERSION := }" >> $GITHUB_ENV
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: -v --timeout 5m
|
||||
skip-cache: true
|
||||
- name: Check golang modules
|
||||
run: make check-vendor
|
||||
test:
|
||||
name: Unit test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Get Golang version
|
||||
id: vars
|
||||
run: |
|
||||
GOLANG_VERSION=$( grep "GOLANG_VERSION :=" versions.mk )
|
||||
echo "GOLANG_VERSION=${GOLANG_VERSION##GOLANG_VERSION := }" >> $GITHUB_ENV
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- run: make test
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: Checkout code
|
||||
|
||||
- name: Build
|
||||
run: make docker-build
|
||||
|
||||
2
.github/workflows/image.yaml
vendored
2
.github/workflows/image.yaml
vendored
@@ -135,4 +135,4 @@ jobs:
|
||||
VERSION: ${COMMIT_SHORT_SHA}
|
||||
run: |
|
||||
echo "${VERSION}"
|
||||
make -f build/container/Makefile build-${{ matrix.dist }}
|
||||
make -f deployments/container/Makefile build-${{ matrix.dist }}
|
||||
|
||||
52
.github/workflows/release.yaml
vendored
Normal file
52
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Run this workflow on new tags
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: Check out code
|
||||
- name: Create Draft Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OWNER: ${{ github.repository_owner }}
|
||||
REPO: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
GH_EXTRA_ARGS=""
|
||||
if [[ ${{ github.ref }} == *-rc.* ]]; then
|
||||
GH_EXTRA_ARGS="--prerelease"
|
||||
fi
|
||||
gh release create ${{ github.ref }} \
|
||||
--draft \
|
||||
-t "${{ github.ref }}" \
|
||||
-R $OWNER/$REPO \
|
||||
--verify-tag \
|
||||
$GH_EXTRA_ARGS
|
||||
|
||||
- name: Upload Release Artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OWNER: ${{ github.repository_owner }}
|
||||
REPO: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
gh release upload ${{ github.ref }} CHANGELOG.md -R $OWNER/$REPO
|
||||
@@ -126,7 +126,7 @@ package-ubuntu18.04-ppc64le:
|
||||
- 'echo "Logging in to CI registry ${CI_REGISTRY}"'
|
||||
- docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
|
||||
script:
|
||||
- make -f build/container/Makefile build-${DIST}
|
||||
- make -f deployments/container/Makefile build-${DIST}
|
||||
|
||||
image-ubi8:
|
||||
extends:
|
||||
|
||||
@@ -33,7 +33,7 @@ variables:
|
||||
# On the multi-arch builder we don't need the qemu setup.
|
||||
SKIP_QEMU_SETUP: "1"
|
||||
# Define the public staging registry
|
||||
STAGING_REGISTRY: registry.gitlab.com/nvidia/container-toolkit/container-toolkit/staging
|
||||
STAGING_REGISTRY: ghcr.io/nvidia
|
||||
STAGING_VERSION: ${CI_COMMIT_SHORT_SHA}
|
||||
ARTIFACTORY_REPO_BASE: "https://urm.nvidia.com/artifactory/sw-gpu-cloudnative"
|
||||
KITMAKER_RELEASE_FOLDER: "kitmaker"
|
||||
@@ -67,7 +67,7 @@ variables:
|
||||
regctl manifest get ${IN_REGISTRY}/${IN_IMAGE_NAME}:${IN_VERSION}-${DIST} --list > /dev/null && echo "${IN_REGISTRY}/${IN_IMAGE_NAME}:${IN_VERSION}-${DIST}" || ( echo "${IN_REGISTRY}/${IN_IMAGE_NAME}:${IN_VERSION}-${DIST} does not exist" && sleep infinity )
|
||||
script:
|
||||
- regctl registry login "${OUT_REGISTRY}" -u "${OUT_REGISTRY_USER}" -p "${OUT_REGISTRY_TOKEN}"
|
||||
- make -f build/container/Makefile IMAGE=${IN_REGISTRY}/${IN_IMAGE_NAME}:${IN_VERSION}-${DIST} OUT_IMAGE=${OUT_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}-${DIST} push-${DIST}
|
||||
- make -f deployments/container/Makefile IMAGE=${IN_REGISTRY}/${IN_IMAGE_NAME}:${IN_VERSION}-${DIST} OUT_IMAGE=${OUT_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}-${DIST} push-${DIST}
|
||||
|
||||
image-ubi8:
|
||||
extends:
|
||||
@@ -244,3 +244,62 @@ release:ngc-packaging:
|
||||
extends:
|
||||
- .dist-packaging
|
||||
- .release:ngc
|
||||
|
||||
# Define the external image signing steps for NGC
|
||||
# Download the ngc cli binary for use in the sign steps
|
||||
.ngccli-setup:
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y curl unzip jq
|
||||
- |
|
||||
if [ -z "${NGCCLI_VERSION}" ]; then
|
||||
NGC_VERSION_URL="https://api.ngc.nvidia.com/v2/resources/nvidia/ngc-apps/ngc_cli/versions"
|
||||
# Extract the latest version from the JSON data using jq
|
||||
export NGCCLI_VERSION=$(curl -s $NGC_VERSION_URL | jq -r '.recipe.latestVersionIdStr')
|
||||
fi
|
||||
echo "NGCCLI_VERSION ${NGCCLI_VERSION}"
|
||||
- curl -sSLo ngccli_linux.zip https://api.ngc.nvidia.com/v2/resources/nvidia/ngc-apps/ngc_cli/versions/${NGCCLI_VERSION}/files/ngccli_linux.zip
|
||||
- unzip ngccli_linux.zip
|
||||
- chmod u+x ngc-cli/ngc
|
||||
|
||||
# .sign forms the base of the deployment jobs which signs images in the CI registry.
|
||||
# This is extended with the image name and version to be deployed.
|
||||
.sign:ngc:
|
||||
image: ubuntu:latest
|
||||
stage: sign
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
variables:
|
||||
NGC_CLI_API_KEY: "${NGC_REGISTRY_TOKEN}"
|
||||
IMAGE_NAME: "${NGC_REGISTRY_IMAGE}"
|
||||
IMAGE_TAG: "${CI_COMMIT_TAG}-${DIST}"
|
||||
retry:
|
||||
max: 2
|
||||
before_script:
|
||||
- !reference [.ngccli-setup, before_script]
|
||||
# We ensure that the IMAGE_NAME and IMAGE_TAG is set
|
||||
- 'echo Image Name: ${IMAGE_NAME} && [[ -n "${IMAGE_NAME}" ]] || exit 1'
|
||||
- 'echo Image Tag: ${IMAGE_TAG} && [[ -n "${IMAGE_TAG}" ]] || exit 1'
|
||||
script:
|
||||
- 'echo "Signing the image ${IMAGE_NAME}:${IMAGE_TAG}"'
|
||||
- ngc-cli/ngc registry image publish --source ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:${IMAGE_TAG} --public --discoverable --allow-guest --sign --org nvidia
|
||||
|
||||
sign:ngc-ubuntu20.04:
|
||||
extends:
|
||||
- .dist-ubuntu20.04
|
||||
- .sign:ngc
|
||||
needs:
|
||||
- release:ngc-ubuntu20.04
|
||||
|
||||
sign:ngc-ubi8:
|
||||
extends:
|
||||
- .dist-ubi8
|
||||
- .sign:ngc
|
||||
needs:
|
||||
- release:ngc-ubi8
|
||||
|
||||
sign:ngc-packaging:
|
||||
extends:
|
||||
- .dist-packaging
|
||||
- .sign:ngc
|
||||
needs:
|
||||
- release:ngc-packaging
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,5 +1,35 @@
|
||||
# NVIDIA Container Toolkit Changelog
|
||||
|
||||
## v1.16.0-rc.1
|
||||
|
||||
- Support vulkan ICD files directly in a driver root. This allows for the discovery of vulkan files in GKE driver installations.
|
||||
- Increase priority of ld.so.conf.d config file injected into container. This ensures that injected libraries are preferred over libraries present in the container.
|
||||
- Set default CDI spec permissions to 644. This fixes permission issues when using the `nvidia-ctk cdi transform` functions.
|
||||
- Add `dev-root` option to `nvidia-ctk system create-device-nodes` command.
|
||||
- Fix location of `libnvidia-ml.so.1` when a non-standard driver root is used. This enabled CDI spec generation when using the driver container on a host.
|
||||
- Recalculate minimum required CDI spec version on save.
|
||||
- Move `nvidia-ctk hook` commands to a separate `nvidia-cdi-hook` binary. The same subcommands are supported.
|
||||
- Use `:` as an `nvidia-ctk config --set` list separator. This fixes a bug when trying to set config options that are lists.
|
||||
|
||||
- [toolkit-container] Bump CUDA base image version to 12.5.0
|
||||
- [toolkit-container] Allow the path to `toolkit.pid` to be specified directly.
|
||||
- [toolkit-container] Remove provenance information from image manifests.
|
||||
- [toolkit-container] Add `dev-root` option when configuring the toolkit. This adds support for GKE driver installations.
|
||||
|
||||
## v1.15.0
|
||||
|
||||
* Remove `nvidia-container-runtime` and `nvidia-docker2` packages.
|
||||
* Use `XDG_DATA_DIRS` environment variable when locating config files such as graphics config files.
|
||||
* Add support for v0.7.0 Container Device Interface (CDI) specification.
|
||||
* Add `--config-search-path` option to `nvidia-ctk cdi generate` command. These paths are used when locating driver files such as graphics config files.
|
||||
* Use D3DKMTEnumAdapters3 to enumerate adpaters on WSL2 if available.
|
||||
* Add support for v1.2.0 OCI Runtime specification.
|
||||
* Explicitly set `NVIDIA_VISIBLE_DEVICES=void` in generated CDI specifications. This prevents the NVIDIA Container Runtime from making additional modifications.
|
||||
|
||||
* [libnvidia-container] Use D3DKMTEnumAdapters3 to enumerate adpaters on WSL2 if available.
|
||||
|
||||
* [toolkit-container] Bump CUDA base image version to 12.4.1
|
||||
|
||||
## v1.15.0-rc.4
|
||||
* Add a `--spec-dir` option to the `nvidia-ctk cdi generate` command. This allows specs outside of `/etc/cdi` and `/var/run/cdi` to be processed.
|
||||
* Add support for extracting device major number from `/proc/devices` if `nvidia` is used as a device name over `nvidia-frontend`.
|
||||
|
||||
16
Makefile
16
Makefile
@@ -38,8 +38,8 @@ EXAMPLE_TARGETS := $(patsubst %,example-%, $(EXAMPLES))
|
||||
CMDS := $(patsubst ./cmd/%/,%,$(sort $(dir $(wildcard ./cmd/*/))))
|
||||
CMD_TARGETS := $(patsubst %,cmd-%, $(CMDS))
|
||||
|
||||
CHECK_TARGETS := golangci-lint
|
||||
MAKE_TARGETS := binaries build check fmt lint-internal test examples cmds coverage generate licenses $(CHECK_TARGETS)
|
||||
CHECK_TARGETS := lint
|
||||
MAKE_TARGETS := binaries build check fmt test examples cmds coverage generate licenses vendor check-vendor $(CHECK_TARGETS)
|
||||
|
||||
TARGETS := $(MAKE_TARGETS) $(EXAMPLE_TARGETS) $(CMD_TARGETS)
|
||||
|
||||
@@ -87,15 +87,23 @@ goimports:
|
||||
go list -f {{.Dir}} $(MODULE)/... \
|
||||
| xargs goimports -local $(MODULE) -w
|
||||
|
||||
golangci-lint:
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
vendor:
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
go mod verify
|
||||
|
||||
check-vendor: vendor
|
||||
git diff --quiet HEAD -- go.mod go.sum vendor
|
||||
|
||||
licenses:
|
||||
go-licenses csv $(MODULE)/...
|
||||
|
||||
COVERAGE_FILE := coverage.out
|
||||
test: build cmds
|
||||
go test -v -coverprofile=$(COVERAGE_FILE) $(MODULE)/...
|
||||
go test -coverprofile=$(COVERAGE_FILE) $(MODULE)/...
|
||||
|
||||
coverage: test
|
||||
cat $(COVERAGE_FILE) | grep -v "_mock.go" > $(COVERAGE_FILE).no-mocks
|
||||
|
||||
31
cmd/nvidia-cdi-hook/README.md
Normal file
31
cmd/nvidia-cdi-hook/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# NVIDIA CDI Hook
|
||||
|
||||
The CLI `nvidia-cdi-hook` provides container device runtime hook capabilities when
|
||||
called by a container runtime, as specific in a
|
||||
[Container Device Interface](https://tags.cncf.io/container-device-interface/blob/main/SPEC.md)
|
||||
file.
|
||||
|
||||
## Generating a CDI
|
||||
|
||||
The CDI itself is created for an NVIDIA-capable device using the
|
||||
[`nvidia-ctk cdi generate`](../nvidia-ctk/) command.
|
||||
|
||||
When `nvidia-ctk cdi generate` is run, the CDI specification is generated as a yaml file.
|
||||
The CDI specification provides instructions for a container runtime to set up devices, files and
|
||||
other resources for the container prior to starting it. Those instructions
|
||||
may include executing command-line tools to prepare the filesystem. The execution
|
||||
of such command-line tools is called a hook.
|
||||
|
||||
`nvidia-cdi-hook` is the CLI tool that is expected to be called by the container runtime,
|
||||
when specified by the CDI file.
|
||||
|
||||
See the [`nvidia-ctk` documentation](../nvidia-ctk/README.md) for more information
|
||||
on generating a CDI file.
|
||||
|
||||
## Functionality
|
||||
|
||||
The `nvidia-cdi-hook` CLI provides the following functionality:
|
||||
|
||||
* `chmod` - Change the permissions of a file or directory inside the directory path to be mounted into a container.
|
||||
* `create-symlinks` - Create symlinks inside the directory path to be mounted into a container.
|
||||
* `update-ldcache` - Update the dynamic linker cache inside the directory path to be mounted into a container.
|
||||
36
cmd/nvidia-cdi-hook/commands/commands.go
Normal file
36
cmd/nvidia-cdi-hook/commands/commands.go
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 commands
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/chmod"
|
||||
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-symlinks"
|
||||
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/update-ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
// New creates the commands associated with supported CDI hooks.
|
||||
// These are shared by the nvidia-cdi-hook and nvidia-ctk hook commands.
|
||||
func New(logger logger.Interface) []*cli.Command {
|
||||
return []*cli.Command{
|
||||
ldcache.NewCommand(logger),
|
||||
symlinks.NewCommand(logger),
|
||||
chmod.NewCommand(logger),
|
||||
}
|
||||
}
|
||||
93
cmd/nvidia-cdi-hook/main.go
Normal file
93
cmd/nvidia-cdi-hook/main.go
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# 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 main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
|
||||
|
||||
cli "github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/commands"
|
||||
)
|
||||
|
||||
// options defines the options that can be set for the CLI through config files,
|
||||
// environment variables, or command line flags
|
||||
type options struct {
|
||||
// Debug indicates whether the CLI is started in "debug" mode
|
||||
Debug bool
|
||||
// Quiet indicates whether the CLI is started in "quiet" mode
|
||||
Quiet bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
logger := logrus.New()
|
||||
|
||||
// Create a options struct to hold the parsed environment variables or command line flags
|
||||
opts := options{}
|
||||
|
||||
// Create the top-level CLI
|
||||
c := cli.NewApp()
|
||||
c.Name = "NVIDIA CDI Hook"
|
||||
c.UseShortOptionHandling = true
|
||||
c.EnableBashCompletion = true
|
||||
c.Usage = "Command to structure files for usage inside a container, called as hooks from a container runtime, defined in a CDI yaml file"
|
||||
c.Version = info.GetVersionString()
|
||||
|
||||
// Setup the flags for this command
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Enable debug-level logging",
|
||||
Destination: &opts.Debug,
|
||||
EnvVars: []string{"NVIDIA_CDI_DEBUG"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "Suppress all output except for errors; overrides --debug",
|
||||
Destination: &opts.Quiet,
|
||||
EnvVars: []string{"NVIDIA_CDI_QUIET"},
|
||||
},
|
||||
}
|
||||
|
||||
// Set log-level for all subcommands
|
||||
c.Before = func(c *cli.Context) error {
|
||||
logLevel := logrus.InfoLevel
|
||||
if opts.Debug {
|
||||
logLevel = logrus.DebugLevel
|
||||
}
|
||||
if opts.Quiet {
|
||||
logLevel = logrus.ErrorLevel
|
||||
}
|
||||
logger.SetLevel(logLevel)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Define the subcommands
|
||||
c.Commands = commands.New(logger)
|
||||
|
||||
// Run the CLI
|
||||
err := c.Run(os.Args)
|
||||
if err != nil {
|
||||
logger.Errorf("%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -153,8 +153,11 @@ func (m command) resolveLDConfigPath(path string) string {
|
||||
return strings.TrimPrefix(config.NormalizeLDConfigPath("@"+path), "@")
|
||||
}
|
||||
|
||||
// createConfig creates (or updates) /etc/ld.so.conf.d/nvcr-<RANDOM_STRING>.conf in the container
|
||||
// createConfig creates (or updates) /etc/ld.so.conf.d/00-nvcr-<RANDOM_STRING>.conf in the container
|
||||
// to include the required paths.
|
||||
// Note that the 00-nvcr prefix is chosen to ensure that these libraries have
|
||||
// a higher precedence than other libraries on the system but are applied AFTER
|
||||
// 00-cuda-compat.conf.
|
||||
func (m command) createConfig(root string, folders []string) error {
|
||||
if len(folders) == 0 {
|
||||
m.logger.Debugf("No folders to add to /etc/ld.so.conf")
|
||||
@@ -165,7 +168,7 @@ func (m command) createConfig(root string, folders []string) error {
|
||||
return fmt.Errorf("failed to create ld.so.conf.d: %v", err)
|
||||
}
|
||||
|
||||
configFile, err := os.CreateTemp(filepath.Join(root, "/etc/ld.so.conf.d"), "nvcr-*.conf")
|
||||
configFile, err := os.CreateTemp(filepath.Join(root, "/etc/ld.so.conf.d"), "00-nvcr-*.conf")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config file: %v", err)
|
||||
}
|
||||
@@ -198,7 +198,7 @@ invoked from the command line as `runc` would. For example:
|
||||
```sh
|
||||
# Setup a rootfs based on Ubuntu 16.04
|
||||
cd $(mktemp -d) && mkdir rootfs
|
||||
curl -sS http://cdimage.ubuntu.com/ubuntu-base/releases/16.04/release/ubuntu-base-16.04-core-amd64.tar.gz | tar --exclude 'dev/*' -C rootfs -xz
|
||||
curl -sS http://cdimage.ubuntu.com/ubuntu-base/releases/16.04/release/ubuntu-base-16.04.6-base-amd64.tar.gz | tar --exclude 'dev/*' -C rootfs -xz
|
||||
|
||||
# Create an OCI runtime spec
|
||||
nvidia-container-runtime spec
|
||||
|
||||
@@ -47,12 +47,13 @@ type options struct {
|
||||
deviceNameStrategies cli.StringSlice
|
||||
driverRoot string
|
||||
devRoot string
|
||||
nvidiaCTKPath string
|
||||
nvidiaCDIHookPath string
|
||||
ldconfigPath string
|
||||
mode string
|
||||
vendor string
|
||||
class string
|
||||
|
||||
configSearchPaths cli.StringSlice
|
||||
librarySearchPaths cli.StringSlice
|
||||
|
||||
csv struct {
|
||||
@@ -86,6 +87,11 @@ func (m command) build() *cli.Command {
|
||||
}
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "config-search-path",
|
||||
Usage: "Specify the path to search for config files when discovering the entities that should be included in the CDI specification.",
|
||||
Destination: &opts.configSearchPaths,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Specify the file to output the generated CDI specification to. If this is '' the specification is output to STDOUT",
|
||||
@@ -126,9 +132,12 @@ func (m command) build() *cli.Command {
|
||||
Destination: &opts.librarySearchPaths,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "nvidia-ctk-path",
|
||||
Usage: "Specify the path to use for the nvidia-ctk in the generated CDI specification. If this is left empty, the path will be searched.",
|
||||
Destination: &opts.nvidiaCTKPath,
|
||||
Name: "nvidia-cdi-hook-path",
|
||||
Aliases: []string{"nvidia-ctk-path"},
|
||||
Usage: "Specify the path to use for the nvidia-cdi-hook in the generated CDI specification. " +
|
||||
"If not specified, the PATH will be searched for `nvidia-cdi-hook`. " +
|
||||
"NOTE: That if this is specified as `nvidia-ctk`, the PATH will be searched for `nvidia-ctk` instead.",
|
||||
Destination: &opts.nvidiaCDIHookPath,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ldconfig-path",
|
||||
@@ -192,7 +201,7 @@ func (m command) validateFlags(c *cli.Context, opts *options) error {
|
||||
}
|
||||
}
|
||||
|
||||
opts.nvidiaCTKPath = config.ResolveNVIDIACTKPath(m.logger, opts.nvidiaCTKPath)
|
||||
opts.nvidiaCDIHookPath = config.ResolveNVIDIACDIHookPath(m.logger, opts.nvidiaCDIHookPath)
|
||||
|
||||
if outputFileFormat := formatFromFilename(opts.output); outputFileFormat != "" {
|
||||
m.logger.Debugf("Inferred output format as %q from output file name", outputFileFormat)
|
||||
@@ -256,10 +265,11 @@ func (m command) generateSpec(opts *options) (spec.Interface, error) {
|
||||
nvcdi.WithLogger(m.logger),
|
||||
nvcdi.WithDriverRoot(opts.driverRoot),
|
||||
nvcdi.WithDevRoot(opts.devRoot),
|
||||
nvcdi.WithNVIDIACTKPath(opts.nvidiaCTKPath),
|
||||
nvcdi.WithNVIDIACDIHookPath(opts.nvidiaCDIHookPath),
|
||||
nvcdi.WithLdconfigPath(opts.ldconfigPath),
|
||||
nvcdi.WithDeviceNamers(deviceNamers...),
|
||||
nvcdi.WithMode(opts.mode),
|
||||
nvcdi.WithConfigSearchPaths(opts.configSearchPaths.Value()),
|
||||
nvcdi.WithLibrarySearchPaths(opts.librarySearchPaths.Value()),
|
||||
nvcdi.WithCSVFiles(opts.csv.files.Value()),
|
||||
nvcdi.WithCSVIgnorePatterns(opts.csv.ignorePatterns.Value()),
|
||||
|
||||
@@ -38,7 +38,8 @@ type command struct {
|
||||
// options stores the subcommand options
|
||||
type options struct {
|
||||
flags.Options
|
||||
sets cli.StringSlice
|
||||
setListSeparator string
|
||||
sets cli.StringSlice
|
||||
}
|
||||
|
||||
// NewCommand constructs an config command with the specified logger
|
||||
@@ -57,6 +58,9 @@ func (m command) build() *cli.Command {
|
||||
c := cli.Command{
|
||||
Name: "config",
|
||||
Usage: "Interact with the NVIDIA Container Toolkit configuration",
|
||||
Before: func(ctx *cli.Context) error {
|
||||
return validateFlags(ctx, &opts)
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
return run(ctx, &opts)
|
||||
},
|
||||
@@ -71,10 +75,21 @@ func (m command) build() *cli.Command {
|
||||
Destination: &opts.Config,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "set",
|
||||
Usage: "Set a config value using the pattern key=value. If value is empty, this is equivalent to specifying the same key in unset. This flag can be specified multiple times",
|
||||
Name: "set",
|
||||
Usage: "Set a config value using the pattern 'key[=value]'. " +
|
||||
"Specifying only 'key' is equivalent to 'key=true' for boolean settings. " +
|
||||
"This flag can be specified multiple times, but only the last value for a specific " +
|
||||
"config option is applied. " +
|
||||
"If the setting represents a list, the elements are colon-separated.",
|
||||
Destination: &opts.sets,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "set-list-separator",
|
||||
Usage: "Specify a separator for lists applied using the set command.",
|
||||
Hidden: true,
|
||||
Value: ":",
|
||||
Destination: &opts.setListSeparator,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "in-place",
|
||||
Aliases: []string{"i"},
|
||||
@@ -96,6 +111,13 @@ func (m command) build() *cli.Command {
|
||||
return &c
|
||||
}
|
||||
|
||||
func validateFlags(c *cli.Context, opts *options) error {
|
||||
if opts.setListSeparator == "" {
|
||||
return fmt.Errorf("set-list-separator must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func run(c *cli.Context, opts *options) error {
|
||||
cfgToml, err := config.New(
|
||||
config.WithConfigFile(opts.Config),
|
||||
@@ -105,11 +127,15 @@ func run(c *cli.Context, opts *options) error {
|
||||
}
|
||||
|
||||
for _, set := range opts.sets.Value() {
|
||||
key, value, err := setFlagToKeyValue(set)
|
||||
key, value, err := setFlagToKeyValue(set, opts.setListSeparator)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid --set option %v: %w", set, err)
|
||||
}
|
||||
cfgToml.Set(key, value)
|
||||
if value == nil {
|
||||
_ = cfgToml.Delete(key)
|
||||
} else {
|
||||
cfgToml.Set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
if err := opts.EnsureOutputFolder(); err != nil {
|
||||
@@ -135,7 +161,7 @@ var errInvalidFormat = errors.New("invalid format")
|
||||
// setFlagToKeyValue converts a --set flag to a key-value pair.
|
||||
// The set flag is of the form key[=value], with the value being optional if key refers to a
|
||||
// boolean config option.
|
||||
func setFlagToKeyValue(setFlag string) (string, interface{}, error) {
|
||||
func setFlagToKeyValue(setFlag string, setListSeparator string) (string, interface{}, error) {
|
||||
setParts := strings.SplitN(setFlag, "=", 2)
|
||||
key := setParts[0]
|
||||
|
||||
@@ -146,24 +172,29 @@ func setFlagToKeyValue(setFlag string) (string, interface{}, error) {
|
||||
|
||||
kind := field.Kind()
|
||||
if len(setParts) != 2 {
|
||||
if kind == reflect.Bool {
|
||||
if kind == reflect.Bool || (kind == reflect.Pointer && field.Elem().Kind() == reflect.Bool) {
|
||||
return key, true, nil
|
||||
}
|
||||
return key, nil, fmt.Errorf("%w: expected key=value; got %v", errInvalidFormat, setFlag)
|
||||
}
|
||||
|
||||
value := setParts[1]
|
||||
if kind == reflect.Pointer && value != "nil" {
|
||||
kind = field.Elem().Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Pointer:
|
||||
return key, nil, nil
|
||||
case reflect.Bool:
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return key, value, fmt.Errorf("%w: %w", errInvalidFormat, err)
|
||||
}
|
||||
return key, b, err
|
||||
return key, b, nil
|
||||
case reflect.String:
|
||||
return key, value, nil
|
||||
case reflect.Slice:
|
||||
valueParts := strings.Split(value, ",")
|
||||
valueParts := strings.Split(value, setListSeparator)
|
||||
switch field.Elem().Kind() {
|
||||
case reflect.String:
|
||||
return key, valueParts, nil
|
||||
@@ -201,7 +232,7 @@ func getStruct(current reflect.Type, paths ...string) (reflect.StructField, erro
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if v != tomlField {
|
||||
if strings.SplitN(v, ",", 2)[0] != tomlField {
|
||||
continue
|
||||
}
|
||||
if len(paths) == 1 {
|
||||
|
||||
@@ -25,11 +25,12 @@ import (
|
||||
func TestSetFlagToKeyValue(t *testing.T) {
|
||||
// TODO: We need to enable this test again since switching to reflect.
|
||||
testCases := []struct {
|
||||
description string
|
||||
setFlag string
|
||||
expectedKey string
|
||||
expectedValue interface{}
|
||||
expectedError error
|
||||
description string
|
||||
setFlag string
|
||||
setListSeparator string
|
||||
expectedKey string
|
||||
expectedValue interface{}
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "option not present returns an error",
|
||||
@@ -106,22 +107,34 @@ func TestSetFlagToKeyValue(t *testing.T) {
|
||||
expectedValue: []string{"string-value"},
|
||||
},
|
||||
{
|
||||
description: "[]string option returns multiple values",
|
||||
setFlag: "nvidia-container-cli.environment=first,second",
|
||||
expectedKey: "nvidia-container-cli.environment",
|
||||
expectedValue: []string{"first", "second"},
|
||||
description: "[]string option returns multiple values",
|
||||
setFlag: "nvidia-container-cli.environment=first,second",
|
||||
setListSeparator: ",",
|
||||
expectedKey: "nvidia-container-cli.environment",
|
||||
expectedValue: []string{"first", "second"},
|
||||
},
|
||||
{
|
||||
description: "[]string option returns values with equals",
|
||||
setFlag: "nvidia-container-cli.environment=first=1,second=2",
|
||||
expectedKey: "nvidia-container-cli.environment",
|
||||
expectedValue: []string{"first=1", "second=2"},
|
||||
description: "[]string option returns values with equals",
|
||||
setFlag: "nvidia-container-cli.environment=first=1,second=2",
|
||||
setListSeparator: ",",
|
||||
expectedKey: "nvidia-container-cli.environment",
|
||||
expectedValue: []string{"first=1", "second=2"},
|
||||
},
|
||||
{
|
||||
description: "[]string option returns multiple values semi-colon",
|
||||
setFlag: "nvidia-container-cli.environment=first;second",
|
||||
setListSeparator: ";",
|
||||
expectedKey: "nvidia-container-cli.environment",
|
||||
expectedValue: []string{"first", "second"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
k, v, err := setFlagToKeyValue(tc.setFlag)
|
||||
if tc.setListSeparator == "" {
|
||||
tc.setListSeparator = ","
|
||||
}
|
||||
k, v, err := setFlagToKeyValue(tc.setFlag, tc.setListSeparator)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
require.EqualValues(t, tc.expectedKey, k)
|
||||
require.EqualValues(t, tc.expectedValue, v)
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
chmod "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/hook/chmod"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/commands"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/hook/create-symlinks"
|
||||
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/hook/update-ldcache"
|
||||
)
|
||||
|
||||
type hookCommand struct {
|
||||
@@ -46,11 +43,7 @@ func (m hookCommand) build() *cli.Command {
|
||||
Usage: "A collection of hooks that may be injected into an OCI spec",
|
||||
}
|
||||
|
||||
hook.Subcommands = []*cli.Command{
|
||||
ldcache.NewCommand(m.logger),
|
||||
symlinks.NewCommand(m.logger),
|
||||
chmod.NewCommand(m.logger),
|
||||
}
|
||||
hook.Subcommands = commands.New(m.logger)
|
||||
|
||||
return &hook
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
@@ -66,6 +67,8 @@ type config struct {
|
||||
mode string
|
||||
hookFilePath string
|
||||
|
||||
runtimeConfigOverrideJSON string
|
||||
|
||||
nvidiaRuntime struct {
|
||||
name string
|
||||
path string
|
||||
@@ -153,6 +156,13 @@ func (m command) build() *cli.Command {
|
||||
Usage: "Enable CDI in the configured runtime",
|
||||
Destination: &config.cdi.enabled,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "runtime-config-override",
|
||||
Destination: &config.runtimeConfigOverrideJSON,
|
||||
Usage: "specify additional runtime options as a JSON string. The paths are relative to the runtime config.",
|
||||
Value: "{}",
|
||||
EnvVars: []string{"RUNTIME_CONFIG_OVERRIDE"},
|
||||
},
|
||||
}
|
||||
|
||||
return &configure
|
||||
@@ -194,6 +204,11 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
|
||||
config.cdi.enabled = false
|
||||
}
|
||||
|
||||
if config.runtimeConfigOverrideJSON != "" && config.runtime != "containerd" {
|
||||
m.logger.Warningf("Ignoring runtime-config-override flag for %v", config.runtime)
|
||||
config.runtimeConfigOverrideJSON = ""
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -237,10 +252,16 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
return fmt.Errorf("unable to load config for runtime %v: %v", config.runtime, err)
|
||||
}
|
||||
|
||||
runtimeConfigOverride, err := config.runtimeConfigOverride()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse config overrides: %w", err)
|
||||
}
|
||||
|
||||
err = cfg.AddRuntime(
|
||||
config.nvidiaRuntime.name,
|
||||
config.nvidiaRuntime.path,
|
||||
config.nvidiaRuntime.setAsDefault,
|
||||
runtimeConfigOverride,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update config: %v", err)
|
||||
@@ -293,6 +314,20 @@ func (c *config) getOuputConfigPath() string {
|
||||
return c.resolveConfigFilePath()
|
||||
}
|
||||
|
||||
// runtimeConfigOverride converts the specified runtimeConfigOverride JSON string to a map.
|
||||
func (o *config) runtimeConfigOverride() (map[string]interface{}, error) {
|
||||
if o.runtimeConfigOverrideJSON == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
runtimeOptions := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(o.runtimeConfigOverrideJSON), &runtimeOptions); err != nil {
|
||||
return nil, fmt.Errorf("failed to read %v as JSON: %w", o.runtimeConfigOverrideJSON, err)
|
||||
}
|
||||
|
||||
return runtimeOptions, nil
|
||||
}
|
||||
|
||||
// configureOCIHook creates and configures the OCI hook for the NVIDIA runtime
|
||||
func (m *command) configureOCIHook(c *cli.Context, config *config) error {
|
||||
err := ocihook.CreateHook(config.hookFilePath, config.nvidiaRuntime.hookPath)
|
||||
|
||||
@@ -31,7 +31,8 @@ type command struct {
|
||||
}
|
||||
|
||||
type options struct {
|
||||
driverRoot string
|
||||
root string
|
||||
devRoot string
|
||||
|
||||
dryRun bool
|
||||
|
||||
@@ -65,11 +66,21 @@ func (m command) build() *cli.Command {
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "driver-root",
|
||||
Usage: "the path to the driver root. Device nodes will be created at `DRIVER_ROOT`/dev",
|
||||
Name: "root",
|
||||
// TODO: Remove this alias
|
||||
Aliases: []string{"driver-root"},
|
||||
Usage: "the path to to the root to use to load the kernel modules. This root must be a chrootable path. " +
|
||||
"If device nodes to be created these will be created at `ROOT`/dev unless an alternative path is specified",
|
||||
Value: "/",
|
||||
Destination: &opts.driverRoot,
|
||||
EnvVars: []string{"NVIDIA_DRIVER_ROOT", "DRIVER_ROOT"},
|
||||
Destination: &opts.root,
|
||||
// TODO: Remove the NVIDIA_DRIVER_ROOT and DRIVER_ROOT envvars.
|
||||
EnvVars: []string{"ROOT", "NVIDIA_DRIVER_ROOT", "DRIVER_ROOT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "dev-root",
|
||||
Usage: "specify the root where `/dev` is located. If this is not specified, the root is assumed.",
|
||||
Destination: &opts.devRoot,
|
||||
EnvVars: []string{"NVIDIA_DEV_ROOT", "DEV_ROOT"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "control-devices",
|
||||
@@ -83,7 +94,7 @@ func (m command) build() *cli.Command {
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "dry-run",
|
||||
Usage: "if set, the command will not create any symlinks.",
|
||||
Usage: "if set, the command will not perform any operations",
|
||||
Value: false,
|
||||
Destination: &opts.dryRun,
|
||||
EnvVars: []string{"DRY_RUN"},
|
||||
@@ -94,6 +105,10 @@ func (m command) build() *cli.Command {
|
||||
}
|
||||
|
||||
func (m command) validateFlags(r *cli.Context, opts *options) error {
|
||||
if opts.devRoot == "" && opts.root != "" {
|
||||
m.logger.Infof("Using dev-root %q", opts.root)
|
||||
opts.devRoot = opts.root
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -102,7 +117,7 @@ func (m command) run(c *cli.Context, opts *options) error {
|
||||
modules := nvmodules.New(
|
||||
nvmodules.WithLogger(m.logger),
|
||||
nvmodules.WithDryRun(opts.dryRun),
|
||||
nvmodules.WithRoot(opts.driverRoot),
|
||||
nvmodules.WithRoot(opts.root),
|
||||
)
|
||||
if err := modules.LoadAll(); err != nil {
|
||||
return fmt.Errorf("failed to load NVIDIA kernel modules: %v", err)
|
||||
@@ -113,12 +128,12 @@ func (m command) run(c *cli.Context, opts *options) error {
|
||||
devices, err := nvdevices.New(
|
||||
nvdevices.WithLogger(m.logger),
|
||||
nvdevices.WithDryRun(opts.dryRun),
|
||||
nvdevices.WithDevRoot(opts.driverRoot),
|
||||
nvdevices.WithDevRoot(opts.devRoot),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.logger.Infof("Creating control device nodes at %s", opts.driverRoot)
|
||||
m.logger.Infof("Creating control device nodes at %s", opts.devRoot)
|
||||
if err := devices.CreateNVIDIAControlDevices(); err != nil {
|
||||
return fmt.Errorf("failed to create NVIDIA control device nodes: %v", err)
|
||||
}
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_DIST
|
||||
ARG CUDA_VERSION
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST}
|
||||
FROM nvidia/cuda:12.5.0-base-ubuntu20.04
|
||||
|
||||
ARG ARTIFACTS_ROOT
|
||||
COPY ${ARTIFACTS_ROOT} /artifacts/packages/
|
||||
@@ -24,7 +22,6 @@ COPY ${ARTIFACTS_ROOT} /artifacts/packages/
|
||||
WORKDIR /artifacts/packages
|
||||
|
||||
# build-args are added to the manifest.txt file below.
|
||||
ARG BASE_DIST
|
||||
ARG PACKAGE_DIST
|
||||
ARG PACKAGE_VERSION
|
||||
ARG GIT_BRANCH
|
||||
@@ -12,12 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_DIST
|
||||
ARG CUDA_VERSION
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
ARG VERSION="N/A"
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST} as build
|
||||
FROM nvidia/cuda:12.5.0-base-ubi8 as build
|
||||
|
||||
RUN yum install -y \
|
||||
wget make git gcc \
|
||||
@@ -50,17 +48,7 @@ COPY . .
|
||||
RUN GOPATH=/artifacts go install -ldflags="-s -w -X 'main.Version=${VERSION}'" ./tools/...
|
||||
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST}
|
||||
|
||||
ARG BASE_DIST
|
||||
# See https://www.centos.org/centos-linux-eol/
|
||||
# and https://stackoverflow.com/a/70930049 for move to vault.centos.org
|
||||
# and https://serverfault.com/questions/1093922/failing-to-run-yum-update-in-centos-8 for move to vault.epel.cloud
|
||||
RUN [[ "${BASE_DIST}" != "centos8" ]] || \
|
||||
( \
|
||||
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-* && \
|
||||
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-Linux-* \
|
||||
)
|
||||
FROM nvidia/cuda:12.5.0-base-ubi8
|
||||
|
||||
ENV NVIDIA_DISABLE_REQUIRE="true"
|
||||
ENV NVIDIA_VISIBLE_DEVICES=void
|
||||
@@ -12,12 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_DIST
|
||||
ARG CUDA_VERSION
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
ARG VERSION="N/A"
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST} as build
|
||||
FROM nvidia/cuda:12.5.0-base-ubuntu20.04 as build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget make git gcc \
|
||||
@@ -49,7 +47,7 @@ COPY . .
|
||||
RUN GOPATH=/artifacts go install -ldflags="-s -w -X 'main.Version=${VERSION}'" ./tools/...
|
||||
|
||||
|
||||
FROM nvcr.io/nvidia/cuda:${CUDA_VERSION}-base-${BASE_DIST}
|
||||
FROM nvcr.io/nvidia/cuda:12.5.0-base-ubuntu20.04
|
||||
|
||||
# Remove the CUDA repository configurations to avoid issues with rotated GPG keys
|
||||
RUN rm -f /etc/apt/sources.list.d/cuda.list
|
||||
@@ -56,9 +56,9 @@ TEST_TARGETS := $(patsubst %,test-%,$(DISTRIBUTIONS))
|
||||
.PHONY: $(DISTRIBUTIONS) $(PUSH_TARGETS) $(BUILD_TARGETS) $(TEST_TARGETS)
|
||||
|
||||
ifneq ($(BUILD_MULTI_ARCH_IMAGES),true)
|
||||
include $(CURDIR)/build/container/native-only.mk
|
||||
include $(CURDIR)/deployments/container/native-only.mk
|
||||
else
|
||||
include $(CURDIR)/build/container/multi-arch.mk
|
||||
include $(CURDIR)/deployments/container/multi-arch.mk
|
||||
endif
|
||||
|
||||
# For the default push target we also push a short tag equal to the version.
|
||||
@@ -84,7 +84,7 @@ push-short:
|
||||
|
||||
|
||||
build-%: DIST = $(*)
|
||||
build-%: DOCKERFILE = $(CURDIR)/build/container/Dockerfile.$(DOCKERFILE_SUFFIX)
|
||||
build-%: DOCKERFILE = $(CURDIR)/deployments/container/Dockerfile.$(DOCKERFILE_SUFFIX)
|
||||
|
||||
ARTIFACTS_ROOT ?= $(shell realpath --relative-to=$(CURDIR) $(DIST_DIR))
|
||||
|
||||
@@ -92,12 +92,11 @@ ARTIFACTS_ROOT ?= $(shell realpath --relative-to=$(CURDIR) $(DIST_DIR))
|
||||
$(BUILD_TARGETS): build-%: $(ARTIFACTS_ROOT)
|
||||
DOCKER_BUILDKIT=1 \
|
||||
$(DOCKER) $(BUILDX) build --pull \
|
||||
--provenance=false --sbom=false \
|
||||
$(DOCKER_BUILD_OPTIONS) \
|
||||
$(DOCKER_BUILD_PLATFORM_OPTIONS) \
|
||||
--tag $(IMAGE) \
|
||||
--build-arg ARTIFACTS_ROOT="$(ARTIFACTS_ROOT)" \
|
||||
--build-arg BASE_DIST="$(BASE_DIST)" \
|
||||
--build-arg CUDA_VERSION="$(CUDA_VERSION)" \
|
||||
--build-arg GOLANG_VERSION="$(GOLANG_VERSION)" \
|
||||
--build-arg PACKAGE_DIST="$(PACKAGE_DIST)" \
|
||||
--build-arg PACKAGE_VERSION="$(PACKAGE_VERSION)" \
|
||||
@@ -110,15 +109,12 @@ $(BUILD_TARGETS): build-%: $(ARTIFACTS_ROOT)
|
||||
$(CURDIR)
|
||||
|
||||
|
||||
build-ubuntu%: BASE_DIST = $(*)
|
||||
build-ubuntu%: DOCKERFILE_SUFFIX := ubuntu
|
||||
build-ubuntu%: PACKAGE_DIST = ubuntu18.04
|
||||
|
||||
build-ubi8: BASE_DIST := ubi8
|
||||
build-ubi8: DOCKERFILE_SUFFIX := centos
|
||||
build-ubi8: DOCKERFILE_SUFFIX := ubi8
|
||||
build-ubi8: PACKAGE_DIST = centos7
|
||||
|
||||
build-packaging: BASE_DIST := ubuntu20.04
|
||||
build-packaging: DOCKERFILE_SUFFIX := packaging
|
||||
build-packaging: PACKAGE_ARCH := amd64
|
||||
build-packaging: PACKAGE_DIST = all
|
||||
@@ -17,6 +17,15 @@
|
||||
ARG BASEIMAGE
|
||||
FROM ${BASEIMAGE}
|
||||
|
||||
# centos:stream8 is EOL.
|
||||
# We switch to the vault repositories for this base image.
|
||||
ARG BASEIMAGE
|
||||
RUN if [ "${BASEIMAGE}" = "quay.io/centos/centos:stream8" ]; then \
|
||||
sed -i -e "s|mirrorlist=|#mirrorlist=|g" \
|
||||
-e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" \
|
||||
/etc/yum.repos.d/CentOS-Stream-*; \
|
||||
fi
|
||||
|
||||
RUN yum install -y \
|
||||
ca-certificates \
|
||||
gcc \
|
||||
|
||||
18
go.mod
18
go.mod
@@ -3,22 +3,22 @@ module github.com/NVIDIA/nvidia-container-toolkit
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/NVIDIA/go-nvlib v0.2.0
|
||||
github.com/NVIDIA/go-nvml v0.12.0-3
|
||||
github.com/NVIDIA/go-nvlib v0.5.0
|
||||
github.com/NVIDIA/go-nvml v0.12.4-0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/urfave/cli/v2 v2.27.1
|
||||
golang.org/x/mod v0.16.0
|
||||
golang.org/x/sys v0.18.0
|
||||
tags.cncf.io/container-device-interface v0.6.2
|
||||
tags.cncf.io/container-device-interface/specs-go v0.6.0
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
golang.org/x/mod v0.18.0
|
||||
golang.org/x/sys v0.21.0
|
||||
tags.cncf.io/container-device-interface v0.7.2
|
||||
tags.cncf.io/container-device-interface/specs-go v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
36
go.sum
36
go.sum
@@ -1,11 +1,11 @@
|
||||
github.com/NVIDIA/go-nvlib v0.2.0 h1:roq+SDstbP1fcy2XVH7wB2Gz2/Ud7Q+NGQYOcVITVrA=
|
||||
github.com/NVIDIA/go-nvlib v0.2.0/go.mod h1:kFuLNTyD1tF6FbRFlk+/EdUW5BrkE+v1Y3A3/9zKSjA=
|
||||
github.com/NVIDIA/go-nvml v0.12.0-3 h1:QwfjYxEqIQVRhl8327g2Y3ZvKResPydpGSKtCIIK9jE=
|
||||
github.com/NVIDIA/go-nvml v0.12.0-3/go.mod h1:SOufGc5Wql+cxrIZ8RyJwVKDYxfbs4WPkHXqadcbfvA=
|
||||
github.com/NVIDIA/go-nvlib v0.5.0 h1:951KGrfr+p3cs89alO9z/ZxPPWKxwht9tx9rxiADoLI=
|
||||
github.com/NVIDIA/go-nvlib v0.5.0/go.mod h1:87z49ULPr4GWPSGfSIp3taU4XENRYN/enIg88MzcL4k=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-0 h1:4tkbB3pT1O77JGr0gQ6uD8FrsUPqP1A/EOEm2wI1TUg=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-0/go.mod h1:8Llmj+1Rr+9VGGwZuRer5N/aCjxGuR5nPb/9ebBiIEQ=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -58,8 +58,8 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
|
||||
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/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
|
||||
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -67,15 +67,15 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -86,7 +86,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
tags.cncf.io/container-device-interface v0.6.2 h1:dThE6dtp/93ZDGhqaED2Pu374SOeUkBfuvkLuiTdwzg=
|
||||
tags.cncf.io/container-device-interface v0.6.2/go.mod h1:Shusyhjs1A5Na/kqPVLL0KqnHQHuunol9LFeUNkuGVE=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.6.0 h1:V+tJJN6dqu8Vym6p+Ru+K5mJ49WL6Aoc5SJFSY0RLsQ=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.6.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=
|
||||
tags.cncf.io/container-device-interface v0.7.2 h1:MLqGnWfOr1wB7m08ieI4YJ3IoLKKozEnnNYBtacDPQU=
|
||||
tags.cncf.io/container-device-interface v0.7.2/go.mod h1:Xb1PvXv2BhfNb3tla4r9JL129ck1Lxv9KuU6eVOfKto=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.7.0 h1:w/maMGVeLP6TIQJVYT5pbqTi8SCw/iHZ+n4ignuGHqg=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.7.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=
|
||||
|
||||
80
hack/generate-changelog.sh
Executable file
80
hack/generate-changelog.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
set -o pipefail
|
||||
this=`basename $0`
|
||||
|
||||
usage () {
|
||||
cat << EOF
|
||||
Generate a changelog for the specified tag
|
||||
Usage: $this --reference <tag> [--remote <remote_name>]
|
||||
|
||||
Options:
|
||||
--since specify the tag to start the changelog from (default: latest tag)
|
||||
--remote specify the remote to fetch tags from (default: upstream)
|
||||
--version specify the version to be released
|
||||
--help/-h show this help and exit
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
REMOTE="upstream"
|
||||
VERSION=""
|
||||
REFERENCE=
|
||||
|
||||
# Parse command line options
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case $key in
|
||||
--since)
|
||||
REFERENCE="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--remote)
|
||||
REMOTE="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--version)
|
||||
VERSION="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--help/-h) usage
|
||||
exit 0
|
||||
;;
|
||||
*) usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Fetch the latest tags from the remote
|
||||
git fetch $REMOTE --tags
|
||||
|
||||
# if REFERENCE is not set, get the latest tag
|
||||
if [ -z "$REFERENCE" ]; then
|
||||
REFERENCE=$(git describe --tags $(git rev-list --tags --max-count=1))
|
||||
fi
|
||||
|
||||
# Print the changelog
|
||||
echo "## Changelog"
|
||||
echo ""
|
||||
echo "### Version $VERSION"
|
||||
|
||||
# Iterate over the commit messages and ignore the ones that start with "Merge" or "Bump"
|
||||
git log --pretty=format:"%s" $REFERENCE..@ | grep -Ev "(^Merge )|(^Bump)" | sed 's/^\(.*\)/- \1/g'
|
||||
@@ -33,8 +33,9 @@ const (
|
||||
configOverride = "XDG_CONFIG_HOME"
|
||||
configFilePath = "nvidia-container-runtime/config.toml"
|
||||
|
||||
nvidiaCTKExecutable = "nvidia-ctk"
|
||||
nvidiaCTKDefaultFilePath = "/usr/bin/nvidia-ctk"
|
||||
nvidiaCTKExecutable = "nvidia-ctk"
|
||||
nvidiaCTKDefaultFilePath = "/usr/bin/nvidia-ctk"
|
||||
nvidiaCDIHookDefaultFilePath = "/usr/bin/nvidia-cdi-hook"
|
||||
|
||||
nvidiaContainerRuntimeHookExecutable = "nvidia-container-runtime-hook"
|
||||
nvidiaContainerRuntimeHookDefaultPath = "/usr/bin/nvidia-container-runtime-hook"
|
||||
@@ -63,6 +64,9 @@ type Config struct {
|
||||
NVIDIACTKConfig CTKConfig `toml:"nvidia-ctk"`
|
||||
NVIDIAContainerRuntimeConfig RuntimeConfig `toml:"nvidia-container-runtime"`
|
||||
NVIDIAContainerRuntimeHookConfig RuntimeHookConfig `toml:"nvidia-container-runtime-hook"`
|
||||
|
||||
// Features allows for finer control over optional features.
|
||||
Features features `toml:"features,omitempty"`
|
||||
}
|
||||
|
||||
// GetConfigFilePath returns the path to the config file for the configured system
|
||||
@@ -174,6 +178,8 @@ var getDistIDLike = func() []string {
|
||||
// This executable is used in hooks and needs to be an absolute path.
|
||||
// If the path is specified as an absolute path, it is used directly
|
||||
// without checking for existence of an executable at that path.
|
||||
//
|
||||
// Deprecated: Use ResolveNVIDIACDIHookPath directly instead.
|
||||
func ResolveNVIDIACTKPath(logger logger.Interface, nvidiaCTKPath string) string {
|
||||
return resolveWithDefault(
|
||||
logger,
|
||||
@@ -183,6 +189,27 @@ func ResolveNVIDIACTKPath(logger logger.Interface, nvidiaCTKPath string) string
|
||||
)
|
||||
}
|
||||
|
||||
// ResolveNVIDIACDIHookPath resolves the path to the nvidia-cdi-hook binary.
|
||||
// This executable is used in hooks and needs to be an absolute path.
|
||||
// If the path is specified as an absolute path, it is used directly
|
||||
// without checking for existence of an executable at that path.
|
||||
func ResolveNVIDIACDIHookPath(logger logger.Interface, nvidiaCDIHookPath string) string {
|
||||
if filepath.Base(nvidiaCDIHookPath) == "nvidia-ctk" {
|
||||
return resolveWithDefault(
|
||||
logger,
|
||||
"NVIDIA Container Toolkit CLI",
|
||||
nvidiaCDIHookPath,
|
||||
nvidiaCTKDefaultFilePath,
|
||||
)
|
||||
}
|
||||
return resolveWithDefault(
|
||||
logger,
|
||||
"NVIDIA CDI Hook CLI",
|
||||
nvidiaCDIHookPath,
|
||||
nvidiaCDIHookDefaultFilePath,
|
||||
)
|
||||
}
|
||||
|
||||
// ResolveNVIDIAContainerRuntimeHookPath resolves the path the nvidia-container-runtime-hook binary.
|
||||
func ResolveNVIDIAContainerRuntimeHookPath(logger logger.Interface, nvidiaContainerRuntimeHookPath string) string {
|
||||
return resolveWithDefault(
|
||||
|
||||
85
internal/config/features.go
Normal file
85
internal/config/features.go
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 config
|
||||
|
||||
type featureName string
|
||||
|
||||
const (
|
||||
FeatureGDS = featureName("gds")
|
||||
FeatureMOFED = featureName("mofed")
|
||||
FeatureNVSWITCH = featureName("nvswitch")
|
||||
FeatureGDRCopy = featureName("gdrcopy")
|
||||
)
|
||||
|
||||
// features specifies a set of named features.
|
||||
type features struct {
|
||||
GDS *feature `toml:"gds,omitempty"`
|
||||
MOFED *feature `toml:"mofed,omitempty"`
|
||||
NVSWITCH *feature `toml:"nvswitch,omitempty"`
|
||||
GDRCopy *feature `toml:"gdrcopy,omitempty"`
|
||||
}
|
||||
|
||||
type feature bool
|
||||
|
||||
// IsEnabled checks whether a specified named feature is enabled.
|
||||
// An optional list of environments to check for feature-specific environment
|
||||
// variables can also be supplied.
|
||||
func (fs features) IsEnabled(n featureName, in ...getenver) bool {
|
||||
featureEnvvars := map[featureName]string{
|
||||
FeatureGDS: "NVIDIA_GDS",
|
||||
FeatureMOFED: "NVIDIA_MOFED",
|
||||
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
|
||||
FeatureGDRCopy: "NVIDIA_GDRCOPY",
|
||||
}
|
||||
|
||||
envvar := featureEnvvars[n]
|
||||
switch n {
|
||||
case FeatureGDS:
|
||||
return fs.GDS.isEnabled(envvar, in...)
|
||||
case FeatureMOFED:
|
||||
return fs.MOFED.isEnabled(envvar, in...)
|
||||
case FeatureNVSWITCH:
|
||||
return fs.NVSWITCH.isEnabled(envvar, in...)
|
||||
case FeatureGDRCopy:
|
||||
return fs.GDRCopy.isEnabled(envvar, in...)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isEnabled checks whether a feature is enabled.
|
||||
// If the enabled value is explicitly set, this is returned, otherwise the
|
||||
// associated envvar is checked in the specified getenver for the string "enabled"
|
||||
// A CUDA container / image can be passed here.
|
||||
func (f *feature) isEnabled(envvar string, ins ...getenver) bool {
|
||||
if f != nil {
|
||||
return bool(*f)
|
||||
}
|
||||
if envvar == "" {
|
||||
return false
|
||||
}
|
||||
for _, in := range ins {
|
||||
if in.Getenv(envvar) == "enabled" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type getenver interface {
|
||||
Getenv(string) string
|
||||
}
|
||||
@@ -36,20 +36,20 @@ import (
|
||||
// TODO: The logic for creating DRM devices should be consolidated between this
|
||||
// and the logic for generating CDI specs for a single device. This is only used
|
||||
// when applying OCI spec modifications to an incoming spec in "legacy" mode.
|
||||
func NewDRMNodesDiscoverer(logger logger.Interface, devices image.VisibleDevices, devRoot string, nvidiaCTKPath string) (Discover, error) {
|
||||
func NewDRMNodesDiscoverer(logger logger.Interface, devices image.VisibleDevices, devRoot string, nvidiaCDIHookPath string) (Discover, error) {
|
||||
drmDeviceNodes, err := newDRMDeviceDiscoverer(logger, devices, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create DRM device discoverer: %v", err)
|
||||
}
|
||||
|
||||
drmByPathSymlinks := newCreateDRMByPathSymlinks(logger, drmDeviceNodes, devRoot, nvidiaCTKPath)
|
||||
drmByPathSymlinks := newCreateDRMByPathSymlinks(logger, drmDeviceNodes, devRoot, nvidiaCDIHookPath)
|
||||
|
||||
discover := Merge(drmDeviceNodes, drmByPathSymlinks)
|
||||
return discover, nil
|
||||
}
|
||||
|
||||
// NewGraphicsMountsDiscoverer creates a discoverer for the mounts required by graphics tools such as vulkan.
|
||||
func NewGraphicsMountsDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPath string) (Discover, error) {
|
||||
func NewGraphicsMountsDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCDIHookPath string) (Discover, error) {
|
||||
libraries := NewMounts(
|
||||
logger,
|
||||
driver.Libraries(),
|
||||
@@ -61,49 +61,61 @@ func NewGraphicsMountsDiscoverer(logger logger.Interface, driver *root.Driver, n
|
||||
|
||||
jsonMounts := NewMounts(
|
||||
logger,
|
||||
lookup.NewFileLocator(
|
||||
lookup.WithLogger(logger),
|
||||
lookup.WithRoot(driver.Root),
|
||||
lookup.WithSearchPaths("/etc", "/usr/share"),
|
||||
),
|
||||
driver.Configs(),
|
||||
driver.Root,
|
||||
[]string{
|
||||
"glvnd/egl_vendor.d/10_nvidia.json",
|
||||
"vulkan/icd.d/nvidia_icd.json",
|
||||
"vulkan/icd.d/nvidia_layers.json",
|
||||
"vulkan/implicit_layer.d/nvidia_layers.json",
|
||||
"egl/egl_external_platform.d/15_nvidia_gbm.json",
|
||||
"egl/egl_external_platform.d/10_nvidia_wayland.json",
|
||||
"nvidia/nvoptix.bin",
|
||||
},
|
||||
)
|
||||
|
||||
xorg := optionalXorgDiscoverer(logger, driver, nvidiaCTKPath)
|
||||
xorg := optionalXorgDiscoverer(logger, driver, nvidiaCDIHookPath)
|
||||
|
||||
discover := Merge(
|
||||
libraries,
|
||||
jsonMounts,
|
||||
newVulkanMountsDiscoverer(logger, driver),
|
||||
xorg,
|
||||
)
|
||||
|
||||
return discover, nil
|
||||
}
|
||||
|
||||
// newVulkanMountsDiscoverer creates a discoverer for vulkan ICD files.
|
||||
// For these files we search the standard driver config paths as well as the
|
||||
// driver root itself. This allows us to support GKE installations where the
|
||||
// vulkan ICD files are at {{ .driverRoot }}/vulkan instead of in /etc/vulkan.
|
||||
func newVulkanMountsDiscoverer(logger logger.Interface, driver *root.Driver) Discover {
|
||||
locator := lookup.First(driver.Configs(), driver.Files())
|
||||
return &mountsToContainerPath{
|
||||
logger: logger,
|
||||
locator: locator,
|
||||
required: []string{
|
||||
"vulkan/icd.d/nvidia_icd.json",
|
||||
"vulkan/icd.d/nvidia_layers.json",
|
||||
"vulkan/implicit_layer.d/nvidia_layers.json",
|
||||
},
|
||||
containerRoot: "/etc",
|
||||
}
|
||||
}
|
||||
|
||||
type drmDevicesByPath struct {
|
||||
None
|
||||
logger logger.Interface
|
||||
nvidiaCTKPath string
|
||||
devRoot string
|
||||
devicesFrom Discover
|
||||
logger logger.Interface
|
||||
nvidiaCDIHookPath string
|
||||
devRoot string
|
||||
devicesFrom Discover
|
||||
}
|
||||
|
||||
// newCreateDRMByPathSymlinks creates a discoverer for a hook to create the by-path symlinks for DRM devices discovered by the specified devices discoverer
|
||||
func newCreateDRMByPathSymlinks(logger logger.Interface, devices Discover, devRoot string, nvidiaCTKPath string) Discover {
|
||||
func newCreateDRMByPathSymlinks(logger logger.Interface, devices Discover, devRoot string, nvidiaCDIHookPath string) Discover {
|
||||
d := drmDevicesByPath{
|
||||
logger: logger,
|
||||
nvidiaCTKPath: nvidiaCTKPath,
|
||||
devRoot: devRoot,
|
||||
devicesFrom: devices,
|
||||
logger: logger,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
devRoot: devRoot,
|
||||
devicesFrom: devices,
|
||||
}
|
||||
|
||||
return &d
|
||||
@@ -131,8 +143,8 @@ func (d drmDevicesByPath) Hooks() ([]Hook, error) {
|
||||
args = append(args, "--link", l)
|
||||
}
|
||||
|
||||
hook := CreateNvidiaCTKHook(
|
||||
d.nvidiaCTKPath,
|
||||
hook := CreateNvidiaCDIHook(
|
||||
d.nvidiaCDIHookPath,
|
||||
"create-symlinks",
|
||||
args...,
|
||||
)
|
||||
@@ -237,17 +249,17 @@ func newDRMDeviceFilter(devices image.VisibleDevices, devRoot string) (Filter, e
|
||||
}
|
||||
|
||||
type xorgHooks struct {
|
||||
libraries Discover
|
||||
driverVersion string
|
||||
nvidiaCTKPath string
|
||||
libraries Discover
|
||||
driverVersion string
|
||||
nvidiaCDIHookPath string
|
||||
}
|
||||
|
||||
var _ Discover = (*xorgHooks)(nil)
|
||||
|
||||
// optionalXorgDiscoverer creates a discoverer for Xorg libraries.
|
||||
// If the creation of the discoverer fails, a None discoverer is returned.
|
||||
func optionalXorgDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPath string) Discover {
|
||||
xorg, err := newXorgDiscoverer(logger, driver, nvidiaCTKPath)
|
||||
func optionalXorgDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCDIHookPath string) Discover {
|
||||
xorg, err := newXorgDiscoverer(logger, driver, nvidiaCDIHookPath)
|
||||
if err != nil {
|
||||
logger.Warningf("Failed to create Xorg discoverer: %v; skipping xorg libraries", err)
|
||||
return None{}
|
||||
@@ -255,7 +267,7 @@ func optionalXorgDiscoverer(logger logger.Interface, driver *root.Driver, nvidia
|
||||
return xorg
|
||||
}
|
||||
|
||||
func newXorgDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPath string) (Discover, error) {
|
||||
func newXorgDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCDIHookPath string) (Discover, error) {
|
||||
libCudaPaths, err := cuda.New(
|
||||
driver.Libraries(),
|
||||
).Locate(".*.*")
|
||||
@@ -285,18 +297,14 @@ func newXorgDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPa
|
||||
},
|
||||
)
|
||||
xorgHooks := xorgHooks{
|
||||
libraries: xorgLibs,
|
||||
driverVersion: version,
|
||||
nvidiaCTKPath: nvidiaCTKPath,
|
||||
libraries: xorgLibs,
|
||||
driverVersion: version,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
}
|
||||
|
||||
xorgConfig := NewMounts(
|
||||
logger,
|
||||
lookup.NewFileLocator(
|
||||
lookup.WithLogger(logger),
|
||||
lookup.WithRoot(driver.Root),
|
||||
lookup.WithSearchPaths("/usr/share"),
|
||||
),
|
||||
driver.Configs(),
|
||||
driver.Root,
|
||||
[]string{"X11/xorg.conf.d/10-nvidia.conf"},
|
||||
)
|
||||
@@ -340,7 +348,7 @@ func (m xorgHooks) Hooks() ([]Hook, error) {
|
||||
link := strings.TrimSuffix(target, "."+m.driverVersion)
|
||||
links := []string{fmt.Sprintf("%s::%s", filepath.Base(target), link)}
|
||||
symlinkHook := CreateCreateSymlinkHook(
|
||||
m.nvidiaCTKPath,
|
||||
m.nvidiaCDIHookPath,
|
||||
links,
|
||||
)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ func (h Hook) Hooks() ([]Hook, error) {
|
||||
}
|
||||
|
||||
// CreateCreateSymlinkHook creates a hook which creates a symlink from link -> target.
|
||||
func CreateCreateSymlinkHook(nvidiaCTKPath string, links []string) Discover {
|
||||
func CreateCreateSymlinkHook(nvidiaCDIHookPath string, links []string) Discover {
|
||||
if len(links) == 0 {
|
||||
return None{}
|
||||
}
|
||||
@@ -50,18 +50,31 @@ func CreateCreateSymlinkHook(nvidiaCTKPath string, links []string) Discover {
|
||||
for _, link := range links {
|
||||
args = append(args, "--link", link)
|
||||
}
|
||||
return CreateNvidiaCTKHook(
|
||||
nvidiaCTKPath,
|
||||
return CreateNvidiaCDIHook(
|
||||
nvidiaCDIHookPath,
|
||||
"create-symlinks",
|
||||
args...,
|
||||
)
|
||||
}
|
||||
|
||||
// CreateNvidiaCTKHook creates a hook which invokes the NVIDIA Container CLI hook subcommand.
|
||||
func CreateNvidiaCTKHook(nvidiaCTKPath string, hookName string, additionalArgs ...string) Hook {
|
||||
// CreateNvidiaCDIHook creates a hook which invokes the NVIDIA Container CLI hook subcommand.
|
||||
func CreateNvidiaCDIHook(nvidiaCDIHookPath string, hookName string, additionalArgs ...string) Hook {
|
||||
return cdiHook(nvidiaCDIHookPath).Create(hookName, additionalArgs...)
|
||||
}
|
||||
|
||||
type cdiHook string
|
||||
|
||||
func (c cdiHook) Create(name string, args ...string) Hook {
|
||||
return Hook{
|
||||
Lifecycle: cdi.CreateContainerHook,
|
||||
Path: nvidiaCTKPath,
|
||||
Args: append([]string{filepath.Base(nvidiaCTKPath), "hook", hookName}, additionalArgs...),
|
||||
Path: string(c),
|
||||
Args: append(c.requiredArgs(name), args...),
|
||||
}
|
||||
}
|
||||
func (c cdiHook) requiredArgs(name string) []string {
|
||||
base := filepath.Base(string(c))
|
||||
if base == "nvidia-ctk" {
|
||||
return []string{base, "hook", name}
|
||||
}
|
||||
return []string{base, name}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ import (
|
||||
)
|
||||
|
||||
// NewLDCacheUpdateHook creates a discoverer that updates the ldcache for the specified mounts. A logger can also be specified
|
||||
func NewLDCacheUpdateHook(logger logger.Interface, mounts Discover, nvidiaCTKPath, ldconfigPath string) (Discover, error) {
|
||||
func NewLDCacheUpdateHook(logger logger.Interface, mounts Discover, nvidiaCDIHookPath, ldconfigPath string) (Discover, error) {
|
||||
d := ldconfig{
|
||||
logger: logger,
|
||||
nvidiaCTKPath: nvidiaCTKPath,
|
||||
ldconfigPath: ldconfigPath,
|
||||
mountsFrom: mounts,
|
||||
logger: logger,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
ldconfigPath: ldconfigPath,
|
||||
mountsFrom: mounts,
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
@@ -38,10 +38,10 @@ func NewLDCacheUpdateHook(logger logger.Interface, mounts Discover, nvidiaCTKPat
|
||||
|
||||
type ldconfig struct {
|
||||
None
|
||||
logger logger.Interface
|
||||
nvidiaCTKPath string
|
||||
ldconfigPath string
|
||||
mountsFrom Discover
|
||||
logger logger.Interface
|
||||
nvidiaCDIHookPath string
|
||||
ldconfigPath string
|
||||
mountsFrom Discover
|
||||
}
|
||||
|
||||
// Hooks checks the required mounts for libraries and returns a hook to update the LDcache for the discovered paths.
|
||||
@@ -51,7 +51,7 @@ func (d ldconfig) Hooks() ([]Hook, error) {
|
||||
return nil, fmt.Errorf("failed to discover mounts for ldcache update: %v", err)
|
||||
}
|
||||
h := CreateLDCacheUpdateHook(
|
||||
d.nvidiaCTKPath,
|
||||
d.nvidiaCDIHookPath,
|
||||
d.ldconfigPath,
|
||||
getLibraryPaths(mounts),
|
||||
)
|
||||
@@ -70,7 +70,7 @@ func CreateLDCacheUpdateHook(executable string, ldconfig string, libraries []str
|
||||
args = append(args, "--folder", f)
|
||||
}
|
||||
|
||||
hook := CreateNvidiaCTKHook(
|
||||
hook := CreateNvidiaCDIHook(
|
||||
executable,
|
||||
"update-ldcache",
|
||||
args...,
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
testNvidiaCTKPath = "/foo/bar/nvidia-ctk"
|
||||
testLdconfigPath = "/bar/baz/ldconfig"
|
||||
testNvidiaCDIHookPath = "/foo/bar/nvidia-cdi-hook"
|
||||
testLdconfigPath = "/bar/baz/ldconfig"
|
||||
)
|
||||
|
||||
func TestLDCacheUpdateHook(t *testing.T) {
|
||||
@@ -42,7 +42,7 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
description: "empty mounts",
|
||||
expectedArgs: []string{"nvidia-ctk", "hook", "update-ldcache"},
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache"},
|
||||
},
|
||||
{
|
||||
description: "mount error",
|
||||
@@ -65,7 +65,7 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
Path: "/usr/local/lib/libbar.so",
|
||||
},
|
||||
},
|
||||
expectedArgs: []string{"nvidia-ctk", "hook", "update-ldcache", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
||||
},
|
||||
{
|
||||
description: "host paths are ignored",
|
||||
@@ -75,12 +75,12 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
Path: "/usr/local/lib/libfoo.so",
|
||||
},
|
||||
},
|
||||
expectedArgs: []string{"nvidia-ctk", "hook", "update-ldcache", "--folder", "/usr/local/lib"},
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib"},
|
||||
},
|
||||
{
|
||||
description: "explicit ldconfig path is passed",
|
||||
ldconfigPath: testLdconfigPath,
|
||||
expectedArgs: []string{"nvidia-ctk", "hook", "update-ldcache", "--ldconfig-path", testLdconfigPath},
|
||||
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--ldconfig-path", testLdconfigPath},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -92,12 +92,12 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
||||
},
|
||||
}
|
||||
expectedHook := Hook{
|
||||
Path: testNvidiaCTKPath,
|
||||
Path: testNvidiaCDIHookPath,
|
||||
Args: tc.expectedArgs,
|
||||
Lifecycle: "createContainer",
|
||||
}
|
||||
|
||||
d, err := NewLDCacheUpdateHook(logger, mountMock, testNvidiaCTKPath, tc.ldconfigPath)
|
||||
d, err := NewLDCacheUpdateHook(logger, mountMock, testNvidiaCDIHookPath, tc.ldconfigPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
hooks, err := d.Hooks()
|
||||
|
||||
81
internal/discover/mounts-to-container-path.go
Normal file
81
internal/discover/mounts-to-container-path.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 discover
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
)
|
||||
|
||||
// mountsToContainerPath defines a Discoverer for a required set of mounts.
|
||||
// When these are discovered by a locator the specified container root is used
|
||||
// to construct the container path for the mount returned.
|
||||
type mountsToContainerPath struct {
|
||||
None
|
||||
logger logger.Interface
|
||||
locator lookup.Locator
|
||||
required []string
|
||||
containerRoot string
|
||||
}
|
||||
|
||||
func (d *mountsToContainerPath) Mounts() ([]Mount, error) {
|
||||
seen := make(map[string]bool)
|
||||
var mounts []Mount
|
||||
for _, target := range d.required {
|
||||
if strings.Contains(target, "*") {
|
||||
// TODO: We could relax this condition.
|
||||
return nil, fmt.Errorf("wildcard patterns are not supported: %s", target)
|
||||
}
|
||||
candidates, err := d.locator.Locate(target)
|
||||
if err != nil {
|
||||
d.logger.Warningf("Could not locate %v: %v", target, err)
|
||||
continue
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
d.logger.Warningf("Missing %v", target)
|
||||
continue
|
||||
}
|
||||
hostPath := candidates[0]
|
||||
if seen[hostPath] {
|
||||
d.logger.Debugf("Skipping duplicate mount %v", hostPath)
|
||||
continue
|
||||
}
|
||||
seen[hostPath] = true
|
||||
d.logger.Debugf("Located %v as %v", target, hostPath)
|
||||
|
||||
containerPath := filepath.Join(d.containerRoot, target)
|
||||
d.logger.Infof("Selecting %v as %v", hostPath, containerPath)
|
||||
|
||||
mount := Mount{
|
||||
HostPath: hostPath,
|
||||
Path: containerPath,
|
||||
Options: []string{
|
||||
"ro",
|
||||
"nosuid",
|
||||
"nodev",
|
||||
"bind",
|
||||
},
|
||||
}
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
|
||||
return mounts, nil
|
||||
}
|
||||
148
internal/discover/mounts-to-container-path_test.go
Normal file
148
internal/discover/mounts-to-container-path_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 discover
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
)
|
||||
|
||||
func TestMountsToContainerPath(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
mountOptions := []string{
|
||||
"ro",
|
||||
"nosuid",
|
||||
"nodev",
|
||||
"bind",
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
required []string
|
||||
locator lookup.Locator
|
||||
containerRoot string
|
||||
expectedMounts []Mount
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "containerRoot is prepended",
|
||||
required: []string{"a/path/exists.txt", "another/path/exists.txt"},
|
||||
locator: &lookup.LocatorMock{
|
||||
LocateFunc: func(s string) ([]string, error) {
|
||||
return []string{"/located/root/" + s}, nil
|
||||
},
|
||||
},
|
||||
containerRoot: "/container",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
HostPath: "/located/root/a/path/exists.txt",
|
||||
Path: "/container/a/path/exists.txt",
|
||||
Options: mountOptions,
|
||||
},
|
||||
{
|
||||
HostPath: "/located/root/another/path/exists.txt",
|
||||
Path: "/container/another/path/exists.txt",
|
||||
Options: mountOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "duplicate mounts are skipped",
|
||||
required: []string{"a/path/exists.txt", "another/path/exists.txt"},
|
||||
locator: &lookup.LocatorMock{
|
||||
LocateFunc: func(s string) ([]string, error) {
|
||||
return []string{"/located/root/single.txt"}, nil
|
||||
},
|
||||
},
|
||||
containerRoot: "/container",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
HostPath: "/located/root/single.txt",
|
||||
Path: "/container/a/path/exists.txt",
|
||||
Options: mountOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "locator errors are ignored",
|
||||
required: []string{"a/path/exists.txt"},
|
||||
locator: &lookup.LocatorMock{
|
||||
LocateFunc: func(s string) ([]string, error) {
|
||||
return nil, errors.New("not found")
|
||||
},
|
||||
},
|
||||
containerRoot: "/container",
|
||||
expectedMounts: []Mount{},
|
||||
},
|
||||
{
|
||||
description: "not located are ignored",
|
||||
required: []string{"a/path/exists.txt"},
|
||||
locator: &lookup.LocatorMock{
|
||||
LocateFunc: func(s string) ([]string, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
containerRoot: "/container",
|
||||
expectedMounts: []Mount{},
|
||||
},
|
||||
{
|
||||
description: "second candidate is ignored",
|
||||
required: []string{"a/path/exists.txt"},
|
||||
locator: &lookup.LocatorMock{
|
||||
LocateFunc: func(s string) ([]string, error) {
|
||||
return []string{"/located/root/" + s, "/located2/root/" + s}, nil
|
||||
},
|
||||
},
|
||||
containerRoot: "/container",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
HostPath: "/located/root/a/path/exists.txt",
|
||||
Path: "/container/a/path/exists.txt",
|
||||
Options: mountOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
d := mountsToContainerPath{
|
||||
logger: logger,
|
||||
locator: tc.locator,
|
||||
required: tc.required,
|
||||
containerRoot: tc.containerRoot,
|
||||
}
|
||||
|
||||
devices, err := d.Devices()
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, devices)
|
||||
|
||||
hooks, err := d.Hooks()
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, hooks)
|
||||
|
||||
mounts, err := d.Mounts()
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
require.ElementsMatch(t, tc.expectedMounts, mounts)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/info"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
)
|
||||
|
||||
// additionalInfo allows for the info.Interface to be extened to implement the infoInterface.
|
||||
|
||||
@@ -20,7 +20,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -32,7 +33,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
description: "init failure returns false",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.ERROR_LIBRARY_NOT_FOUND
|
||||
},
|
||||
@@ -41,7 +42,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "no devices returns false",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -56,7 +57,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "DeviceGetCount error returns false",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -71,7 +72,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "Failure to get device name returns false",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -82,7 +83,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
return 1, nvml.SUCCESS
|
||||
},
|
||||
DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) {
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
return "", nvml.ERROR_UNKNOWN
|
||||
},
|
||||
@@ -94,7 +95,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "nested panic returns false",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -105,7 +106,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
return 1, nvml.SUCCESS
|
||||
},
|
||||
DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) {
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
panic("deep panic")
|
||||
},
|
||||
@@ -117,7 +118,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "Single device name with no nvgpu",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -128,7 +129,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
return 1, nvml.SUCCESS
|
||||
},
|
||||
DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) {
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
return "NVIDIA A100-SXM4-40GB", nvml.SUCCESS
|
||||
},
|
||||
@@ -140,7 +141,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "Single device name with nvgpu",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -151,7 +152,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
return 1, nvml.SUCCESS
|
||||
},
|
||||
DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) {
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
return "Orin (nvgpu)", nvml.SUCCESS
|
||||
},
|
||||
@@ -163,7 +164,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "Multiple device names with no nvgpu",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -174,7 +175,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
return 2, nvml.SUCCESS
|
||||
},
|
||||
DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) {
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
return "NVIDIA A100-SXM4-40GB", nvml.SUCCESS
|
||||
},
|
||||
@@ -186,7 +187,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "Multiple device names with nvgpu",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -197,7 +198,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
return 2, nvml.SUCCESS
|
||||
},
|
||||
DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) {
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
return "Orin (nvgpu)", nvml.SUCCESS
|
||||
},
|
||||
@@ -209,7 +210,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "Mixed device names",
|
||||
nvmllib: &nvml.InterfaceMock{
|
||||
nvmllib: &mock.Interface{
|
||||
InitFunc: func() nvml.Return {
|
||||
return nvml.SUCCESS
|
||||
},
|
||||
@@ -226,7 +227,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
} else {
|
||||
deviceName = "Orin (nvgpu)"
|
||||
}
|
||||
device := &nvml.DeviceMock{
|
||||
device := &mock.Device{
|
||||
GetNameFunc: func() (string, nvml.Return) {
|
||||
return deviceName, nvml.SUCCESS
|
||||
},
|
||||
@@ -242,7 +243,7 @@ func TestUsesNVGPUModule(t *testing.T) {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
sut := additionalInfo{
|
||||
nvmllib: tc.nvmllib,
|
||||
devicelib: device.New(device.WithNvml(tc.nvmllib)),
|
||||
devicelib: device.New(tc.nvmllib),
|
||||
}
|
||||
|
||||
flag, _ := sut.UsesNVGPUModule()
|
||||
|
||||
@@ -17,75 +17,40 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/info"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
// infoInterface provides an alias for mocking.
|
||||
//
|
||||
//go:generate moq -stub -out info-interface_mock.go . infoInterface
|
||||
type infoInterface interface {
|
||||
info.Interface
|
||||
// UsesNVGPUModule indicates whether the system is using the nvgpu kernel module
|
||||
UsesNVGPUModule() (bool, string)
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
logger logger.Interface
|
||||
info infoInterface
|
||||
}
|
||||
|
||||
// ResolveAutoMode determines the correct mode for the platform if set to "auto"
|
||||
func ResolveAutoMode(logger logger.Interface, mode string, image image.CUDA) (rmode string) {
|
||||
nvinfo := info.New()
|
||||
nvmllib := nvml.New()
|
||||
devicelib := device.New(
|
||||
device.WithNvml(nvmllib),
|
||||
)
|
||||
|
||||
info := additionalInfo{
|
||||
Interface: nvinfo,
|
||||
nvmllib: nvmllib,
|
||||
devicelib: devicelib,
|
||||
}
|
||||
|
||||
r := resolver{
|
||||
logger: logger,
|
||||
info: info,
|
||||
}
|
||||
return r.resolveMode(mode, image)
|
||||
return resolveMode(logger, mode, image, nil)
|
||||
}
|
||||
|
||||
// resolveMode determines the correct mode for the platform if set to "auto"
|
||||
func (r resolver) resolveMode(mode string, image image.CUDA) (rmode string) {
|
||||
func resolveMode(logger logger.Interface, mode string, image image.CUDA, propertyExtractor info.PropertyExtractor) (rmode string) {
|
||||
if mode != "auto" {
|
||||
r.logger.Infof("Using requested mode '%s'", mode)
|
||||
logger.Infof("Using requested mode '%s'", mode)
|
||||
return mode
|
||||
}
|
||||
defer func() {
|
||||
r.logger.Infof("Auto-detected mode as '%v'", rmode)
|
||||
logger.Infof("Auto-detected mode as '%v'", rmode)
|
||||
}()
|
||||
|
||||
if image.OnlyFullyQualifiedCDIDevices() {
|
||||
return "cdi"
|
||||
}
|
||||
|
||||
isTegra, reason := r.info.IsTegraSystem()
|
||||
r.logger.Debugf("Is Tegra-based system? %v: %v", isTegra, reason)
|
||||
nvinfo := info.New(
|
||||
info.WithLogger(logger),
|
||||
info.WithPropertyExtractor(propertyExtractor),
|
||||
)
|
||||
|
||||
hasNVML, reason := r.info.HasNvml()
|
||||
r.logger.Debugf("Has NVML? %v: %v", hasNVML, reason)
|
||||
|
||||
usesNVGPUModule, reason := r.info.UsesNVGPUModule()
|
||||
r.logger.Debugf("Uses nvgpu kernel module? %v: %v", usesNVGPUModule, reason)
|
||||
|
||||
if (isTegra && !hasNVML) || usesNVGPUModule {
|
||||
switch nvinfo.ResolvePlatform() {
|
||||
case info.PlatformNVML, info.PlatformWSL:
|
||||
return "legacy"
|
||||
case info.PlatformTegra:
|
||||
return "csv"
|
||||
}
|
||||
|
||||
return "legacy"
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package info
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/info"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -202,23 +203,24 @@ func TestResolveAutoMode(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
info := &infoInterfaceMock{
|
||||
properties := &info.PropertyExtractorMock{
|
||||
HasNvmlFunc: func() (bool, string) {
|
||||
return tc.info["nvml"], "nvml"
|
||||
},
|
||||
HasDXCoreFunc: func() (bool, string) {
|
||||
return tc.info["dxcore"], "dxcore"
|
||||
},
|
||||
IsTegraSystemFunc: func() (bool, string) {
|
||||
return tc.info["tegra"], "tegra"
|
||||
},
|
||||
UsesNVGPUModuleFunc: func() (bool, string) {
|
||||
HasTegraFilesFunc: func() (bool, string) {
|
||||
return tc.info["tegra"], "tegra"
|
||||
},
|
||||
UsesOnlyNVGPUModuleFunc: func() (bool, string) {
|
||||
return tc.info["nvgpu"], "nvgpu"
|
||||
},
|
||||
}
|
||||
|
||||
r := resolver{
|
||||
logger: logger,
|
||||
info: info,
|
||||
}
|
||||
|
||||
var mounts []specs.Mount
|
||||
for _, d := range tc.mounts {
|
||||
mount := specs.Mount{
|
||||
@@ -231,7 +233,7 @@ func TestResolveAutoMode(t *testing.T) {
|
||||
image.WithEnvMap(tc.envmap),
|
||||
image.WithMounts(mounts),
|
||||
)
|
||||
mode := r.resolveMode(tc.mode, image)
|
||||
mode := resolveMode(logger, tc.mode, image, properties)
|
||||
require.EqualValues(t, tc.expectedMode, mode)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
// Code generated by moq; DO NOT EDIT.
|
||||
// github.com/matryer/moq
|
||||
|
||||
package info
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ensure, that infoInterfaceMock does implement infoInterface.
|
||||
// If this is not the case, regenerate this file with moq.
|
||||
var _ infoInterface = &infoInterfaceMock{}
|
||||
|
||||
// infoInterfaceMock is a mock implementation of infoInterface.
|
||||
//
|
||||
// func TestSomethingThatUsesinfoInterface(t *testing.T) {
|
||||
//
|
||||
// // make and configure a mocked infoInterface
|
||||
// mockedinfoInterface := &infoInterfaceMock{
|
||||
// HasDXCoreFunc: func() (bool, string) {
|
||||
// panic("mock out the HasDXCore method")
|
||||
// },
|
||||
// HasNvmlFunc: func() (bool, string) {
|
||||
// panic("mock out the HasNvml method")
|
||||
// },
|
||||
// IsTegraSystemFunc: func() (bool, string) {
|
||||
// panic("mock out the IsTegraSystem method")
|
||||
// },
|
||||
// UsesNVGPUModuleFunc: func() (bool, string) {
|
||||
// panic("mock out the UsesNVGPUModule method")
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// // use mockedinfoInterface in code that requires infoInterface
|
||||
// // and then make assertions.
|
||||
//
|
||||
// }
|
||||
type infoInterfaceMock struct {
|
||||
// HasDXCoreFunc mocks the HasDXCore method.
|
||||
HasDXCoreFunc func() (bool, string)
|
||||
|
||||
// HasNvmlFunc mocks the HasNvml method.
|
||||
HasNvmlFunc func() (bool, string)
|
||||
|
||||
// IsTegraSystemFunc mocks the IsTegraSystem method.
|
||||
IsTegraSystemFunc func() (bool, string)
|
||||
|
||||
// UsesNVGPUModuleFunc mocks the UsesNVGPUModule method.
|
||||
UsesNVGPUModuleFunc func() (bool, string)
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
// HasDXCore holds details about calls to the HasDXCore method.
|
||||
HasDXCore []struct {
|
||||
}
|
||||
// HasNvml holds details about calls to the HasNvml method.
|
||||
HasNvml []struct {
|
||||
}
|
||||
// IsTegraSystem holds details about calls to the IsTegraSystem method.
|
||||
IsTegraSystem []struct {
|
||||
}
|
||||
// UsesNVGPUModule holds details about calls to the UsesNVGPUModule method.
|
||||
UsesNVGPUModule []struct {
|
||||
}
|
||||
}
|
||||
lockHasDXCore sync.RWMutex
|
||||
lockHasNvml sync.RWMutex
|
||||
lockIsTegraSystem sync.RWMutex
|
||||
lockUsesNVGPUModule sync.RWMutex
|
||||
}
|
||||
|
||||
// HasDXCore calls HasDXCoreFunc.
|
||||
func (mock *infoInterfaceMock) HasDXCore() (bool, string) {
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockHasDXCore.Lock()
|
||||
mock.calls.HasDXCore = append(mock.calls.HasDXCore, callInfo)
|
||||
mock.lockHasDXCore.Unlock()
|
||||
if mock.HasDXCoreFunc == nil {
|
||||
var (
|
||||
bOut bool
|
||||
sOut string
|
||||
)
|
||||
return bOut, sOut
|
||||
}
|
||||
return mock.HasDXCoreFunc()
|
||||
}
|
||||
|
||||
// HasDXCoreCalls gets all the calls that were made to HasDXCore.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedinfoInterface.HasDXCoreCalls())
|
||||
func (mock *infoInterfaceMock) HasDXCoreCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockHasDXCore.RLock()
|
||||
calls = mock.calls.HasDXCore
|
||||
mock.lockHasDXCore.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// HasNvml calls HasNvmlFunc.
|
||||
func (mock *infoInterfaceMock) HasNvml() (bool, string) {
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockHasNvml.Lock()
|
||||
mock.calls.HasNvml = append(mock.calls.HasNvml, callInfo)
|
||||
mock.lockHasNvml.Unlock()
|
||||
if mock.HasNvmlFunc == nil {
|
||||
var (
|
||||
bOut bool
|
||||
sOut string
|
||||
)
|
||||
return bOut, sOut
|
||||
}
|
||||
return mock.HasNvmlFunc()
|
||||
}
|
||||
|
||||
// HasNvmlCalls gets all the calls that were made to HasNvml.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedinfoInterface.HasNvmlCalls())
|
||||
func (mock *infoInterfaceMock) HasNvmlCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockHasNvml.RLock()
|
||||
calls = mock.calls.HasNvml
|
||||
mock.lockHasNvml.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// IsTegraSystem calls IsTegraSystemFunc.
|
||||
func (mock *infoInterfaceMock) IsTegraSystem() (bool, string) {
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockIsTegraSystem.Lock()
|
||||
mock.calls.IsTegraSystem = append(mock.calls.IsTegraSystem, callInfo)
|
||||
mock.lockIsTegraSystem.Unlock()
|
||||
if mock.IsTegraSystemFunc == nil {
|
||||
var (
|
||||
bOut bool
|
||||
sOut string
|
||||
)
|
||||
return bOut, sOut
|
||||
}
|
||||
return mock.IsTegraSystemFunc()
|
||||
}
|
||||
|
||||
// IsTegraSystemCalls gets all the calls that were made to IsTegraSystem.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedinfoInterface.IsTegraSystemCalls())
|
||||
func (mock *infoInterfaceMock) IsTegraSystemCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockIsTegraSystem.RLock()
|
||||
calls = mock.calls.IsTegraSystem
|
||||
mock.lockIsTegraSystem.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// UsesNVGPUModule calls UsesNVGPUModuleFunc.
|
||||
func (mock *infoInterfaceMock) UsesNVGPUModule() (bool, string) {
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockUsesNVGPUModule.Lock()
|
||||
mock.calls.UsesNVGPUModule = append(mock.calls.UsesNVGPUModule, callInfo)
|
||||
mock.lockUsesNVGPUModule.Unlock()
|
||||
if mock.UsesNVGPUModuleFunc == nil {
|
||||
var (
|
||||
bOut bool
|
||||
sOut string
|
||||
)
|
||||
return bOut, sOut
|
||||
}
|
||||
return mock.UsesNVGPUModuleFunc()
|
||||
}
|
||||
|
||||
// UsesNVGPUModuleCalls gets all the calls that were made to UsesNVGPUModule.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedinfoInterface.UsesNVGPUModuleCalls())
|
||||
func (mock *infoInterfaceMock) UsesNVGPUModuleCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockUsesNVGPUModule.RLock()
|
||||
calls = mock.calls.UsesNVGPUModule
|
||||
mock.lockUsesNVGPUModule.RUnlock()
|
||||
return calls
|
||||
}
|
||||
45
internal/lookup/root/options.go
Normal file
45
internal/lookup/root/options.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 root
|
||||
|
||||
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
|
||||
type Option func(*Driver)
|
||||
|
||||
func WithLogger(logger logger.Interface) Option {
|
||||
return func(d *Driver) {
|
||||
d.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func WithDriverRoot(root string) Option {
|
||||
return func(d *Driver) {
|
||||
d.Root = root
|
||||
}
|
||||
}
|
||||
|
||||
func WithLibrarySearchPaths(paths ...string) Option {
|
||||
return func(d *Driver) {
|
||||
d.librarySearchPaths = paths
|
||||
}
|
||||
}
|
||||
|
||||
func WithConfigSearchPaths(paths ...string) Option {
|
||||
return func(d *Driver) {
|
||||
d.configSearchPaths = paths
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
@@ -30,27 +31,65 @@ type Driver struct {
|
||||
Root string
|
||||
// librarySearchPaths specifies explicit search paths for discovering libraries.
|
||||
librarySearchPaths []string
|
||||
// configSearchPaths specified explicit search paths for discovering driver config files.
|
||||
configSearchPaths []string
|
||||
}
|
||||
|
||||
// New creates a new Driver root at the specified path.
|
||||
// TODO: Use functional options here.
|
||||
func New(logger logger.Interface, path string, librarySearchPaths []string) *Driver {
|
||||
return &Driver{
|
||||
logger: logger,
|
||||
Root: path,
|
||||
librarySearchPaths: normalizeSearchPaths(librarySearchPaths...),
|
||||
// New creates a new Driver root using the specified options.
|
||||
func New(opts ...Option) *Driver {
|
||||
d := &Driver{}
|
||||
for _, opt := range opts {
|
||||
opt(d)
|
||||
}
|
||||
if d.logger == nil {
|
||||
d.logger = logger.New()
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Drivers returns a Locator for driver libraries.
|
||||
// Files returns a Locator for arbitrary driver files.
|
||||
func (r *Driver) Files(opts ...lookup.Option) lookup.Locator {
|
||||
return lookup.NewFileLocator(
|
||||
append(opts,
|
||||
lookup.WithLogger(r.logger),
|
||||
lookup.WithRoot(r.Root),
|
||||
)...,
|
||||
)
|
||||
}
|
||||
|
||||
// Libraries returns a Locator for driver libraries.
|
||||
func (r *Driver) Libraries() lookup.Locator {
|
||||
return lookup.NewLibraryLocator(
|
||||
lookup.WithLogger(r.logger),
|
||||
lookup.WithRoot(r.Root),
|
||||
lookup.WithSearchPaths(r.librarySearchPaths...),
|
||||
lookup.WithSearchPaths(normalizeSearchPaths(r.librarySearchPaths...)...),
|
||||
)
|
||||
}
|
||||
|
||||
// Configs returns a locator for driver configs.
|
||||
// If configSearchPaths is specified, these paths are used as absolute paths,
|
||||
// otherwise, /etc and /usr/share are searched.
|
||||
func (r *Driver) Configs() lookup.Locator {
|
||||
return lookup.NewFileLocator(r.configSearchOptions()...)
|
||||
}
|
||||
|
||||
func (r *Driver) configSearchOptions() []lookup.Option {
|
||||
if len(r.configSearchPaths) > 0 {
|
||||
return []lookup.Option{
|
||||
lookup.WithLogger(r.logger),
|
||||
lookup.WithRoot("/"),
|
||||
lookup.WithSearchPaths(normalizeSearchPaths(r.configSearchPaths...)...),
|
||||
}
|
||||
}
|
||||
searchPaths := []string{"/etc"}
|
||||
searchPaths = append(searchPaths, xdgDataDirs()...)
|
||||
return []lookup.Option{
|
||||
lookup.WithLogger(r.logger),
|
||||
lookup.WithRoot(r.Root),
|
||||
lookup.WithSearchPaths(searchPaths...),
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeSearchPaths takes a list of paths and normalized these.
|
||||
// Each of the elements in the list is expanded if it is a path list and the
|
||||
// resultant list is returned.
|
||||
@@ -63,3 +102,13 @@ func normalizeSearchPaths(paths ...string) []string {
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
// xdgDataDirs finds the paths as specified in the environment variable XDG_DATA_DIRS.
|
||||
// See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html.
|
||||
func xdgDataDirs() []string {
|
||||
if dirs, exists := os.LookupEnv("XDG_DATA_DIRS"); exists && dirs != "" {
|
||||
return normalizeSearchPaths(dirs)
|
||||
}
|
||||
|
||||
return []string{"/usr/local/share", "/usr/share"}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, de
|
||||
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devices []string) (spec.Interface, error) {
|
||||
cdilib, err := nvcdi.New(
|
||||
nvcdi.WithLogger(logger),
|
||||
nvcdi.WithNVIDIACTKPath(cfg.NVIDIACTKConfig.Path),
|
||||
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
|
||||
nvcdi.WithDriverRoot(cfg.NVIDIAContainerCLIConfig.Root),
|
||||
nvcdi.WithVendor("runtime.nvidia.com"),
|
||||
nvcdi.WithClass("gpu"),
|
||||
|
||||
@@ -62,7 +62,7 @@ func NewCSVModifier(logger logger.Interface, cfg *config.Config, image image.CUD
|
||||
cdilib, err := nvcdi.New(
|
||||
nvcdi.WithLogger(logger),
|
||||
nvcdi.WithDriverRoot(cfg.NVIDIAContainerCLIConfig.Root),
|
||||
nvcdi.WithNVIDIACTKPath(cfg.NVIDIACTKConfig.Path),
|
||||
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
|
||||
nvcdi.WithMode(nvcdi.ModeCSV),
|
||||
nvcdi.WithCSVFiles(csvFiles),
|
||||
)
|
||||
|
||||
@@ -26,13 +26,6 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
const (
|
||||
nvidiaGDSEnvvar = "NVIDIA_GDS"
|
||||
nvidiaMOFEDEnvvar = "NVIDIA_MOFED"
|
||||
nvidiaNVSWITCHEnvvar = "NVIDIA_NVSWITCH"
|
||||
nvidiaGDRCOPYEnvvar = "NVIDIA_GDRCOPY"
|
||||
)
|
||||
|
||||
// NewFeatureGatedModifier creates the modifiers for optional features.
|
||||
// These include:
|
||||
//
|
||||
@@ -53,7 +46,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
driverRoot := cfg.NVIDIAContainerCLIConfig.Root
|
||||
devRoot := cfg.NVIDIAContainerCLIConfig.Root
|
||||
|
||||
if image.Getenv(nvidiaGDSEnvvar) == "enabled" {
|
||||
if cfg.Features.IsEnabled(config.FeatureGDS, image) {
|
||||
d, err := discover.NewGDSDiscoverer(logger, driverRoot, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for GDS devices: %w", err)
|
||||
@@ -61,7 +54,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if image.Getenv(nvidiaMOFEDEnvvar) == "enabled" {
|
||||
if cfg.Features.IsEnabled(config.FeatureMOFED, image) {
|
||||
d, err := discover.NewMOFEDDiscoverer(logger, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for MOFED devices: %w", err)
|
||||
@@ -69,7 +62,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if image.Getenv(nvidiaNVSWITCHEnvvar) == "enabled" {
|
||||
if cfg.Features.IsEnabled(config.FeatureNVSWITCH, image) {
|
||||
d, err := discover.NewNvSwitchDiscoverer(logger, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for NVSWITCH devices: %w", err)
|
||||
@@ -77,7 +70,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if image.Getenv(nvidiaGDRCOPYEnvvar) == "enabled" {
|
||||
if cfg.Features.IsEnabled(config.FeatureGDRCopy, image) {
|
||||
d, err := discover.NewGDRCopyDiscoverer(logger, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for GDRCopy devices: %w", err)
|
||||
|
||||
@@ -29,20 +29,18 @@ import (
|
||||
|
||||
// NewGraphicsModifier constructs a modifier that injects graphics-related modifications into an OCI runtime specification.
|
||||
// The value of the NVIDIA_DRIVER_CAPABILITIES environment variable is checked to determine if this modification should be made.
|
||||
func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
||||
func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image image.CUDA, driver *root.Driver) (oci.SpecModifier, error) {
|
||||
if required, reason := requiresGraphicsModifier(image); !required {
|
||||
logger.Infof("No graphics modifier required: %v", reason)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: We should not just pass `nil` as the search path here.
|
||||
driver := root.New(logger, cfg.NVIDIAContainerCLIConfig.Root, nil)
|
||||
nvidiaCTKPath := cfg.NVIDIACTKConfig.Path
|
||||
nvidiaCDIHookPath := cfg.NVIDIACTKConfig.Path
|
||||
|
||||
mounts, err := discover.NewGraphicsMountsDiscoverer(
|
||||
logger,
|
||||
driver,
|
||||
nvidiaCTKPath,
|
||||
nvidiaCDIHookPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create mounts discoverer: %v", err)
|
||||
@@ -54,7 +52,7 @@ func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image imag
|
||||
logger,
|
||||
image.DevicesFromEnvvars(visibleDevicesEnvvar),
|
||||
devRoot,
|
||||
nvidiaCTKPath,
|
||||
nvidiaCDIHookPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer: %v", err)
|
||||
|
||||
@@ -90,10 +90,9 @@ func TestDiscovererFromCSVFiles(t *testing.T) {
|
||||
expectedHooks: []discover.Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk",
|
||||
"hook",
|
||||
"nvidia-cdi-hook",
|
||||
"create-symlinks",
|
||||
"--link",
|
||||
"/usr/lib/aarch64-linux-gnu/tegra/libv4l2_nvargus.so::/usr/lib/aarch64-linux-gnu/libv4l/plugins/nv/libv4l2_nvargus.so",
|
||||
@@ -147,10 +146,9 @@ func TestDiscovererFromCSVFiles(t *testing.T) {
|
||||
expectedHooks: []discover.Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk",
|
||||
"hook",
|
||||
"nvidia-cdi-hook",
|
||||
"create-symlinks",
|
||||
"--link",
|
||||
"/usr/lib/aarch64-linux-gnu/tegra/libv4l2_nvargus.so::/usr/lib/aarch64-linux-gnu/libv4l/plugins/nv/libv4l2_nvargus.so",
|
||||
@@ -189,7 +187,7 @@ func TestDiscovererFromCSVFiles(t *testing.T) {
|
||||
|
||||
o := tegraOptions{
|
||||
logger: logger,
|
||||
nvidiaCTKPath: "/usr/bin/nvidia-ctk",
|
||||
nvidiaCDIHookPath: "/usr/bin/nvidia-cdi-hook",
|
||||
csvFiles: []string{"dummy"},
|
||||
ignorePatterns: tc.ignorePatterns,
|
||||
symlinkLocator: tc.symlinkLocator,
|
||||
|
||||
@@ -28,10 +28,10 @@ import (
|
||||
|
||||
type symlinkHook struct {
|
||||
discover.None
|
||||
logger logger.Interface
|
||||
nvidiaCTKPath string
|
||||
targets []string
|
||||
mountsFrom discover.Discover
|
||||
logger logger.Interface
|
||||
nvidiaCDIHookPath string
|
||||
targets []string
|
||||
mountsFrom discover.Discover
|
||||
|
||||
// The following can be overridden for testing
|
||||
symlinkChainLocator lookup.Locator
|
||||
@@ -42,7 +42,7 @@ type symlinkHook struct {
|
||||
func (o tegraOptions) createCSVSymlinkHooks(targets []string, mounts discover.Discover) discover.Discover {
|
||||
return symlinkHook{
|
||||
logger: o.logger,
|
||||
nvidiaCTKPath: o.nvidiaCTKPath,
|
||||
nvidiaCDIHookPath: o.nvidiaCDIHookPath,
|
||||
targets: targets,
|
||||
mountsFrom: mounts,
|
||||
symlinkChainLocator: o.symlinkChainLocator,
|
||||
@@ -60,7 +60,7 @@ func (d symlinkHook) Hooks() ([]discover.Hook, error) {
|
||||
csvSymlinks := d.getCSVFileSymlinks()
|
||||
|
||||
return discover.CreateCreateSymlinkHook(
|
||||
d.nvidiaCTKPath,
|
||||
d.nvidiaCDIHookPath,
|
||||
append(csvSymlinks, specificLinks...),
|
||||
).Hooks()
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type tegraOptions struct {
|
||||
csvFiles []string
|
||||
driverRoot string
|
||||
devRoot string
|
||||
nvidiaCTKPath string
|
||||
nvidiaCDIHookPath string
|
||||
ldconfigPath string
|
||||
librarySearchPaths []string
|
||||
ignorePatterns ignoreMountSpecPatterns
|
||||
@@ -80,7 +80,7 @@ func New(opts ...Option) (discover.Discover, error) {
|
||||
return nil, fmt.Errorf("failed to create CSV discoverer: %v", err)
|
||||
}
|
||||
|
||||
ldcacheUpdateHook, err := discover.NewLDCacheUpdateHook(o.logger, csvDiscoverer, o.nvidiaCTKPath, o.ldconfigPath)
|
||||
ldcacheUpdateHook, err := discover.NewLDCacheUpdateHook(o.logger, csvDiscoverer, o.nvidiaCDIHookPath, o.ldconfigPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ldcach update hook discoverer: %v", err)
|
||||
}
|
||||
@@ -133,10 +133,10 @@ func WithCSVFiles(csvFiles []string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithNVIDIACTKPath sets the path to the nvidia-container-toolkit binary.
|
||||
func WithNVIDIACTKPath(nvidiaCTKPath string) Option {
|
||||
// WithNVIDIACDIHookPath sets the path to the nvidia-cdi-hook binary.
|
||||
func WithNVIDIACDIHookPath(nvidiaCDIHookPath string) Option {
|
||||
return func(o *tegraOptions) {
|
||||
o.nvidiaCTKPath = nvidiaCTKPath
|
||||
o.nvidiaCDIHookPath = nvidiaCDIHookPath
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
)
|
||||
|
||||
// Run is an entry point that allows for idiomatic handling of errors
|
||||
@@ -65,6 +66,7 @@ func (r rt) Run(argv []string) (rerr error) {
|
||||
if r.modeOverride != "" {
|
||||
cfg.NVIDIAContainerRuntimeConfig.Mode = r.modeOverride
|
||||
}
|
||||
//nolint:staticcheck // TODO(elezar): We should swith the nvidia-container-runtime from using nvidia-ctk to using nvidia-cdi-hook.
|
||||
cfg.NVIDIACTKConfig.Path = config.ResolveNVIDIACTKPath(r.logger, cfg.NVIDIACTKConfig.Path)
|
||||
cfg.NVIDIAContainerRuntimeHookConfig.Path = config.ResolveNVIDIAContainerRuntimeHookPath(r.logger, cfg.NVIDIAContainerRuntimeHookConfig.Path)
|
||||
|
||||
@@ -76,8 +78,13 @@ func (r rt) Run(argv []string) (rerr error) {
|
||||
r.logger.Infof("Running with config:\n%+v", cfg)
|
||||
}
|
||||
|
||||
driver := root.New(
|
||||
root.WithLogger(r.logger),
|
||||
root.WithDriverRoot(cfg.NVIDIAContainerCLIConfig.Root),
|
||||
)
|
||||
|
||||
r.logger.Debugf("Command line arguments: %v", argv)
|
||||
runtime, err := newNVIDIAContainerRuntime(r.logger, cfg, argv)
|
||||
runtime, err := newNVIDIAContainerRuntime(r.logger, cfg, argv, driver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create NVIDIA Container Runtime: %v", err)
|
||||
}
|
||||
|
||||
@@ -23,12 +23,13 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/modifier"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
// newNVIDIAContainerRuntime is a factory method that constructs a runtime based on the selected configuration and specified logger
|
||||
func newNVIDIAContainerRuntime(logger logger.Interface, cfg *config.Config, argv []string) (oci.Runtime, error) {
|
||||
func newNVIDIAContainerRuntime(logger logger.Interface, cfg *config.Config, argv []string, driver *root.Driver) (oci.Runtime, error) {
|
||||
lowLevelRuntime, err := oci.NewLowLevelRuntime(logger, cfg.NVIDIAContainerRuntimeConfig.Runtimes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error constructing low-level runtime: %v", err)
|
||||
@@ -44,7 +45,7 @@ func newNVIDIAContainerRuntime(logger logger.Interface, cfg *config.Config, argv
|
||||
return nil, fmt.Errorf("error constructing OCI specification: %v", err)
|
||||
}
|
||||
|
||||
specModifier, err := newSpecModifier(logger, cfg, ociSpec)
|
||||
specModifier, err := newSpecModifier(logger, cfg, ociSpec, driver)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct OCI spec modifier: %v", err)
|
||||
}
|
||||
@@ -61,7 +62,7 @@ func newNVIDIAContainerRuntime(logger logger.Interface, cfg *config.Config, argv
|
||||
}
|
||||
|
||||
// newSpecModifier is a factory method that creates constructs an OCI spec modifer based on the provided config.
|
||||
func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Spec) (oci.SpecModifier, error) {
|
||||
func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Spec, driver *root.Driver) (oci.SpecModifier, error) {
|
||||
rawSpec, err := ociSpec.Load()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load OCI spec: %v", err)
|
||||
@@ -82,7 +83,7 @@ func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Sp
|
||||
return modeModifier, nil
|
||||
}
|
||||
|
||||
graphicsModifier, err := modifier.NewGraphicsModifier(logger, cfg, image)
|
||||
graphicsModifier, err := modifier.NewGraphicsModifier(logger, cfg, image, driver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
@@ -63,6 +64,9 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestFactoryMethod(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
driver := root.New(
|
||||
root.WithDriverRoot("/nvidia/driver/root"),
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
@@ -143,6 +147,7 @@ func TestFactoryMethod(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
|
||||
bundleDir := t.TempDir()
|
||||
|
||||
specFile, err := os.Create(filepath.Join(bundleDir, "config.json"))
|
||||
@@ -151,7 +156,7 @@ func TestFactoryMethod(t *testing.T) {
|
||||
|
||||
argv := []string{"--bundle", bundleDir, "create"}
|
||||
|
||||
_, err = newNVIDIAContainerRuntime(logger, tc.cfg, argv)
|
||||
_, err = newNVIDIAContainerRuntime(logger, tc.cfg, argv, driver)
|
||||
if tc.expectedError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
nvidia-container-runtime /usr/bin
|
||||
nvidia-ctk /usr/bin
|
||||
nvidia-cdi-hook /usr/bin
|
||||
|
||||
@@ -16,6 +16,7 @@ Source2: LICENSE
|
||||
Source3: nvidia-container-runtime
|
||||
Source4: nvidia-container-runtime.cdi
|
||||
Source5: nvidia-container-runtime.legacy
|
||||
Source6: nvidia-cdi-hook
|
||||
|
||||
Obsoletes: nvidia-container-runtime <= 3.5.0-1, nvidia-container-runtime-hook <= 1.4.0-2
|
||||
Provides: nvidia-container-runtime
|
||||
@@ -27,7 +28,7 @@ Requires: nvidia-container-toolkit-base == %{version}-%{release}
|
||||
Provides tools and utilities to enable GPU support in containers.
|
||||
|
||||
%prep
|
||||
cp %{SOURCE0} %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} %{SOURCE5} .
|
||||
cp %{SOURCE0} %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} %{SOURCE5} %{SOURCE6} .
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}%{_bindir}
|
||||
@@ -36,6 +37,7 @@ install -m 755 -t %{buildroot}%{_bindir} nvidia-container-runtime
|
||||
install -m 755 -t %{buildroot}%{_bindir} nvidia-container-runtime.cdi
|
||||
install -m 755 -t %{buildroot}%{_bindir} nvidia-container-runtime.legacy
|
||||
install -m 755 -t %{buildroot}%{_bindir} nvidia-ctk
|
||||
install -m 755 -t %{buildroot}%{_bindir} nvidia-cdi-hook
|
||||
|
||||
%post
|
||||
if [ $1 -gt 1 ]; then # only on package upgrade
|
||||
@@ -86,6 +88,7 @@ Provides tools such as the NVIDIA Container Runtime and NVIDIA Container Toolkit
|
||||
%license LICENSE
|
||||
%{_bindir}/nvidia-container-runtime
|
||||
%{_bindir}/nvidia-ctk
|
||||
%{_bindir}/nvidia-cdi-hook
|
||||
|
||||
# The OPERATOR EXTENSIONS package consists of components that are required to enable GPU support in Kubernetes.
|
||||
# This package is not distributed as part of the NVIDIA Container Toolkit RPMs.
|
||||
|
||||
@@ -19,7 +19,7 @@ package engine
|
||||
// Interface defines the API for a runtime config updater.
|
||||
type Interface interface {
|
||||
DefaultRuntime() string
|
||||
AddRuntime(string, string, bool) error
|
||||
AddRuntime(string, string, bool, ...map[string]interface{}) error
|
||||
Set(string, interface{})
|
||||
RemoveRuntime(string) error
|
||||
Save(string) (int64, error)
|
||||
|
||||
@@ -30,7 +30,7 @@ type ConfigV1 Config
|
||||
var _ engine.Interface = (*ConfigV1)(nil)
|
||||
|
||||
// AddRuntime adds a runtime to the containerd config
|
||||
func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool, configOverrides ...map[string]interface{}) error {
|
||||
if c == nil || c.Tree == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
@@ -75,6 +75,16 @@ func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error
|
||||
}
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "Runtime"}, path)
|
||||
|
||||
defaultRuntimeSubtree := subtreeAtPath(config, "plugins", "cri", "containerd", "default_runtime")
|
||||
if err := defaultRuntimeSubtree.applyOverrides(configOverrides...); err != nil {
|
||||
return fmt.Errorf("failed to apply config overrides to default_runtime: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
runtimeSubtree := subtreeAtPath(config, "plugins", "cri", "containerd", "runtimes", name)
|
||||
if err := runtimeSubtree.applyOverrides(configOverrides...); err != nil {
|
||||
return fmt.Errorf("failed to apply config overrides: %w", err)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
// AddRuntime adds a runtime to the containerd config
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool, configOverrides ...map[string]interface{}) error {
|
||||
if c == nil || c.Tree == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
@@ -60,6 +60,11 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}, name)
|
||||
}
|
||||
|
||||
runtimeSubtree := subtreeAtPath(config, "plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name)
|
||||
if err := runtimeSubtree.applyOverrides(configOverrides...); err != nil {
|
||||
return fmt.Errorf("failed to apply config overrides: %w", err)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
return nil
|
||||
}
|
||||
|
||||
97
pkg/config/engine/containerd/config_v2_test.go
Normal file
97
pkg/config/engine/containerd/config_v2_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 containerd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddRuntime(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
configOverrides []map[string]interface{}
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "empty config not default runtime",
|
||||
expectedConfig: `
|
||||
version = 2
|
||||
[plugins]
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
`,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "empty config not default runtime with overrides",
|
||||
configOverrides: []map[string]interface{}{
|
||||
{
|
||||
"options": map[string]interface{}{
|
||||
"SystemdCgroup": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfig: `
|
||||
version = 2
|
||||
[plugins]
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
config, err := toml.Load(tc.config)
|
||||
require.NoError(t, err)
|
||||
expectedConfig, err := toml.Load(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &Config{
|
||||
Tree: config,
|
||||
}
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault, tc.configOverrides...)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), config.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
56
pkg/config/engine/containerd/toml.go
Normal file
56
pkg/config/engine/containerd/toml.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 containerd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
// tomlTree is an alias for toml.Tree that allows for extensions.
|
||||
type tomlTree toml.Tree
|
||||
|
||||
func subtreeAtPath(c toml.Tree, path ...string) *tomlTree {
|
||||
tree := c.GetPath(path).(*toml.Tree)
|
||||
return (*tomlTree)(tree)
|
||||
}
|
||||
|
||||
func (t *tomlTree) insert(other map[string]interface{}) error {
|
||||
|
||||
for key, value := range other {
|
||||
if insertsubtree, ok := value.(map[string]interface{}); ok {
|
||||
subtree := (*toml.Tree)(t).Get(key).(*toml.Tree)
|
||||
return (*tomlTree)(subtree).insert(insertsubtree)
|
||||
}
|
||||
(*toml.Tree)(t).Set(key, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tomlTree) applyOverrides(overrides ...map[string]interface{}) error {
|
||||
for _, override := range overrides {
|
||||
subconfig, err := toml.TreeFromMap(override)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid toml config: %w", err)
|
||||
}
|
||||
if err := t.insert(subconfig.ToMap()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func New(opts ...Option) (engine.Interface, error) {
|
||||
}
|
||||
|
||||
// AddRuntime adds a new runtime to the crio config
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool, _ ...map[string]interface{}) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func New(opts ...Option) (engine.Interface, error) {
|
||||
}
|
||||
|
||||
// AddRuntime adds a new runtime to the docker config
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool, _ ...map[string]interface{}) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ func (l *nvmllib) newCommonNVMLDiscoverer() (discover.Discover, error) {
|
||||
},
|
||||
)
|
||||
|
||||
graphicsMounts, err := discover.NewGraphicsMountsDiscoverer(l.logger, l.driver, l.nvidiaCTKPath)
|
||||
graphicsMounts, err := discover.NewGraphicsMountsDiscoverer(l.logger, l.driver, l.nvidiaCDIHookPath)
|
||||
if err != nil {
|
||||
l.logger.Warningf("failed to create discoverer for graphics mounts: %v", err)
|
||||
}
|
||||
|
||||
driverFiles, err := NewDriverDiscoverer(l.logger, l.driver, l.nvidiaCTKPath, l.ldconfigPath, l.nvmllib)
|
||||
driverFiles, err := NewDriverDiscoverer(l.logger, l.driver, l.nvidiaCDIHookPath, l.ldconfigPath, l.nvmllib)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create discoverer for driver files: %v", err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
|
||||
// NewDriverDiscoverer creates a discoverer for the libraries and binaries associated with a driver installation.
|
||||
// The supplied NVML Library is used to query the expected driver version.
|
||||
func NewDriverDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPath string, ldconfigPath string, nvmllib nvml.Interface) (discover.Discover, error) {
|
||||
func NewDriverDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCDIHookPath string, ldconfigPath string, nvmllib nvml.Interface) (discover.Discover, error) {
|
||||
if r := nvmllib.Init(); r != nvml.SUCCESS {
|
||||
return nil, fmt.Errorf("failed to initialize NVML: %v", r)
|
||||
}
|
||||
@@ -49,11 +49,11 @@ func NewDriverDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTK
|
||||
return nil, fmt.Errorf("failed to determine driver version: %v", r)
|
||||
}
|
||||
|
||||
return newDriverVersionDiscoverer(logger, driver, nvidiaCTKPath, ldconfigPath, version)
|
||||
return newDriverVersionDiscoverer(logger, driver, nvidiaCDIHookPath, ldconfigPath, version)
|
||||
}
|
||||
|
||||
func newDriverVersionDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPath, ldconfigPath, version string) (discover.Discover, error) {
|
||||
libraries, err := NewDriverLibraryDiscoverer(logger, driver, nvidiaCTKPath, ldconfigPath, version)
|
||||
func newDriverVersionDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCDIHookPath, ldconfigPath, version string) (discover.Discover, error) {
|
||||
libraries, err := NewDriverLibraryDiscoverer(logger, driver, nvidiaCDIHookPath, ldconfigPath, version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create discoverer for driver libraries: %v", err)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func newDriverVersionDiscoverer(logger logger.Interface, driver *root.Driver, nv
|
||||
}
|
||||
|
||||
// NewDriverLibraryDiscoverer creates a discoverer for the libraries associated with the specified driver version.
|
||||
func NewDriverLibraryDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCTKPath, ldconfigPath, version string) (discover.Discover, error) {
|
||||
func NewDriverLibraryDiscoverer(logger logger.Interface, driver *root.Driver, nvidiaCDIHookPath, ldconfigPath, version string) (discover.Discover, error) {
|
||||
libraryPaths, err := getVersionLibs(logger, driver, version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get libraries for driver version: %v", err)
|
||||
@@ -97,7 +97,7 @@ func NewDriverLibraryDiscoverer(logger logger.Interface, driver *root.Driver, nv
|
||||
libraryPaths,
|
||||
)
|
||||
|
||||
hooks, _ := discover.NewLDCacheUpdateHook(logger, libraries, nvidiaCTKPath, ldconfigPath)
|
||||
hooks, _ := discover.NewLDCacheUpdateHook(logger, libraries, nvidiaCDIHookPath, ldconfigPath)
|
||||
|
||||
d := discover.Merge(
|
||||
libraries,
|
||||
|
||||
@@ -39,7 +39,7 @@ var requiredDriverStoreFiles = []string{
|
||||
}
|
||||
|
||||
// newWSLDriverDiscoverer returns a Discoverer for WSL2 drivers.
|
||||
func newWSLDriverDiscoverer(logger logger.Interface, driverRoot string, nvidiaCTKPath, ldconfigPath string) (discover.Discover, error) {
|
||||
func newWSLDriverDiscoverer(logger logger.Interface, driverRoot string, nvidiaCDIHookPath, ldconfigPath string) (discover.Discover, error) {
|
||||
err := dxcore.Init()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize dxcore: %v", err)
|
||||
@@ -56,11 +56,11 @@ func newWSLDriverDiscoverer(logger logger.Interface, driverRoot string, nvidiaCT
|
||||
}
|
||||
logger.Infof("Using WSL driver store paths: %v", driverStorePaths)
|
||||
|
||||
return newWSLDriverStoreDiscoverer(logger, driverRoot, nvidiaCTKPath, ldconfigPath, driverStorePaths)
|
||||
return newWSLDriverStoreDiscoverer(logger, driverRoot, nvidiaCDIHookPath, ldconfigPath, driverStorePaths)
|
||||
}
|
||||
|
||||
// newWSLDriverStoreDiscoverer returns a Discoverer for WSL2 drivers in the driver store associated with a dxcore adapter.
|
||||
func newWSLDriverStoreDiscoverer(logger logger.Interface, driverRoot string, nvidiaCTKPath string, ldconfigPath string, driverStorePaths []string) (discover.Discover, error) {
|
||||
func newWSLDriverStoreDiscoverer(logger logger.Interface, driverRoot string, nvidiaCDIHookPath string, ldconfigPath string, driverStorePaths []string) (discover.Discover, error) {
|
||||
var searchPaths []string
|
||||
seen := make(map[string]bool)
|
||||
for _, path := range driverStorePaths {
|
||||
@@ -88,12 +88,12 @@ func newWSLDriverStoreDiscoverer(logger logger.Interface, driverRoot string, nvi
|
||||
)
|
||||
|
||||
symlinkHook := nvidiaSMISimlinkHook{
|
||||
logger: logger,
|
||||
mountsFrom: libraries,
|
||||
nvidiaCTKPath: nvidiaCTKPath,
|
||||
logger: logger,
|
||||
mountsFrom: libraries,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
}
|
||||
|
||||
ldcacheHook, _ := discover.NewLDCacheUpdateHook(logger, libraries, nvidiaCTKPath, ldconfigPath)
|
||||
ldcacheHook, _ := discover.NewLDCacheUpdateHook(logger, libraries, nvidiaCDIHookPath, ldconfigPath)
|
||||
|
||||
d := discover.Merge(
|
||||
libraries,
|
||||
@@ -106,9 +106,9 @@ func newWSLDriverStoreDiscoverer(logger logger.Interface, driverRoot string, nvi
|
||||
|
||||
type nvidiaSMISimlinkHook struct {
|
||||
discover.None
|
||||
logger logger.Interface
|
||||
mountsFrom discover.Discover
|
||||
nvidiaCTKPath string
|
||||
logger logger.Interface
|
||||
mountsFrom discover.Discover
|
||||
nvidiaCDIHookPath string
|
||||
}
|
||||
|
||||
// Hooks returns a hook that creates a symlink to nvidia-smi in the driver store.
|
||||
@@ -135,7 +135,7 @@ func (m nvidiaSMISimlinkHook) Hooks() ([]discover.Hook, error) {
|
||||
}
|
||||
link := "/usr/bin/nvidia-smi"
|
||||
links := []string{fmt.Sprintf("%s::%s", target, link)}
|
||||
symlinkHook := discover.CreateCreateSymlinkHook(m.nvidiaCTKPath, links)
|
||||
symlinkHook := discover.CreateCreateSymlinkHook(m.nvidiaCDIHookPath, links)
|
||||
|
||||
return symlinkHook.Hooks()
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ func TestNvidiaSMISymlinkHook(t *testing.T) {
|
||||
expectedHooks: []discover.Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "create-symlinks",
|
||||
Path: "nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks",
|
||||
"--link", "nvidia-smi::/usr/bin/nvidia-smi"},
|
||||
},
|
||||
},
|
||||
@@ -112,8 +112,8 @@ func TestNvidiaSMISymlinkHook(t *testing.T) {
|
||||
expectedHooks: []discover.Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "create-symlinks",
|
||||
Path: "nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks",
|
||||
"--link", "/some/path/nvidia-smi::/usr/bin/nvidia-smi"},
|
||||
},
|
||||
},
|
||||
@@ -132,8 +132,8 @@ func TestNvidiaSMISymlinkHook(t *testing.T) {
|
||||
expectedHooks: []discover.Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "create-symlinks",
|
||||
Path: "nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks",
|
||||
"--link", "/some/path/nvidia-smi::/usr/bin/nvidia-smi"},
|
||||
},
|
||||
},
|
||||
@@ -143,9 +143,9 @@ func TestNvidiaSMISymlinkHook(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
m := nvidiaSMISimlinkHook{
|
||||
logger: logger,
|
||||
mountsFrom: tc.mounts,
|
||||
nvidiaCTKPath: "nvidia-ctk",
|
||||
logger: logger,
|
||||
mountsFrom: tc.mounts,
|
||||
nvidiaCDIHookPath: "nvidia-cdi-hook",
|
||||
}
|
||||
|
||||
devices, err := m.Devices()
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
|
||||
@@ -58,7 +58,7 @@ func (l *nvmllib) GetGPUDeviceSpecs(i int, d device.Device) ([]specs.Device, err
|
||||
|
||||
// GetGPUDeviceEdits returns the CDI edits for the full GPU represented by 'device'.
|
||||
func (l *nvmllib) GetGPUDeviceEdits(d device.Device) (*cdi.ContainerEdits, error) {
|
||||
device, err := newFullGPUDiscoverer(l.logger, l.devRoot, l.nvidiaCTKPath, d)
|
||||
device, err := newFullGPUDiscoverer(l.logger, l.devRoot, l.nvidiaCDIHookPath, d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create device discoverer: %v", err)
|
||||
}
|
||||
@@ -73,17 +73,17 @@ func (l *nvmllib) GetGPUDeviceEdits(d device.Device) (*cdi.ContainerEdits, error
|
||||
|
||||
// byPathHookDiscoverer discovers the entities required for injecting by-path DRM device links
|
||||
type byPathHookDiscoverer struct {
|
||||
logger logger.Interface
|
||||
devRoot string
|
||||
nvidiaCTKPath string
|
||||
pciBusID string
|
||||
deviceNodes discover.Discover
|
||||
logger logger.Interface
|
||||
devRoot string
|
||||
nvidiaCDIHookPath string
|
||||
pciBusID string
|
||||
deviceNodes discover.Discover
|
||||
}
|
||||
|
||||
var _ discover.Discover = (*byPathHookDiscoverer)(nil)
|
||||
|
||||
// newFullGPUDiscoverer creates a discoverer for the full GPU defined by the specified device.
|
||||
func newFullGPUDiscoverer(logger logger.Interface, devRoot string, nvidiaCTKPath string, d device.Device) (discover.Discover, error) {
|
||||
func newFullGPUDiscoverer(logger logger.Interface, devRoot string, nvidiaCDIHookPath string, d device.Device) (discover.Discover, error) {
|
||||
// TODO: The functionality to get device paths should be integrated into the go-nvlib/pkg/device.Device interface.
|
||||
// This will allow reuse here and in other code where the paths are queried such as the NVIDIA device plugin.
|
||||
minor, ret := d.GetMinorNumber()
|
||||
@@ -112,17 +112,17 @@ func newFullGPUDiscoverer(logger logger.Interface, devRoot string, nvidiaCTKPath
|
||||
)
|
||||
|
||||
byPathHooks := &byPathHookDiscoverer{
|
||||
logger: logger,
|
||||
devRoot: devRoot,
|
||||
nvidiaCTKPath: nvidiaCTKPath,
|
||||
pciBusID: pciBusID,
|
||||
deviceNodes: deviceNodes,
|
||||
logger: logger,
|
||||
devRoot: devRoot,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
pciBusID: pciBusID,
|
||||
deviceNodes: deviceNodes,
|
||||
}
|
||||
|
||||
deviceFolderPermissionHooks := newDeviceFolderPermissionHookDiscoverer(
|
||||
logger,
|
||||
devRoot,
|
||||
nvidiaCTKPath,
|
||||
nvidiaCDIHookPath,
|
||||
deviceNodes,
|
||||
)
|
||||
|
||||
@@ -157,8 +157,8 @@ func (d *byPathHookDiscoverer) Hooks() ([]discover.Hook, error) {
|
||||
args = append(args, "--link", l)
|
||||
}
|
||||
|
||||
hook := discover.CreateNvidiaCTKHook(
|
||||
d.nvidiaCTKPath,
|
||||
hook := discover.CreateNvidiaCDIHook(
|
||||
d.nvidiaCDIHookPath,
|
||||
"create-symlinks",
|
||||
args...,
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ func (l *csvlib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
||||
tegra.WithLogger(l.logger),
|
||||
tegra.WithDriverRoot(l.driverRoot),
|
||||
tegra.WithDevRoot(l.devRoot),
|
||||
tegra.WithNVIDIACTKPath(l.nvidiaCTKPath),
|
||||
tegra.WithNVIDIACDIHookPath(l.nvidiaCDIHookPath),
|
||||
tegra.WithLdconfigPath(l.ldconfigPath),
|
||||
tegra.WithCSVFiles(l.csvFiles),
|
||||
tegra.WithLibrarySearchPaths(l.librarySearchPaths...),
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
|
||||
@@ -80,7 +80,17 @@ func (l *nvmllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
||||
// GetDeviceSpecsByID returns the CDI device specs for the GPU(s) represented by
|
||||
// the provided identifiers, where an identifier is an index or UUID of a valid
|
||||
// GPU device.
|
||||
func (l *nvmllib) GetDeviceSpecsByID(identifiers ...string) ([]specs.Device, error) {
|
||||
// Deprecated: Use GetDeviceSpecsBy instead.
|
||||
func (l *nvmllib) GetDeviceSpecsByID(ids ...string) ([]specs.Device, error) {
|
||||
var identifiers []device.Identifier
|
||||
for _, id := range ids {
|
||||
identifiers = append(identifiers, device.Identifier(id))
|
||||
}
|
||||
return l.GetDeviceSpecsBy(identifiers...)
|
||||
}
|
||||
|
||||
// GetDeviceSpecsBy is not supported for the gdslib specs.
|
||||
func (l *nvmllib) GetDeviceSpecsBy(identifiers ...device.Identifier) ([]specs.Device, error) {
|
||||
for _, id := range identifiers {
|
||||
if id == "all" {
|
||||
return l.GetAllDeviceSpecs()
|
||||
@@ -109,7 +119,7 @@ func (l *nvmllib) GetDeviceSpecsByID(identifiers ...string) ([]specs.Device, err
|
||||
return nil, fmt.Errorf("failed to get CDI device edits for identifier %q: %w", identifiers[i], err)
|
||||
}
|
||||
deviceSpec := specs.Device{
|
||||
Name: identifiers[i],
|
||||
Name: string(identifiers[i]),
|
||||
ContainerEdits: *deviceEdits.ContainerEdits,
|
||||
}
|
||||
deviceSpecs = append(deviceSpecs, deviceSpec)
|
||||
@@ -119,7 +129,7 @@ func (l *nvmllib) GetDeviceSpecsByID(identifiers ...string) ([]specs.Device, err
|
||||
}
|
||||
|
||||
// TODO: move this to go-nvlib?
|
||||
func (l *nvmllib) getNVMLDevicesByID(identifiers ...string) ([]nvml.Device, error) {
|
||||
func (l *nvmllib) getNVMLDevicesByID(identifiers ...device.Identifier) ([]nvml.Device, error) {
|
||||
var devices []nvml.Device
|
||||
for _, id := range identifiers {
|
||||
dev, err := l.getNVMLDeviceByID(id)
|
||||
@@ -131,25 +141,24 @@ func (l *nvmllib) getNVMLDevicesByID(identifiers ...string) ([]nvml.Device, erro
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (l *nvmllib) getNVMLDeviceByID(id string) (nvml.Device, error) {
|
||||
func (l *nvmllib) getNVMLDeviceByID(id device.Identifier) (nvml.Device, error) {
|
||||
var err error
|
||||
devID := device.Identifier(id)
|
||||
|
||||
if devID.IsUUID() {
|
||||
return l.nvmllib.DeviceGetHandleByUUID(id)
|
||||
if id.IsUUID() {
|
||||
return l.nvmllib.DeviceGetHandleByUUID(string(id))
|
||||
}
|
||||
|
||||
if devID.IsGpuIndex() {
|
||||
if idx, err := strconv.Atoi(id); err == nil {
|
||||
if id.IsGpuIndex() {
|
||||
if idx, err := strconv.Atoi(string(id)); err == nil {
|
||||
return l.nvmllib.DeviceGetHandleByIndex(idx)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to convert device index to an int: %w", err)
|
||||
}
|
||||
|
||||
if devID.IsMigIndex() {
|
||||
if id.IsMigIndex() {
|
||||
var gpuIdx, migIdx int
|
||||
var parent nvml.Device
|
||||
split := strings.SplitN(id, ":", 2)
|
||||
split := strings.SplitN(string(id), ":", 2)
|
||||
if gpuIdx, err = strconv.Atoi(split[0]); err != nil {
|
||||
return nil, fmt.Errorf("failed to convert device index to an int: %w", err)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (l *wsllib) GetAllDeviceSpecs() ([]specs.Device, error) {
|
||||
|
||||
// GetCommonEdits generates a CDI specification that can be used for ANY devices
|
||||
func (l *wsllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
||||
driver, err := newWSLDriverDiscoverer(l.logger, l.driverRoot, l.nvidiaCTKPath, l.ldconfigPath)
|
||||
driver, err := newWSLDriverDiscoverer(l.logger, l.driverRoot, l.nvidiaCDIHookPath, l.ldconfigPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create discoverer for WSL driver: %v", err)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/info"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
@@ -48,8 +48,9 @@ type nvcdilib struct {
|
||||
deviceNamers DeviceNamers
|
||||
driverRoot string
|
||||
devRoot string
|
||||
nvidiaCTKPath string
|
||||
nvidiaCDIHookPath string
|
||||
ldconfigPath string
|
||||
configSearchPaths []string
|
||||
librarySearchPaths []string
|
||||
|
||||
csvFiles []string
|
||||
@@ -80,22 +81,44 @@ func New(opts ...Option) (Interface, error) {
|
||||
indexNamer, _ := NewDeviceNamer(DeviceNameStrategyIndex)
|
||||
l.deviceNamers = []DeviceNamer{indexNamer}
|
||||
}
|
||||
if l.nvidiaCDIHookPath == "" {
|
||||
l.nvidiaCDIHookPath = "/usr/bin/nvidia-cdi-hook"
|
||||
}
|
||||
if l.driverRoot == "" {
|
||||
l.driverRoot = "/"
|
||||
}
|
||||
if l.devRoot == "" {
|
||||
l.devRoot = l.driverRoot
|
||||
}
|
||||
if l.nvidiaCTKPath == "" {
|
||||
l.nvidiaCTKPath = "/usr/bin/nvidia-ctk"
|
||||
l.driver = root.New(
|
||||
root.WithLogger(l.logger),
|
||||
root.WithDriverRoot(l.driverRoot),
|
||||
root.WithLibrarySearchPaths(l.librarySearchPaths...),
|
||||
)
|
||||
if l.nvmllib == nil {
|
||||
var nvmlOpts []nvml.LibraryOption
|
||||
candidates, err := l.driver.Libraries().Locate("libnvidia-ml.so.1")
|
||||
if err != nil {
|
||||
l.logger.Warningf("Ignoring error in locating libnvidia-ml.so.1: %v", err)
|
||||
} else {
|
||||
libNvidiaMlPath := candidates[0]
|
||||
l.logger.Infof("Using %v", libNvidiaMlPath)
|
||||
nvmlOpts = append(nvmlOpts, nvml.WithLibraryPath(libNvidiaMlPath))
|
||||
}
|
||||
l.nvmllib = nvml.New(nvmlOpts...)
|
||||
}
|
||||
if l.devicelib == nil {
|
||||
l.devicelib = device.New(l.nvmllib)
|
||||
}
|
||||
if l.infolib == nil {
|
||||
l.infolib = info.New()
|
||||
l.infolib = info.New(
|
||||
info.WithRoot(l.driverRoot),
|
||||
info.WithLogger(l.logger),
|
||||
info.WithNvmlLib(l.nvmllib),
|
||||
info.WithDeviceLib(l.devicelib),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: We need to improve the construction of this driver root.
|
||||
l.driver = root.New(l.logger, l.driverRoot, l.librarySearchPaths)
|
||||
|
||||
var lib Interface
|
||||
switch l.resolveMode() {
|
||||
case ModeCSV:
|
||||
@@ -109,13 +132,6 @@ func New(opts ...Option) (Interface, error) {
|
||||
}
|
||||
lib = (*managementlib)(l)
|
||||
case ModeNvml:
|
||||
if l.nvmllib == nil {
|
||||
l.nvmllib = nvml.New()
|
||||
}
|
||||
if l.devicelib == nil {
|
||||
l.devicelib = device.New(device.WithNvml(l.nvmllib))
|
||||
}
|
||||
|
||||
lib = (*nvmllib)(l)
|
||||
case ModeWsl:
|
||||
lib = (*wsllib)(l)
|
||||
@@ -180,26 +196,19 @@ func (l *nvcdilib) resolveMode() (rmode string) {
|
||||
return l.mode
|
||||
}
|
||||
defer func() {
|
||||
l.logger.Infof("Auto-detected mode as %q", rmode)
|
||||
l.logger.Infof("Auto-detected mode as '%v'", rmode)
|
||||
}()
|
||||
|
||||
isWSL, reason := l.infolib.HasDXCore()
|
||||
l.logger.Debugf("Is WSL-based system? %v: %v", isWSL, reason)
|
||||
|
||||
if isWSL {
|
||||
platform := l.infolib.ResolvePlatform()
|
||||
switch platform {
|
||||
case info.PlatformNVML:
|
||||
return ModeNvml
|
||||
case info.PlatformTegra:
|
||||
return ModeCSV
|
||||
case info.PlatformWSL:
|
||||
return ModeWsl
|
||||
}
|
||||
|
||||
isNvml, reason := l.infolib.HasNvml()
|
||||
l.logger.Debugf("Is NVML-based system? %v: %v", isNvml, reason)
|
||||
|
||||
isTegra, reason := l.infolib.IsTegraSystem()
|
||||
l.logger.Debugf("Is Tegra-based system? %v: %v", isTegra, reason)
|
||||
|
||||
if isTegra && !isNvml {
|
||||
return ModeCSV
|
||||
}
|
||||
|
||||
l.logger.Warningf("Unsupported platform detected: %v; assuming %v", platform, ModeNvml)
|
||||
return ModeNvml
|
||||
}
|
||||
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
# Copyright (c) NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# 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 nvcdi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestResolveMode(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testCases := []struct {
|
||||
mode string
|
||||
isTegra bool
|
||||
hasDXCore bool
|
||||
hasNVML bool
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
mode: "auto",
|
||||
hasDXCore: true,
|
||||
expected: "wsl",
|
||||
},
|
||||
{
|
||||
mode: "auto",
|
||||
hasDXCore: false,
|
||||
isTegra: true,
|
||||
hasNVML: false,
|
||||
expected: "csv",
|
||||
},
|
||||
{
|
||||
mode: "auto",
|
||||
hasDXCore: false,
|
||||
isTegra: false,
|
||||
hasNVML: false,
|
||||
expected: "nvml",
|
||||
},
|
||||
{
|
||||
mode: "auto",
|
||||
hasDXCore: false,
|
||||
isTegra: true,
|
||||
hasNVML: true,
|
||||
expected: "nvml",
|
||||
},
|
||||
{
|
||||
mode: "auto",
|
||||
hasDXCore: false,
|
||||
isTegra: false,
|
||||
expected: "nvml",
|
||||
},
|
||||
{
|
||||
mode: "nvml",
|
||||
hasDXCore: true,
|
||||
isTegra: true,
|
||||
expected: "nvml",
|
||||
},
|
||||
{
|
||||
mode: "wsl",
|
||||
hasDXCore: false,
|
||||
expected: "wsl",
|
||||
},
|
||||
{
|
||||
mode: "not-auto",
|
||||
hasDXCore: true,
|
||||
expected: "not-auto",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) {
|
||||
l := nvcdilib{
|
||||
logger: logger,
|
||||
mode: tc.mode,
|
||||
infolib: infoMock{hasDXCore: tc.hasDXCore, isTegra: tc.isTegra, hasNVML: tc.hasNVML},
|
||||
}
|
||||
|
||||
require.Equal(t, tc.expected, l.resolveMode())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type infoMock struct {
|
||||
hasDXCore bool
|
||||
isTegra bool
|
||||
hasNVML bool
|
||||
}
|
||||
|
||||
func (i infoMock) HasDXCore() (bool, string) {
|
||||
return i.hasDXCore, ""
|
||||
}
|
||||
|
||||
func (i infoMock) HasNvml() (bool, string) {
|
||||
return i.hasNVML, ""
|
||||
}
|
||||
|
||||
func (i infoMock) IsTegraSystem() (bool, string) {
|
||||
return i.isTegra, ""
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func (m *managementlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
|
||||
return nil, fmt.Errorf("failed to get CUDA version: %v", err)
|
||||
}
|
||||
|
||||
driver, err := newDriverVersionDiscoverer(m.logger, m.driver, m.nvidiaCTKPath, m.ldconfigPath, version)
|
||||
driver, err := newDriverVersionDiscoverer(m.logger, m.driver, m.nvidiaCDIHookPath, m.ldconfigPath, version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create driver library discoverer: %v", err)
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func (m *managementlib) newManagementDeviceDiscoverer() (discover.Discover, erro
|
||||
deviceFolderPermissionHooks := newDeviceFolderPermissionHookDiscoverer(
|
||||
m.logger,
|
||||
m.devRoot,
|
||||
m.nvidiaCTKPath,
|
||||
m.nvidiaCDIHookPath,
|
||||
deviceNodes,
|
||||
)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
)
|
||||
|
||||
// UUIDer is an interface for getting UUIDs.
|
||||
|
||||
@@ -6,7 +6,7 @@ package nvcdi
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
)
|
||||
|
||||
// Ensure, that nvmlUUIDerMock does implement nvmlUUIDer.
|
||||
|
||||
@@ -19,7 +19,7 @@ package nvcdi
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ package nvcdi
|
||||
|
||||
import (
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/info"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
|
||||
@@ -34,6 +35,13 @@ func WithDeviceLib(devicelib device.Interface) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithInfoLib sets the info library for CDI spec generation.
|
||||
func WithInfoLib(infolib info.Interface) Option {
|
||||
return func(l *nvcdilib) {
|
||||
l.infolib = infolib
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeviceNamers sets the device namer for the library
|
||||
func WithDeviceNamers(namers ...DeviceNamer) Option {
|
||||
return func(l *nvcdilib) {
|
||||
@@ -63,9 +71,16 @@ func WithLogger(logger logger.Interface) Option {
|
||||
}
|
||||
|
||||
// WithNVIDIACTKPath sets the path to the NVIDIA Container Toolkit CLI path for the library
|
||||
//
|
||||
// Deprecated: Use WithNVIDIACDIHookPath instead.
|
||||
func WithNVIDIACTKPath(path string) Option {
|
||||
return WithNVIDIACDIHookPath(path)
|
||||
}
|
||||
|
||||
// WithNVIDIACDIHookPath sets the path to the NVIDIA Container Toolkit CLI path for the library
|
||||
func WithNVIDIACDIHookPath(path string) Option {
|
||||
return func(l *nvcdilib) {
|
||||
l.nvidiaCTKPath = path
|
||||
l.nvidiaCDIHookPath = path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +141,13 @@ func WithCSVIgnorePatterns(csvIgnorePatterns []string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigSearchPaths sets the search paths for config files.
|
||||
func WithConfigSearchPaths(paths []string) Option {
|
||||
return func(o *nvcdilib) {
|
||||
o.configSearchPaths = paths
|
||||
}
|
||||
}
|
||||
|
||||
// WithLibrarySearchPaths sets the library search paths.
|
||||
// This is currently only used for CSV-mode.
|
||||
func WithLibrarySearchPaths(paths []string) Option {
|
||||
|
||||
@@ -24,6 +24,8 @@ import (
|
||||
|
||||
const (
|
||||
// DetectMinimumVersion is a constant that triggers a spec to detect the minimum required version.
|
||||
//
|
||||
// Deprecated: DetectMinimumVersion is deprecated and will be removed.
|
||||
DetectMinimumVersion = "DETECT_MINIMUM_VERSION"
|
||||
|
||||
// FormatJSON indicates a JSON output format
|
||||
|
||||
@@ -39,6 +39,8 @@ type builder struct {
|
||||
mergedDeviceOptions []transform.MergedDeviceOption
|
||||
noSimplify bool
|
||||
permissions os.FileMode
|
||||
|
||||
transformOnSave transform.Transformer
|
||||
}
|
||||
|
||||
// newBuilder creates a new spec builder with the supplied options
|
||||
@@ -47,15 +49,23 @@ func newBuilder(opts ...Option) *builder {
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
if s.raw != nil {
|
||||
s.noSimplify = true
|
||||
vendor, class := parser.ParseQualifier(s.raw.Kind)
|
||||
s.vendor = vendor
|
||||
s.class = class
|
||||
if s.vendor == "" {
|
||||
s.vendor = vendor
|
||||
}
|
||||
if s.class == "" {
|
||||
s.class = class
|
||||
}
|
||||
if s.version == "" || s.version == DetectMinimumVersion {
|
||||
s.version = s.raw.Version
|
||||
}
|
||||
}
|
||||
|
||||
if s.version == "" {
|
||||
s.version = DetectMinimumVersion
|
||||
if s.version == "" || s.version == DetectMinimumVersion {
|
||||
s.transformOnSave = &setMinimumRequiredVersion{}
|
||||
s.version = cdi.CurrentVersion
|
||||
}
|
||||
if s.vendor == "" {
|
||||
s.vendor = "nvidia.com"
|
||||
@@ -67,7 +77,7 @@ func newBuilder(opts ...Option) *builder {
|
||||
s.format = FormatYAML
|
||||
}
|
||||
if s.permissions == 0 {
|
||||
s.permissions = 0600
|
||||
s.permissions = 0644
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -83,13 +93,8 @@ func (o *builder) Build() (*spec, error) {
|
||||
ContainerEdits: o.edits,
|
||||
}
|
||||
}
|
||||
|
||||
if raw.Version == DetectMinimumVersion {
|
||||
minVersion, err := cdi.MinimumRequiredVersion(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get minimum required CDI spec version: %v", err)
|
||||
}
|
||||
raw.Version = minVersion
|
||||
if raw.Version == "" {
|
||||
raw.Version = o.version
|
||||
}
|
||||
|
||||
if !o.noSimplify {
|
||||
@@ -110,11 +115,11 @@ func (o *builder) Build() (*spec, error) {
|
||||
}
|
||||
|
||||
s := spec{
|
||||
Spec: raw,
|
||||
format: o.format,
|
||||
permissions: o.permissions,
|
||||
Spec: raw,
|
||||
format: o.format,
|
||||
permissions: o.permissions,
|
||||
transformOnSave: o.transformOnSave,
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
|
||||
35
pkg/nvcdi/spec/set-minimum-version.go
Normal file
35
pkg/nvcdi/spec/set-minimum-version.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 spec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
type setMinimumRequiredVersion struct{}
|
||||
|
||||
func (d setMinimumRequiredVersion) Transform(spec *specs.Spec) error {
|
||||
minVersion, err := cdi.MinimumRequiredVersion(spec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get minimum required CDI spec version: %v", err)
|
||||
}
|
||||
spec.Version = minVersion
|
||||
return nil
|
||||
}
|
||||
@@ -24,12 +24,15 @@ import (
|
||||
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
|
||||
)
|
||||
|
||||
type spec struct {
|
||||
*specs.Spec
|
||||
format string
|
||||
permissions os.FileMode
|
||||
format string
|
||||
permissions os.FileMode
|
||||
transformOnSave transform.Transformer
|
||||
}
|
||||
|
||||
var _ Interface = (*spec)(nil)
|
||||
@@ -41,18 +44,23 @@ func New(opts ...Option) (Interface, error) {
|
||||
|
||||
// Save writes the spec to the specified path and overwrites the file if it exists.
|
||||
func (s *spec) Save(path string) error {
|
||||
if s.transformOnSave != nil {
|
||||
err := s.transformOnSave.Transform(s.Raw())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying transform: %w", err)
|
||||
}
|
||||
}
|
||||
path, err := s.normalizePath(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to normalize path: %w", err)
|
||||
}
|
||||
|
||||
specDir := filepath.Dir(path)
|
||||
registry := cdi.GetRegistry(
|
||||
cache, _ := cdi.NewCache(
|
||||
cdi.WithAutoRefresh(false),
|
||||
cdi.WithSpecDirs(specDir),
|
||||
)
|
||||
|
||||
if err := registry.SpecDB().WriteSpec(s.Raw(), filepath.Base(path)); err != nil {
|
||||
if err := cache.WriteSpec(s.Raw(), filepath.Base(path)); err != nil {
|
||||
return fmt.Errorf("failed to write spec: %w", err)
|
||||
}
|
||||
|
||||
|
||||
181
pkg/nvcdi/spec/spec_test.go
Normal file
181
pkg/nvcdi/spec/spec_test.go
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 spec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform/root"
|
||||
)
|
||||
|
||||
func TestSpec(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
options []Option
|
||||
expectedNewError error
|
||||
transform transform.Transformer
|
||||
expectedSpec string
|
||||
}{
|
||||
{
|
||||
description: "default options return empty spec",
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.3.0
|
||||
containerEdits: {}
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "version is overridden",
|
||||
options: []Option{WithVersion("0.5.0")},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits: {}
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "raw spec is used as is",
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Version: "0.5.0",
|
||||
Kind: "nvidia.com/gpu",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
},
|
||||
},
|
||||
)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits:
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "raw spec with no version uses minimum version",
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
},
|
||||
},
|
||||
)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.3.0
|
||||
containerEdits:
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "spec with host dev path uses 0.5.0 version",
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/some/dev/dev0",
|
||||
Path: "/dev/dev0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: /some/dev/dev0
|
||||
path: /dev/dev0
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "transformed spec uses minimum version",
|
||||
options: []Option{WithRawSpec(
|
||||
&specs.Spec{
|
||||
Kind: "nvidia.com/gpu",
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Env: []string{"FOO=bar"},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/some/dev/dev0",
|
||||
Path: "/dev/dev0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)},
|
||||
transform: transform.Merge(
|
||||
root.New(
|
||||
root.WithRoot("/some/dev/"),
|
||||
root.WithTargetRoot("/dev/"),
|
||||
root.WithRelativeTo("host"),
|
||||
),
|
||||
transform.NewSimplifier(),
|
||||
),
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: /dev/dev0
|
||||
path: /dev/dev0
|
||||
env:
|
||||
- FOO=bar
|
||||
devices: null
|
||||
kind: nvidia.com/gpu
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
|
||||
s, err := New(tc.options...)
|
||||
require.ErrorIs(t, err, tc.expectedNewError)
|
||||
|
||||
if tc.transform != nil {
|
||||
err := tc.transform.Transform(s.Raw())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
_, err = s.WriteTo(buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, tc.expectedSpec, buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -98,13 +98,13 @@ func TestDeduplicate(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -114,8 +114,8 @@ func TestDeduplicate(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -141,8 +141,8 @@ func TestMergedDevice(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -153,8 +153,8 @@ func TestMergedDevice(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -169,8 +169,8 @@ func TestMergedDevice(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -181,8 +181,8 @@ func TestMergedDevice(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -193,8 +193,8 @@ func TestMergedDevice(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -165,27 +165,27 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk", "hook", "update-ldcache",
|
||||
"nvidia-cdi-hook", "update-ldcache",
|
||||
"--folder",
|
||||
"/root/path/to/target",
|
||||
},
|
||||
},
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/target-root/usr/bin/nvidia-ctk",
|
||||
Path: "/target-root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk", "hook", "update-ldcache",
|
||||
"nvidia-cdi-hook", "update-ldcache",
|
||||
"--folder",
|
||||
"/target-root/path/to/target",
|
||||
},
|
||||
},
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/different-root/usr/bin/nvidia-ctk",
|
||||
Path: "/different-root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk", "hook", "update-ldcache",
|
||||
"nvidia-cdi-hook", "update-ldcache",
|
||||
"--folder",
|
||||
"/different-root/path/to/target",
|
||||
},
|
||||
@@ -198,27 +198,27 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk", "hook", "update-ldcache",
|
||||
"nvidia-cdi-hook", "update-ldcache",
|
||||
"--folder",
|
||||
"/target-root/path/to/target",
|
||||
},
|
||||
},
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/target-root/usr/bin/nvidia-ctk",
|
||||
Path: "/target-root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk", "hook", "update-ldcache",
|
||||
"nvidia-cdi-hook", "update-ldcache",
|
||||
"--folder",
|
||||
"/target-root/path/to/target",
|
||||
},
|
||||
},
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/different-root/usr/bin/nvidia-ctk",
|
||||
Path: "/different-root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-ctk", "hook", "update-ldcache",
|
||||
"nvidia-cdi-hook", "update-ldcache",
|
||||
"--folder",
|
||||
"/different-root/path/to/target",
|
||||
},
|
||||
@@ -236,7 +236,7 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "startContainer",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"--link",
|
||||
"/root/path/to/target::/root/path/to/link",
|
||||
@@ -250,7 +250,7 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "startContainer",
|
||||
Path: "/target-root/usr/bin/nvidia-ctk",
|
||||
Path: "/target-root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"--link",
|
||||
"/target-root/path/to/target::/target-root/path/to/link",
|
||||
@@ -269,7 +269,7 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"--link",
|
||||
"/root/path/to/target::/root/path/to/link",
|
||||
@@ -283,7 +283,7 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"--link",
|
||||
"/target-root/path/to/target::/target-root/path/to/link",
|
||||
@@ -302,7 +302,7 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createRuntime",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"--link",
|
||||
"/root/path/to/target::/root/path/to/link",
|
||||
@@ -316,7 +316,7 @@ func TestContainerRootTransformer(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createRuntime",
|
||||
Path: "/root/usr/bin/nvidia-ctk",
|
||||
Path: "/root/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"--link",
|
||||
"/root/path/to/target::/root/path/to/link",
|
||||
|
||||
99
pkg/nvcdi/transform/root/driver-root.go
Normal file
99
pkg/nvcdi/transform/root/driver-root.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 root
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform"
|
||||
)
|
||||
|
||||
type DriverOption func(*driverOptions)
|
||||
|
||||
func WithDriverRoot(root string) DriverOption {
|
||||
return func(do *driverOptions) {
|
||||
do.driverRoot = root
|
||||
}
|
||||
}
|
||||
|
||||
func WithTargetDriverRoot(root string) DriverOption {
|
||||
return func(do *driverOptions) {
|
||||
do.targetDriverRoot = root
|
||||
}
|
||||
}
|
||||
|
||||
func WithDevRoot(root string) DriverOption {
|
||||
return func(do *driverOptions) {
|
||||
do.devRoot = root
|
||||
}
|
||||
}
|
||||
|
||||
func WithTargetDevRoot(root string) DriverOption {
|
||||
return func(do *driverOptions) {
|
||||
do.targetDevRoot = root
|
||||
}
|
||||
}
|
||||
|
||||
type driverOptions struct {
|
||||
driverRoot string
|
||||
targetDriverRoot string
|
||||
devRoot string
|
||||
targetDevRoot string
|
||||
}
|
||||
|
||||
// NewDriverTransformer creates a transformer for transforming driver specifications.
|
||||
func NewDriverTransformer(opts ...DriverOption) transform.Transformer {
|
||||
d := &driverOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(d)
|
||||
}
|
||||
if d.driverRoot == "" {
|
||||
d.driverRoot = "/"
|
||||
}
|
||||
if d.targetDriverRoot == "" {
|
||||
d.targetDriverRoot = "/"
|
||||
}
|
||||
if d.devRoot == "" {
|
||||
d.devRoot = d.driverRoot
|
||||
}
|
||||
if d.targetDevRoot == "" {
|
||||
d.targetDevRoot = d.targetDriverRoot
|
||||
}
|
||||
|
||||
var transformers []transform.Transformer
|
||||
|
||||
if d.targetDevRoot != d.targetDriverRoot {
|
||||
devRootTransformer := New(
|
||||
WithRoot(ensureDev(d.devRoot)),
|
||||
WithTargetRoot(ensureDev(d.targetDevRoot)),
|
||||
)
|
||||
transformers = append(transformers, devRootTransformer)
|
||||
}
|
||||
|
||||
driverRootTransformer := New(
|
||||
WithRoot(d.driverRoot),
|
||||
WithTargetRoot(d.targetDriverRoot),
|
||||
)
|
||||
transformers = append(transformers, driverRootTransformer)
|
||||
|
||||
return transform.Merge(transformers...)
|
||||
}
|
||||
|
||||
func ensureDev(p string) string {
|
||||
return filepath.Join(strings.TrimSuffix(filepath.Clean(p), "/dev"), "/dev")
|
||||
}
|
||||
208
pkg/nvcdi/transform/root/driver-root_test.go
Normal file
208
pkg/nvcdi/transform/root/driver-root_test.go
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# 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 root
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"tags.cncf.io/container-device-interface/specs-go"
|
||||
)
|
||||
|
||||
func TestDriverTransformer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
driverRoot string
|
||||
targetDriverRoot string
|
||||
devRoot string
|
||||
targetDevRoot string
|
||||
spec *specs.Spec
|
||||
expectedError error
|
||||
expectedSpec *specs.Spec
|
||||
}{
|
||||
{
|
||||
description: "dev root not specified",
|
||||
driverRoot: "/driver-root",
|
||||
targetDriverRoot: "/host/driver/root/",
|
||||
spec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/driver-root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/driver-root/dev/host/path",
|
||||
Path: "/driver-root/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/host/driver/root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/host/driver/root/dev/host/path",
|
||||
Path: "/driver-root/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "dev driver root matches",
|
||||
driverRoot: "/driver-root",
|
||||
targetDriverRoot: "/host/driver/root/",
|
||||
devRoot: "/driver-root",
|
||||
targetDevRoot: "/host/driver/root/",
|
||||
spec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/driver-root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/driver-root/dev/host/path",
|
||||
Path: "/driver-root/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/host/driver/root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/host/driver/root/dev/host/path",
|
||||
Path: "/driver-root/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "dev driver root matches separate target dev root",
|
||||
driverRoot: "/driver-root",
|
||||
targetDriverRoot: "/host/driver/root/",
|
||||
devRoot: "/driver-root",
|
||||
targetDevRoot: "/host/dev/root/",
|
||||
spec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/driver-root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/driver-root/dev/host/path",
|
||||
Path: "/driver-root/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/host/driver/root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/host/dev/root/dev/host/path",
|
||||
Path: "/driver-root/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "dev root specified with explicit target",
|
||||
driverRoot: "/driver-root",
|
||||
targetDriverRoot: "/host/driver/root/",
|
||||
devRoot: "/",
|
||||
targetDevRoot: "/dev/root/",
|
||||
spec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/driver-root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/dev/host/path",
|
||||
Path: "/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
ContainerEdits: specs.ContainerEdits{
|
||||
Mounts: []*specs.Mount{
|
||||
{
|
||||
HostPath: "/host/driver/root/host/path",
|
||||
ContainerPath: "/driver-root/container/path",
|
||||
},
|
||||
},
|
||||
DeviceNodes: []*specs.DeviceNode{
|
||||
{
|
||||
HostPath: "/dev/root/dev/host/path",
|
||||
Path: "/dev/container/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
transformer := NewDriverTransformer(
|
||||
WithDriverRoot(tc.driverRoot),
|
||||
WithTargetDriverRoot(tc.targetDriverRoot),
|
||||
WithDevRoot(tc.devRoot),
|
||||
WithTargetDevRoot(tc.targetDevRoot),
|
||||
)
|
||||
|
||||
err := transformer.Transform(tc.spec)
|
||||
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
require.EqualValues(t, tc.expectedSpec, tc.spec)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -115,8 +115,8 @@ func TestSimplify(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -127,8 +127,8 @@ func TestSimplify(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -139,13 +139,13 @@ func TestSimplify(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -160,8 +160,8 @@ func TestSimplify(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -172,8 +172,8 @@ func TestSimplify(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -184,8 +184,8 @@ func TestSimplify(t *testing.T) {
|
||||
Hooks: []*specs.Hook{
|
||||
{
|
||||
HookName: "createContainer",
|
||||
Path: "/usr/bin/nvidia-ctk",
|
||||
Args: []string{"nvidia-ctk", "hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "chmod", "--mode", "755", "--path", "/dev/dri"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -25,10 +25,10 @@ import (
|
||||
)
|
||||
|
||||
type deviceFolderPermissions struct {
|
||||
logger logger.Interface
|
||||
devRoot string
|
||||
nvidiaCTKPath string
|
||||
devices discover.Discover
|
||||
logger logger.Interface
|
||||
devRoot string
|
||||
nvidiaCDIHookPath string
|
||||
devices discover.Discover
|
||||
}
|
||||
|
||||
var _ discover.Discover = (*deviceFolderPermissions)(nil)
|
||||
@@ -39,12 +39,12 @@ var _ discover.Discover = (*deviceFolderPermissions)(nil)
|
||||
// The nested devices that are applicable to the NVIDIA GPU devices are:
|
||||
// - DRM devices at /dev/dri/*
|
||||
// - NVIDIA Caps devices at /dev/nvidia-caps/*
|
||||
func newDeviceFolderPermissionHookDiscoverer(logger logger.Interface, devRoot string, nvidiaCTKPath string, devices discover.Discover) discover.Discover {
|
||||
func newDeviceFolderPermissionHookDiscoverer(logger logger.Interface, devRoot string, nvidiaCDIHookPath string, devices discover.Discover) discover.Discover {
|
||||
d := &deviceFolderPermissions{
|
||||
logger: logger,
|
||||
devRoot: devRoot,
|
||||
nvidiaCTKPath: nvidiaCTKPath,
|
||||
devices: devices,
|
||||
logger: logger,
|
||||
devRoot: devRoot,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
devices: devices,
|
||||
}
|
||||
|
||||
return d
|
||||
@@ -70,8 +70,8 @@ func (d *deviceFolderPermissions) Hooks() ([]discover.Hook, error) {
|
||||
args = append(args, "--path", folder)
|
||||
}
|
||||
|
||||
hook := discover.CreateNvidiaCTKHook(
|
||||
d.nvidiaCTKPath,
|
||||
hook := discover.CreateNvidiaCDIHook(
|
||||
d.nvidiaCDIHookPath,
|
||||
"chmod",
|
||||
args...,
|
||||
)
|
||||
|
||||
@@ -45,8 +45,6 @@ echo "Building ${TARGET} for all packages to ${DIST_DIR}"
|
||||
|
||||
: "${LIBNVIDIA_CONTAINER_ROOT:=${PROJECT_ROOT}/third_party/libnvidia-container}"
|
||||
: "${NVIDIA_CONTAINER_TOOLKIT_ROOT:=${PROJECT_ROOT}}"
|
||||
: "${NVIDIA_CONTAINER_RUNTIME_ROOT:=${PROJECT_ROOT}/third_party/nvidia-container-runtime}"
|
||||
: "${NVIDIA_DOCKER_ROOT:=${PROJECT_ROOT}/third_party/nvidia-docker}"
|
||||
|
||||
|
||||
"${SCRIPTS_DIR}/get-component-versions.sh"
|
||||
@@ -70,57 +68,3 @@ make -C "${NVIDIA_CONTAINER_TOOLKIT_ROOT}" \
|
||||
LIBNVIDIA_CONTAINER_TAG="${NVIDIA_CONTAINER_TOOLKIT_TAG}" \
|
||||
"${TARGET}"
|
||||
fi
|
||||
|
||||
# If required we also build the nvidia-container-runtime and nvidia-docker packages.
|
||||
# Since these are essentially meta packages intended to allow for users to
|
||||
# transition from older installation workflows, we skip these for rc builds
|
||||
# (NVIDIA_CONTAINER_TOOLKIT_TAG != "") and releases with a non-zero patch
|
||||
# version of 0.
|
||||
if [[ -n ${FORCE_META_PACKAGES} || -z ${NVIDIA_CONTAINER_TOOLKIT_TAG} && "${NVIDIA_CONTAINER_TOOLKIT_VERSION%.0}" != "${NVIDIA_CONTAINER_TOOLKIT_VERSION}" ]]; then
|
||||
package_format=$(package_type ${TARGET})
|
||||
package_target=$(get_package_target ${TARGET})
|
||||
|
||||
# We set the TOOLKIT_VERSION, TOOLKIT_TAG for the nvidia-container-runtime and nvidia-docker targets
|
||||
# The LIB_TAG is also overridden to match the TOOLKIT_TAG.
|
||||
|
||||
# Build nvidia-container-runtime if required
|
||||
package_name="nvidia-container-runtime"
|
||||
package_version=${NVIDIA_CONTAINER_RUNTIME_VERSION}${NVIDIA_CONTAINER_TOOLKIT_TAG:+~${NVIDIA_CONTAINER_TOOLKIT_TAG}}-1
|
||||
package_pattern=${DIST_DIR}/${package_format}/all/${package_name}?${package_version}?*.${package_format}
|
||||
package=$(ls ${package_pattern}) || echo ""
|
||||
if [[ -z ${package} ]]; then
|
||||
echo "${package_name} does not exist"
|
||||
make -C ${NVIDIA_CONTAINER_RUNTIME_ROOT} \
|
||||
LIB_VERSION="${NVIDIA_CONTAINER_RUNTIME_VERSION}" \
|
||||
LIB_TAG="${NVIDIA_CONTAINER_TOOLKIT_TAG}" \
|
||||
TOOLKIT_VERSION="${NVIDIA_CONTAINER_TOOLKIT_VERSION}" \
|
||||
TOOLKIT_TAG="${NVIDIA_CONTAINER_TOOLKIT_TAG}" \
|
||||
${TARGET}
|
||||
fi
|
||||
if [[ -n ${package_target} ]]; then
|
||||
mkdir -p ${DIST_DIR}/${package_target}/
|
||||
cp -p ${package_pattern} ${DIST_DIR}/${package_target}/
|
||||
fi
|
||||
|
||||
# Build nvidia-docker2 if required
|
||||
package_name="nvidia-docker2"
|
||||
package_version=${NVIDIA_DOCKER_VERSION}${NVIDIA_CONTAINER_TOOLKIT_TAG:+~${NVIDIA_CONTAINER_TOOLKIT_TAG}}-1
|
||||
package_pattern=${DIST_DIR}/${package_format}/all/${package_name}?${package_version}?*.${package_format}
|
||||
package=$(ls ${package_pattern}) || echo ""
|
||||
if [[ -z ${package} ]]; then
|
||||
echo "${package_name} does not exist"
|
||||
make -C ${NVIDIA_DOCKER_ROOT} \
|
||||
LIB_VERSION="${NVIDIA_DOCKER_VERSION}" \
|
||||
LIB_TAG="${NVIDIA_CONTAINER_TOOLKIT_TAG}" \
|
||||
TOOLKIT_VERSION="${NVIDIA_CONTAINER_TOOLKIT_VERSION}" \
|
||||
TOOLKIT_TAG="${NVIDIA_CONTAINER_TOOLKIT_TAG}" \
|
||||
${TARGET}
|
||||
fi
|
||||
if [[ -n ${package_target} ]]; then
|
||||
mkdir -p ${DIST_DIR}/${package_target}/
|
||||
cp -p ${package_pattern} ${DIST_DIR}/${package_target}/
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Skipping nvidia-container-runtime and nvidia-docker builds."
|
||||
fi
|
||||
|
||||
@@ -47,8 +47,6 @@ eval $(${SCRIPTS_DIR}/get-component-versions.sh)
|
||||
|
||||
export NVIDIA_CONTAINER_TOOLKIT_VERSION
|
||||
export NVIDIA_CONTAINER_TOOLKIT_TAG
|
||||
export NVIDIA_CONTAINER_RUNTIME_VERSION
|
||||
export NVIDIA_DOCKER_VERSION
|
||||
|
||||
for target in ${targets[@]}; do
|
||||
"${SCRIPTS_DIR}/build-all-components.sh" "${target}"
|
||||
|
||||
@@ -37,8 +37,7 @@ PACKAGE_IMAGE=$1
|
||||
: "${ARTIFACTS_DIR="${PROJECT_ROOT}/artifacts"}"
|
||||
|
||||
# For release-candidates we skip certain packages.
|
||||
# For example, we don't release release candidates of nvidia-container-runtime and nvidia-docker2
|
||||
# since these only bump the nvidia-container-toolkit dependency.
|
||||
# This function returns 0 if a package should be skipped and 1 otherwise.
|
||||
function skip-for-release-candidate() {
|
||||
# We always skip nvidia-container-toolkit-operator-extensions packages
|
||||
if [[ "${package_name/"nvidia-container-toolkit-operator-extensions"/}" != "${package_name}" ]]; then
|
||||
@@ -53,14 +52,6 @@ function skip-for-release-candidate() {
|
||||
if [[ "${VERSION%.0}" == "${VERSION}" ]]; then
|
||||
is_non_patch_full_release=0
|
||||
fi
|
||||
|
||||
local package_name=$1
|
||||
if [[ "${package_name/"nvidia-docker2"/}" != "${package_name}" ]]; then
|
||||
return ${is_non_patch_full_release}
|
||||
fi
|
||||
if [[ "${package_name/"nvidia-container-runtime"/}" != "${package_name}" ]]; then
|
||||
return ${is_non_patch_full_release}
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../scripts && pwd )"
|
||||
PROJECT_ROOT="$( cd ${SCRIPTS_DIR}/.. && pwd )"
|
||||
|
||||
NVIDIA_CONTAINER_TOOLKIT_ROOT=${PROJECT_ROOT}
|
||||
NVIDIA_CONTAINER_RUNTIME_ROOT=${PROJECT_ROOT}/third_party/nvidia-container-runtime
|
||||
NVIDIA_DOCKER_ROOT=${PROJECT_ROOT}/third_party/nvidia-docker
|
||||
|
||||
versions_makefile=${NVIDIA_CONTAINER_TOOLKIT_ROOT}/versions.mk
|
||||
# Get version for nvidia-container-toolit
|
||||
@@ -38,22 +36,6 @@ nvidia_container_toolkit_version=$(grep -m 1 "^LIB_VERSION := " ${versions_makef
|
||||
nvidia_container_toolkit_tag=$(grep -m 1 "^LIB_TAG .= " ${versions_makefile} | sed -e 's/LIB_TAG .=[[:space:]]\(.*\)[[:space:]]*/\1/')
|
||||
nvidia_container_toolkit_version_tag="${nvidia_container_toolkit_version}${nvidia_container_toolkit_tag:+~${nvidia_container_toolkit_tag}}"
|
||||
|
||||
# Get version for nvidia-container-runtime
|
||||
nvidia_container_runtime_version=$(grep -m 1 "^NVIDIA_CONTAINER_RUNTIME_VERSION := " ${versions_makefile} | sed -e 's/NVIDIA_CONTAINER_RUNTIME_VERSION :=[[:space:]]\(.*\)[[:space:]]*/\1/')
|
||||
nvidia_container_runtime_tag=${nvidia_container_toolkit_tag}
|
||||
nvidia_container_runtime_version_tag="${nvidia_container_runtime_version}${nvidia_container_runtime_tag:+~${nvidia_container_runtime_tag}}"
|
||||
|
||||
# Get version for nvidia-docker
|
||||
nvidia_docker_version=$(grep -m 1 "^NVIDIA_DOCKER_VERSION := " ${versions_makefile} | sed -e 's/NVIDIA_DOCKER_VERSION :=[[:space:]]\(.*\)[[:space:]]*/\1/')
|
||||
nvidia_docker_tag=${nvidia_container_toolkit_tag}
|
||||
nvidia_docker_version_tag="${nvidia_docker_version}${nvidia_docker_tag:+~${nvidia_docker_tag}}"
|
||||
|
||||
echo "NVIDIA_CONTAINER_TOOLKIT_VERSION=${nvidia_container_toolkit_version}"
|
||||
echo "NVIDIA_CONTAINER_TOOLKIT_TAG=${nvidia_container_toolkit_tag}"
|
||||
echo "NVIDIA_CONTAINER_TOOLKIT_PACKAGE_VERSION=${nvidia_container_toolkit_version_tag//\~/-}"
|
||||
echo "NVIDIA_CONTAINER_RUNTIME_VERSION=${nvidia_container_runtime_version}"
|
||||
echo "NVIDIA_CONTAINER_RUNTIME_TAG=${nvidia_container_runtime_tag}"
|
||||
echo "NVIDIA_CONTAINER_RUNTIME_PACKAGE_VERSION=${nvidia_container_runtime_version_tag//\~/-}"
|
||||
echo "NVIDIA_DOCKER_VERSION=${nvidia_docker_version}"
|
||||
echo "NVIDIA_DOCKER_TAG=${nvidia_docker_tag}"
|
||||
echo "NVIDIA_DOCKER_PACKAGE_VERSION=${nvidia_docker_version_tag//\~/-}"
|
||||
|
||||
@@ -142,19 +142,6 @@ function sync() {
|
||||
fi
|
||||
|
||||
done
|
||||
if [[ ${REPO} == "stable" ]]; then
|
||||
for f in $(ls ${src}/nvidia-container-runtime*.${pkg_type} ${src}/nvidia-docker*.${pkg_type}); do
|
||||
df=${dst}/$(basename ${f})
|
||||
df_stable=${df//"/experimental/"/"/stable/"}
|
||||
if [[ -f "${df}" ]]; then
|
||||
echo "${df} already exists; skipping"
|
||||
elif [[ ${REPO} == "experimental" && -f ${df_stable} ]]; then
|
||||
echo "${df_stable} already exists; skipping"
|
||||
else
|
||||
cp ${f} ${df}
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
targets=${all[@]}
|
||||
@@ -191,18 +178,6 @@ done
|
||||
|
||||
git -C ${PACKAGE_REPO_ROOT} add ${REPO}
|
||||
|
||||
if [[ "${REPO}" == "stable" ]]; then
|
||||
# Stable release
|
||||
git -C ${PACKAGE_REPO_ROOT} commit -s -F- <<EOF
|
||||
Add packages for NVIDIA Container Toolkit ${VERSION} release
|
||||
|
||||
These include:
|
||||
* libnvidia-container* ${LIBNVIDIA_CONTAINER_PACKAGE_VERSION}
|
||||
* nvidia-container-toolkit ${NVIDIA_CONTAINER_TOOLKIT_PACKAGE_VERSION}
|
||||
* nvidia-container-runtime ${NVIDIA_CONTAINER_RUNTIME_PACKAGE_VERSION}
|
||||
* nvidia-docker ${NVIDIA_DOCKER_PACKAGE_VERSION}
|
||||
EOF
|
||||
else
|
||||
# Experimental / release candidate release
|
||||
git -C ${PACKAGE_REPO_ROOT} commit -s -F- <<EOF
|
||||
Add packages for NVIDIA Container Toolkit ${VERSION} ${REPO} release
|
||||
@@ -211,7 +186,6 @@ These include:
|
||||
* libnvidia-container* ${LIBNVIDIA_CONTAINER_PACKAGE_VERSION}
|
||||
* nvidia-container-toolkit ${NVIDIA_CONTAINER_TOOLKIT_PACKAGE_VERSION}
|
||||
EOF
|
||||
fi
|
||||
|
||||
: ${MASTER_KEY_PATH:? Path to master key MASTER_KEY_PATH must be set}
|
||||
: ${SUB_KEY_PATH:? Path to sub key SUB_KEY_PATH must be set}
|
||||
@@ -242,12 +216,12 @@ function sign() {
|
||||
sign deb
|
||||
|
||||
git -C ${PACKAGE_REPO_ROOT} add ${REPO}
|
||||
git -C ${PACKAGE_REPO_ROOT} commit -s -m "TOFIX: Sign deb packages for ${VERSION} in ${REPO}"
|
||||
git -C ${PACKAGE_REPO_ROOT} commit -s -m "fixup! Add packages for NVIDIA Container Toolkit ${VERSION} ${REPO} release"
|
||||
|
||||
sign rpm
|
||||
|
||||
git -C ${PACKAGE_REPO_ROOT} add ${REPO}
|
||||
git -C ${PACKAGE_REPO_ROOT} commit -s -m "TOFIX: Sign rpm packages for ${VERSION} in ${REPO}"
|
||||
git -C ${PACKAGE_REPO_ROOT} commit -s -m "fixup! Add packages for NVIDIA Container Toolkit ${VERSION} ${REPO} release"
|
||||
|
||||
echo "To publish changes, go to ${PACKAGE_REPO_ROOT} and run: "
|
||||
echo "To publish changes, go to ${PACKAGE_REPO_ROOT} and run:"
|
||||
echo " git rebase -i ${UPSTREAM_REFERENCE}"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user