mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Compare commits
1 Commits
main
...
pull-reque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85adc3fd08 |
6
.github/workflows/image.yaml
vendored
6
.github/workflows/image.yaml
vendored
@@ -79,8 +79,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target:
|
dist:
|
||||||
- application
|
- ubi9
|
||||||
- packaging
|
- packaging
|
||||||
needs: packages
|
needs: packages
|
||||||
steps:
|
steps:
|
||||||
@@ -117,4 +117,4 @@ jobs:
|
|||||||
BUILD_MULTI_ARCH_IMAGES: ${{ inputs.build_multi_arch_images }}
|
BUILD_MULTI_ARCH_IMAGES: ${{ inputs.build_multi_arch_images }}
|
||||||
run: |
|
run: |
|
||||||
echo "${VERSION}"
|
echo "${VERSION}"
|
||||||
make -f deployments/container/Makefile build-${{ matrix.target }}
|
make -f deployments/container/Makefile build-${{ matrix.dist }}
|
||||||
|
|||||||
@@ -287,6 +287,5 @@ sign-images-dummy:
|
|||||||
- release-images-dummy
|
- release-images-dummy
|
||||||
variables:
|
variables:
|
||||||
NGC_CLI: "echo [DUMMY] ngc-cli/ngc"
|
NGC_CLI: "echo [DUMMY] ngc-cli/ngc"
|
||||||
IMAGE_TAG: "${CI_COMMIT_SHORT_SHA}"
|
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG == null || $CI_COMMIT_TAG == ""
|
- if: $CI_COMMIT_TAG == null || $CI_COMMIT_TAG == ""
|
||||||
|
|||||||
147
CHANGELOG.md
147
CHANGELOG.md
@@ -1,139 +1,34 @@
|
|||||||
# NVIDIA Container Toolkit Changelog
|
# NVIDIA Container Toolkit Changelog
|
||||||
|
|
||||||
## v1.18.0-rc.1
|
## v1.17.4
|
||||||
|
- Disable mounting of compat libs from container by default
|
||||||
- Add create-soname-symlinks hook
|
|
||||||
- Require matching version of libnvidia-container-tools
|
|
||||||
- Add envvar for libcuda.so parent dir to CDI spec
|
|
||||||
- Add EnvVar to Discover interface
|
|
||||||
- Resolve to legacy by default in nvidia-container-runtime-hook
|
|
||||||
- Default to jit-cdi mode in the nvidia runtime
|
|
||||||
- Use functional options to construct runtime mode resolver
|
|
||||||
- Add NVIDIA_CTK_CONFIG_FILE_PATH envvar
|
|
||||||
- Switch to cuda ubi9 base image
|
|
||||||
- Use single version tag for image
|
|
||||||
- BUGFIX: modifier: respect GPU volume-mount device requests
|
|
||||||
- Ensure consistent sorting of annotation devices
|
|
||||||
- Extract deb and rpm packages to single image
|
|
||||||
- Remove docker-run as default runtime candidate
|
|
||||||
- Return annotation devices from VisibleDevices
|
|
||||||
- Make CDI device requests consistent with other methods
|
|
||||||
- Construct container info once
|
|
||||||
- Add logic to extract annotation device requests to image type
|
|
||||||
- Add IsPrivileged function to CUDA container type
|
|
||||||
- Add device IDs to nvcdi.GetSpec API
|
|
||||||
- Refactor extracting requested devices from the container image
|
|
||||||
- Add EnvVars option for all nvidia-ctk cdi commands
|
|
||||||
- Add nvidia-cdi-refresh service
|
|
||||||
- Add discovery of arch-specific vulkan ICD
|
|
||||||
- Add disabled-device-node-modification hook to CDI spec
|
|
||||||
- Add a hook to disable device node creation in a container
|
|
||||||
- Remove redundant deduplication of search paths for WSL
|
|
||||||
- Added ability to disable specific (or all) CDI hooks
|
|
||||||
- Consolidate HookName functionality on internal/discover pkg
|
|
||||||
- Add envvar to control debug logging in CDI hooks
|
|
||||||
- Add FeatureFlags to the nvcdi API
|
|
||||||
- Reenable nvsandboxutils for driver discovery
|
|
||||||
- Edit discover.mounts to have a deterministic output
|
|
||||||
- Refactor the way we create CDI Hooks
|
|
||||||
- Issue warning on unsupported CDI hook
|
|
||||||
- Run update-ldcache in isolated namespaces
|
|
||||||
- Add cuda-compat-mode config option
|
|
||||||
- Fix mode detection on Thor-based systems
|
|
||||||
- Add rprivate to CDI mount options
|
|
||||||
- Skip nil discoverers in merge
|
|
||||||
- bump runc go dep to v1.3.0
|
|
||||||
- Fix resolution of libs in LDCache on ARM
|
|
||||||
- Updated .release:staging to stage images in nvstaging
|
|
||||||
- Refactor toolkit installer
|
|
||||||
- Allow container runtime executable path to be specified
|
|
||||||
- Add support for building ubuntu22.04 on arm64
|
|
||||||
- Fix race condition in mounts cache
|
|
||||||
- Add support for building ubuntu22.04 on amd64
|
|
||||||
- Fix update-ldcache arguments
|
|
||||||
- Remove positional arguments from nvidia-ctk-installer
|
|
||||||
- Remove deprecated --runtime-args from nvidia-ctk-installer
|
|
||||||
- Add version info to nvidia-ctk-installer
|
|
||||||
- Update nvidia-ctk-installer app name to match binary name
|
|
||||||
- Allow nvidia-ctk config --set to accept comma-separated lists
|
|
||||||
- Disable enable-cuda-compat hook for management containers
|
|
||||||
- Allow enable-cuda-compat hook to be disabled in CDI spec generation
|
|
||||||
- Add disable-cuda-compat-lib-hook feature flag
|
|
||||||
- Add basic integration tests for forward compat
|
|
||||||
- Ensure that mode hook is executed last
|
|
||||||
- Add enable-cuda-compat hook to CDI spec generation
|
|
||||||
- Add ldconfig hook in legacy mode
|
|
||||||
- Add enable-cuda-compat hook if required
|
|
||||||
- Add enable-cuda-compat hook to allow compat libs to be discovered
|
|
||||||
- Use libcontainer execseal to run ldconfig
|
|
||||||
- Add ignore-imex-channel-requests feature flag
|
|
||||||
- Disable nvsandboxutils in nvcdi API
|
|
||||||
- Allow cdi mode to work with --gpus flag
|
|
||||||
- Add E2E GitHub Action for Container Toolkit
|
|
||||||
- Add remote-test option for E2E
|
|
||||||
- Enable CDI in runtime if CDI_ENABLED is set
|
|
||||||
- Fix overwriting docker feature flags
|
|
||||||
- Add option in toolkit container to enable CDI in runtime
|
|
||||||
- Remove Set from engine config API
|
|
||||||
- Add EnableCDI() method to engine.Interface
|
|
||||||
- Add IMEX binaries to CDI discovery
|
|
||||||
- Rename test folder to tests
|
|
||||||
- Add allow-cuda-compat-libs-from-container feature flag
|
- Add allow-cuda-compat-libs-from-container feature flag
|
||||||
- Disable mounting of compat libs from container
|
|
||||||
- Skip graphics modifier in CSV mode
|
- Skip graphics modifier in CSV mode
|
||||||
- Move nvidia-toolkit to nvidia-ctk-installer
|
|
||||||
- Automated regression testing for the NVIDIA Container Toolkit
|
|
||||||
- Add support for containerd version 3 config
|
|
||||||
- Remove watch option from create-dev-char-symlinks
|
|
||||||
- Add string TOML source
|
|
||||||
- Improve the implementation for UseLegacyConfig
|
|
||||||
- Properly pass configSearchPaths to a Driver constructor
|
- Properly pass configSearchPaths to a Driver constructor
|
||||||
- Fix create-device-node test when devices exist
|
|
||||||
- Add imex mode to CDI spec generation
|
|
||||||
- Only allow host-relative LDConfig paths
|
|
||||||
- Fix NVIDIA_IMEX_CHANNELS handling on legacy images
|
|
||||||
- Fix bug in default config file path
|
|
||||||
- Fix fsnotify.Remove logic function.
|
|
||||||
- Force symlink creation in create-symlink hook
|
|
||||||
|
|
||||||
### Changes in the Toolkit Container
|
|
||||||
|
|
||||||
- Create /work/nvidia-toolkit symlink
|
|
||||||
- Use Apache license for images
|
|
||||||
- Switch to golang distroless image
|
|
||||||
- Switch to cuda ubi9 base image
|
|
||||||
- Use single version tag for image
|
|
||||||
- Extract deb and rpm packages to single image
|
|
||||||
- Bump nvidia/cuda in /deployments/container
|
|
||||||
- Bump nvidia/cuda in /deployments/container
|
|
||||||
- Add E2E GitHub Action for Container Toolkit
|
|
||||||
- Bump nvidia/cuda in /deployments/container
|
|
||||||
- Move nvidia-toolkit to nvidia-ctk-installer
|
|
||||||
- Add support for containerd version 3 config
|
- Add support for containerd version 3 config
|
||||||
- Improve the implementation for UseLegacyConfig
|
- Add string TOML source
|
||||||
- Bump nvidia/cuda in /deployments/container
|
|
||||||
- Add imex mode to CDI spec generation
|
|
||||||
- Only allow host-relative LDConfig paths
|
|
||||||
- Fallback to file for runtime config
|
|
||||||
|
|
||||||
### Changes in libnvidia-container
|
### Changes in libnvidia-container
|
||||||
|
|
||||||
- Fix pointer accessing local variable out of scope
|
|
||||||
- Require version match between libnvidia-container-tools and libnvidia-container1
|
|
||||||
- Add libnvidia-gpucomp.so to the list of compute libs
|
|
||||||
- Use VERSION_ prefix for version parts in makefiles
|
|
||||||
- Add additional logging
|
|
||||||
- Do not discard container flags when --cuda-compat-mode is not specified
|
|
||||||
- Remove unneeded --no-cntlibs argument from list command
|
|
||||||
- Add cuda-compat-mode flag to configure command
|
|
||||||
- Skip files when user has insufficient permissions
|
|
||||||
- Fix building with Go 1.24
|
|
||||||
- Add no-cntlibs CLI option to nvidia-container-cli
|
- Add no-cntlibs CLI option to nvidia-container-cli
|
||||||
- Fix always using fallback
|
|
||||||
- Add fallback for systems without memfd_create()
|
|
||||||
- Create virtual copy of host ldconfig binary before calling fexecve()
|
|
||||||
- Fix some typos in text.
|
|
||||||
|
|
||||||
|
### Changes in the Toolkit Container
|
||||||
|
- Bump CUDA base image version to 12.6.3
|
||||||
|
|
||||||
|
## v1.17.3
|
||||||
|
- Only allow host-relative LDConfig paths by default.
|
||||||
|
### Changes in libnvidia-container
|
||||||
|
- Create virtual copy of host ldconfig binary before calling fexecve()
|
||||||
|
|
||||||
|
## v1.17.2
|
||||||
|
- Fixed a bug where legacy images would set imex channels as `all`.
|
||||||
|
|
||||||
|
## v1.17.1
|
||||||
|
- Fixed a bug where specific symlinks existing in a container image could cause a container to fail to start.
|
||||||
|
- Fixed a bug on Tegra-based systems where a container would fail to start.
|
||||||
|
- Fixed a bug where the default container runtime config path was not properly set.
|
||||||
|
|
||||||
|
### Changes in the Toolkit Container
|
||||||
|
- Fallback to using a config file if the current runtime config can not be determined from the command line.
|
||||||
|
|
||||||
## v1.17.0
|
## v1.17.0
|
||||||
- Promote v1.17.0-rc.2 to v1.17.0
|
- Promote v1.17.0-rc.2 to v1.17.0
|
||||||
|
|||||||
@@ -28,4 +28,4 @@ The [user guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolk
|
|||||||
[Checkout the Contributing document!](CONTRIBUTING.md)
|
[Checkout the Contributing document!](CONTRIBUTING.md)
|
||||||
|
|
||||||
* Please let us know by [filing a new issue](https://github.com/NVIDIA/nvidia-container-toolkit/issues/new)
|
* Please let us know by [filing a new issue](https://github.com/NVIDIA/nvidia-container-toolkit/issues/new)
|
||||||
* You can contribute by creating a [pull request](https://github.com/NVIDIA/nvidia-container-toolkit/compare) to our public GitHub repository
|
* You can contribute by creating a [merge request](https://gitlab.com/nvidia/container-toolkit/container-toolkit/-/merge_requests/new) to our public GitLab repository
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/chmod"
|
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/chmod"
|
||||||
createsonamesymlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-soname-symlinks"
|
|
||||||
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-symlinks"
|
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-symlinks"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/cudacompat"
|
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/cudacompat"
|
||||||
disabledevicenodemodification "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/disable-device-node-modification"
|
disabledevicenodemodification "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/disable-device-node-modification"
|
||||||
@@ -36,7 +35,6 @@ func New(logger logger.Interface) []*cli.Command {
|
|||||||
symlinks.NewCommand(logger),
|
symlinks.NewCommand(logger),
|
||||||
chmod.NewCommand(logger),
|
chmod.NewCommand(logger),
|
||||||
cudacompat.NewCommand(logger),
|
cudacompat.NewCommand(logger),
|
||||||
createsonamesymlinks.NewCommand(logger),
|
|
||||||
disabledevicenodemodification.NewCommand(logger),
|
disabledevicenodemodification.NewCommand(logger),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
/**
|
|
||||||
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# 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 create_soname_symlinks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/moby/sys/reexec"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldconfig"
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
reexecUpdateLdCacheCommandName = "reexec-create-soname-symlinks"
|
|
||||||
)
|
|
||||||
|
|
||||||
type command struct {
|
|
||||||
logger logger.Interface
|
|
||||||
}
|
|
||||||
|
|
||||||
type options struct {
|
|
||||||
folders cli.StringSlice
|
|
||||||
ldconfigPath string
|
|
||||||
containerSpec string
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
reexec.Register(reexecUpdateLdCacheCommandName, createSonameSymlinksHandler)
|
|
||||||
if reexec.Init() {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCommand constructs an create-soname-symlinks command with the specified logger
|
|
||||||
func NewCommand(logger logger.Interface) *cli.Command {
|
|
||||||
c := command{
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
return c.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
// build the create-soname-symlinks command
|
|
||||||
func (m command) build() *cli.Command {
|
|
||||||
cfg := options{}
|
|
||||||
|
|
||||||
// Create the 'create-soname-symlinks' command
|
|
||||||
c := cli.Command{
|
|
||||||
Name: "create-soname-symlinks",
|
|
||||||
Usage: "Create soname symlinks libraries in specified directories",
|
|
||||||
Before: func(c *cli.Context) error {
|
|
||||||
return m.validateFlags(c, &cfg)
|
|
||||||
},
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return m.run(c, &cfg)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Flags = []cli.Flag{
|
|
||||||
&cli.StringSliceFlag{
|
|
||||||
Name: "folder",
|
|
||||||
Usage: "Specify a directory to generate soname symlinks in. Can be specified multiple times",
|
|
||||||
Destination: &cfg.folders,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "ldconfig-path",
|
|
||||||
Usage: "Specify the path to ldconfig on the host",
|
|
||||||
Destination: &cfg.ldconfigPath,
|
|
||||||
Value: "/sbin/ldconfig",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "container-spec",
|
|
||||||
Usage: "Specify the path to the OCI container spec. If empty or '-' the spec will be read from STDIN",
|
|
||||||
Destination: &cfg.containerSpec,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m command) validateFlags(c *cli.Context, cfg *options) error {
|
|
||||||
if cfg.ldconfigPath == "" {
|
|
||||||
return errors.New("ldconfig-path must be specified")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m command) run(c *cli.Context, cfg *options) error {
|
|
||||||
s, err := oci.LoadContainerState(cfg.containerSpec)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to load container state: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
containerRootDir, err := s.GetContainerRoot()
|
|
||||||
if err != nil || containerRootDir == "" || containerRootDir == "/" {
|
|
||||||
return fmt.Errorf("failed to determined container root: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd, err := ldconfig.NewRunner(
|
|
||||||
reexecUpdateLdCacheCommandName,
|
|
||||||
cfg.ldconfigPath,
|
|
||||||
containerRootDir,
|
|
||||||
cfg.folders.Value()...,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// createSonameSymlinksHandler wraps createSonameSymlinks with error handling.
|
|
||||||
func createSonameSymlinksHandler() {
|
|
||||||
if err := createSonameSymlinks(os.Args); err != nil {
|
|
||||||
log.Printf("Error updating ldcache: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// createSonameSymlinks ensures that soname symlinks are created in the
|
|
||||||
// specified directories.
|
|
||||||
// It is invoked from a reexec'd handler and provides namespace isolation for
|
|
||||||
// the operations performed by this hook. At the point where this is invoked,
|
|
||||||
// we are in a new mount namespace that is cloned from the parent.
|
|
||||||
//
|
|
||||||
// args[0] is the reexec initializer function name
|
|
||||||
// args[1] is the path of the ldconfig binary on the host
|
|
||||||
// args[2] is the container root directory
|
|
||||||
// The remaining args are directories where soname symlinks need to be created.
|
|
||||||
func createSonameSymlinks(args []string) error {
|
|
||||||
if len(args) < 3 {
|
|
||||||
return fmt.Errorf("incorrect arguments: %v", args)
|
|
||||||
}
|
|
||||||
hostLdconfigPath := args[1]
|
|
||||||
containerRootDirPath := args[2]
|
|
||||||
|
|
||||||
ldconfig, err := ldconfig.New(
|
|
||||||
hostLdconfigPath,
|
|
||||||
containerRootDirPath,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to construct ldconfig runner: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ldconfig.CreateSonameSymlinks(args[3:]...)
|
|
||||||
}
|
|
||||||
46
cmd/nvidia-cdi-hook/update-ldcache/container-root.go
Normal file
46
cmd/nvidia-cdi-hook/update-ldcache/container-root.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
# Copyright (c) 2025, 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 ldcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/moby/sys/symlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A containerRoot represents the root filesystem of a container.
|
||||||
|
type containerRoot string
|
||||||
|
|
||||||
|
// hasPath checks whether the specified path exists in the root.
|
||||||
|
func (r containerRoot) hasPath(path string) bool {
|
||||||
|
resolved, err := r.resolve(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(resolved); err != nil && os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve returns the absolute path including root path.
|
||||||
|
// Symlinks are resolved, but are guaranteed to resolve in the root.
|
||||||
|
func (r containerRoot) resolve(path string) (string, error) {
|
||||||
|
absolute := filepath.Clean(filepath.Join(string(r), path))
|
||||||
|
return symlink.FollowSymlinkInScope(absolute, string(r))
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
package ldconfig
|
package ldcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -29,8 +29,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
securejoin "github.com/cyphar/filepath-securejoin"
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
"github.com/moby/sys/reexec"
|
|
||||||
|
|
||||||
|
"github.com/moby/sys/reexec"
|
||||||
"github.com/opencontainers/runc/libcontainer/utils"
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@@ -182,7 +182,7 @@ func createTmpFs(target string, size int) error {
|
|||||||
// createReexecCommand creates a command that can be used to trigger the reexec
|
// createReexecCommand creates a command that can be used to trigger the reexec
|
||||||
// initializer.
|
// initializer.
|
||||||
// On linux this command runs in new namespaces.
|
// On linux this command runs in new namespaces.
|
||||||
func createReexecCommand(args []string) (*exec.Cmd, error) {
|
func createReexecCommand(args []string) *exec.Cmd {
|
||||||
cmd := reexec.Command(args...)
|
cmd := reexec.Command(args...)
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@@ -196,5 +196,5 @@ func createReexecCommand(args []string) (*exec.Cmd, error) {
|
|||||||
syscall.CLONE_NEWNET,
|
syscall.CLONE_NEWNET,
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd, nil
|
return cmd
|
||||||
}
|
}
|
||||||
@@ -17,11 +17,14 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
package ldconfig
|
package ldcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/moby/sys/reexec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pivotRoot(newroot string) error {
|
func pivotRoot(newroot string) error {
|
||||||
@@ -36,6 +39,13 @@ func mountProc(newroot string) error {
|
|||||||
return fmt.Errorf("not supported")
|
return fmt.Errorf("not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func createReexecCommand(args []string) (*exec.Cmd, error) {
|
// createReexecCommand creates a command that can be used ot trigger the reexec
|
||||||
return nil, fmt.Errorf("not supported")
|
// initializer.
|
||||||
|
func createReexecCommand(args []string) *exec.Cmd {
|
||||||
|
cmd := reexec.Command(args...)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
return cmd
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
package ldconfig
|
package ldcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
package ldconfig
|
package ldcache
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
||||||
@@ -21,16 +21,24 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/moby/sys/reexec"
|
"github.com/moby/sys/reexec"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldconfig"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ldsoconfdFilenamePattern specifies the pattern for the filename
|
||||||
|
// in ld.so.conf.d that includes references to the specified directories.
|
||||||
|
// The 00-nvcr prefix is chosen to ensure that these libraries have a
|
||||||
|
// higher precedence than other libraries on the system, but lower than
|
||||||
|
// the 00-cuda-compat that is included in some containers.
|
||||||
|
ldsoconfdFilenamePattern = "00-nvcr-*.conf"
|
||||||
|
|
||||||
reexecUpdateLdCacheCommandName = "reexec-update-ldcache"
|
reexecUpdateLdCacheCommandName = "reexec-update-ldcache"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -115,15 +123,15 @@ func (m command) run(c *cli.Context, cfg *options) error {
|
|||||||
return fmt.Errorf("failed to determined container root: %v", err)
|
return fmt.Errorf("failed to determined container root: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd, err := ldconfig.NewRunner(
|
args := []string{
|
||||||
reexecUpdateLdCacheCommandName,
|
reexecUpdateLdCacheCommandName,
|
||||||
cfg.ldconfigPath,
|
strings.TrimPrefix(config.NormalizeLDConfigPath("@"+cfg.ldconfigPath), "@"),
|
||||||
containerRootDir,
|
containerRootDir,
|
||||||
cfg.folders.Value()...,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
args = append(args, cfg.folders.Value()...)
|
||||||
|
|
||||||
|
cmd := createReexecCommand(args)
|
||||||
|
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,16 +143,15 @@ func updateLdCacheHandler() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLdCache ensures that the ldcache in the container is updated to include
|
// updateLdCache is invoked from a reexec'd handler and provides namespace
|
||||||
// libraries that are mounted from the host.
|
// isolation for the operations performed by this hook.
|
||||||
// It is invoked from a reexec'd handler and provides namespace isolation for
|
// At the point where this is invoked, we are in a new mount namespace that is
|
||||||
// the operations performed by this hook. At the point where this is invoked,
|
// cloned from the parent.
|
||||||
// we are in a new mount namespace that is cloned from the parent.
|
|
||||||
//
|
//
|
||||||
// args[0] is the reexec initializer function name
|
// args[0] is the reexec initializer function name
|
||||||
// args[1] is the path of the ldconfig binary on the host
|
// args[1] is the path of the ldconfig binary on the host
|
||||||
// args[2] is the container root directory
|
// args[2] is the container root directory
|
||||||
// The remaining args are folders where soname symlinks need to be created.
|
// The remaining args are folders that need to be added to the ldcache.
|
||||||
func updateLdCache(args []string) error {
|
func updateLdCache(args []string) error {
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
return fmt.Errorf("incorrect arguments: %v", args)
|
return fmt.Errorf("incorrect arguments: %v", args)
|
||||||
@@ -152,13 +159,97 @@ func updateLdCache(args []string) error {
|
|||||||
hostLdconfigPath := args[1]
|
hostLdconfigPath := args[1]
|
||||||
containerRootDirPath := args[2]
|
containerRootDirPath := args[2]
|
||||||
|
|
||||||
ldconfig, err := ldconfig.New(
|
// To prevent leaking the parent proc filesystem, we create a new proc mount
|
||||||
hostLdconfigPath,
|
// in the container root.
|
||||||
containerRootDirPath,
|
if err := mountProc(containerRootDirPath); err != nil {
|
||||||
)
|
return fmt.Errorf("error mounting /proc: %w", err)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to construct ldconfig runner: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ldconfig.UpdateLDCache(args[3:]...)
|
// We mount the host ldconfig before we pivot root since host paths are not
|
||||||
|
// visible after the pivot root operation.
|
||||||
|
ldconfigPath, err := mountLdConfig(hostLdconfigPath, containerRootDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error mounting host ldconfig: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We pivot to the container root for the new process, this further limits
|
||||||
|
// access to the host.
|
||||||
|
if err := pivotRoot(containerRootDirPath); err != nil {
|
||||||
|
return fmt.Errorf("error running pivot_root: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runLdconfig(ldconfigPath, args[3:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runLdconfig runs the ldconfig binary and ensures that the specified directories
|
||||||
|
// are processed for the ldcache.
|
||||||
|
func runLdconfig(ldconfigPath string, directories ...string) error {
|
||||||
|
args := []string{
|
||||||
|
"ldconfig",
|
||||||
|
// Explicitly specify using /etc/ld.so.conf since the host's ldconfig may
|
||||||
|
// be configured to use a different config file by default.
|
||||||
|
// Note that since we apply the `-r {{ .containerRootDir }}` argument, /etc/ld.so.conf is
|
||||||
|
// in the container.
|
||||||
|
"-f", "/etc/ld.so.conf",
|
||||||
|
}
|
||||||
|
|
||||||
|
containerRoot := containerRoot("/")
|
||||||
|
|
||||||
|
if containerRoot.hasPath("/etc/ld.so.cache") {
|
||||||
|
args = append(args, "-C", "/etc/ld.so.cache")
|
||||||
|
} else {
|
||||||
|
args = append(args, "-N")
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerRoot.hasPath("/etc/ld.so.conf.d") {
|
||||||
|
err := createLdsoconfdFile(ldsoconfdFilenamePattern, directories...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update ld.so.conf.d: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args = append(args, directories...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SafeExec(ldconfigPath, args, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createLdsoconfdFile creates a file at /etc/ld.so.conf.d/.
|
||||||
|
// The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
|
||||||
|
// contains the specified directories on each line.
|
||||||
|
func createLdsoconfdFile(pattern string, dirs ...string) error {
|
||||||
|
if len(dirs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ldsoconfdDir := "/etc/ld.so.conf.d"
|
||||||
|
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create config file: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = configFile.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
added := make(map[string]bool)
|
||||||
|
for _, dir := range dirs {
|
||||||
|
if added[dir] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(configFile, "%s\n", dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update config file: %w", err)
|
||||||
|
}
|
||||||
|
added[dir] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The created file needs to be world readable for the cases where the container is run as a non-root user.
|
||||||
|
if err := configFile.Chmod(0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to chmod config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,14 +242,7 @@ func (hookConfig *hookConfig) getNvidiaConfig(image image.CUDA, privileged bool)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hookConfig *hookConfig) getContainerConfig() (config *containerConfig) {
|
func (hookConfig *hookConfig) getContainerConfig() (config containerConfig) {
|
||||||
hookConfig.Lock()
|
|
||||||
defer hookConfig.Unlock()
|
|
||||||
|
|
||||||
if hookConfig.containerConfig != nil {
|
|
||||||
return hookConfig.containerConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
var h HookState
|
var h HookState
|
||||||
d := json.NewDecoder(os.Stdin)
|
d := json.NewDecoder(os.Stdin)
|
||||||
if err := d.Decode(&h); err != nil {
|
if err := d.Decode(&h); err != nil {
|
||||||
@@ -278,13 +271,10 @@ func (hookConfig *hookConfig) getContainerConfig() (config *containerConfig) {
|
|||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cc := containerConfig{
|
return containerConfig{
|
||||||
Pid: h.Pid,
|
Pid: h.Pid,
|
||||||
Rootfs: s.Root.Path,
|
Rootfs: s.Root.Path,
|
||||||
Image: i,
|
Image: i,
|
||||||
Nvidia: hookConfig.getNvidiaConfig(i, privileged),
|
Nvidia: hookConfig.getNvidiaConfig(i, privileged),
|
||||||
}
|
}
|
||||||
hookConfig.containerConfig = &cc
|
|
||||||
|
|
||||||
return hookConfig.containerConfig
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ func TestGetNvidiaConfig(t *testing.T) {
|
|||||||
hookCfg := tc.hookConfig
|
hookCfg := tc.hookConfig
|
||||||
if hookCfg == nil {
|
if hookCfg == nil {
|
||||||
defaultConfig, _ := config.GetDefault()
|
defaultConfig, _ := config.GetDefault()
|
||||||
hookCfg = &hookConfig{Config: defaultConfig}
|
hookCfg = &hookConfig{defaultConfig}
|
||||||
}
|
}
|
||||||
cfg = hookCfg.getNvidiaConfig(image, tc.privileged)
|
cfg = hookCfg.getNvidiaConfig(image, tc.privileged)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,46 +4,50 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configPath = "/etc/nvidia-container-runtime/config.toml"
|
||||||
|
driverPath = "/run/nvidia/driver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// hookConfig wraps the toolkit config.
|
// hookConfig wraps the toolkit config.
|
||||||
// This allows for functions to be defined on the local type.
|
// This allows for functions to be defined on the local type.
|
||||||
type hookConfig struct {
|
type hookConfig struct {
|
||||||
sync.Mutex
|
|
||||||
*config.Config
|
*config.Config
|
||||||
containerConfig *containerConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadConfig loads the required paths for the hook config.
|
// loadConfig loads the required paths for the hook config.
|
||||||
func loadConfig() (*config.Config, error) {
|
func loadConfig() (*config.Config, error) {
|
||||||
configFilePath, required := getConfigFilePath()
|
var configPaths []string
|
||||||
cfg, err := config.New(
|
var required bool
|
||||||
config.WithConfigFile(configFilePath),
|
if len(*configflag) != 0 {
|
||||||
config.WithRequired(true),
|
configPaths = append(configPaths, *configflag)
|
||||||
)
|
required = true
|
||||||
if err == nil {
|
} else {
|
||||||
return cfg.Config()
|
configPaths = append(configPaths, path.Join(driverPath, configPath), configPath)
|
||||||
} else if os.IsNotExist(err) && !required {
|
|
||||||
return config.GetDefault()
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("couldn't open required configuration file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getConfigFilePath() (string, bool) {
|
for _, p := range configPaths {
|
||||||
if configFromFlag := *configflag; configFromFlag != "" {
|
cfg, err := config.New(
|
||||||
return configFromFlag, true
|
config.WithConfigFile(p),
|
||||||
|
config.WithRequired(true),
|
||||||
|
)
|
||||||
|
if err == nil {
|
||||||
|
return cfg.Config()
|
||||||
|
} else if os.IsNotExist(err) && !required {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("couldn't open required configuration file: %v", err)
|
||||||
}
|
}
|
||||||
if configFromEnvvar := os.Getenv(config.FilePathOverrideEnvVar); configFromEnvvar != "" {
|
|
||||||
return configFromEnvvar, true
|
return config.GetDefault()
|
||||||
}
|
|
||||||
return config.GetConfigFilePath(), false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHookConfig() (*hookConfig, error) {
|
func getHookConfig() (*hookConfig, error) {
|
||||||
@@ -51,7 +55,7 @@ func getHookConfig() (*hookConfig, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load config: %v", err)
|
return nil, fmt.Errorf("failed to load config: %v", err)
|
||||||
}
|
}
|
||||||
config := &hookConfig{Config: cfg}
|
config := &hookConfig{cfg}
|
||||||
|
|
||||||
allSupportedDriverCapabilities := image.SupportedDriverCapabilities
|
allSupportedDriverCapabilities := image.SupportedDriverCapabilities
|
||||||
if config.SupportedDriverCapabilities == "all" {
|
if config.SupportedDriverCapabilities == "all" {
|
||||||
@@ -69,8 +73,8 @@ func getHookConfig() (*hookConfig, error) {
|
|||||||
|
|
||||||
// getConfigOption returns the toml config option associated with the
|
// getConfigOption returns the toml config option associated with the
|
||||||
// specified struct field.
|
// specified struct field.
|
||||||
func (c *hookConfig) getConfigOption(fieldName string) string {
|
func (c hookConfig) getConfigOption(fieldName string) string {
|
||||||
t := reflect.TypeOf(&c)
|
t := reflect.TypeOf(c)
|
||||||
f, ok := t.FieldByName(fieldName)
|
f, ok := t.FieldByName(fieldName)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fieldName
|
return fieldName
|
||||||
@@ -123,21 +127,3 @@ func (c *hookConfig) nvidiaContainerCliCUDACompatModeFlags() []string {
|
|||||||
}
|
}
|
||||||
return []string{flag}
|
return []string{flag}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *hookConfig) assertModeIsLegacy() error {
|
|
||||||
if c.NVIDIAContainerRuntimeHookConfig.SkipModeDetection {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mr := info.NewRuntimeModeResolver(
|
|
||||||
info.WithLogger(&logInterceptor{}),
|
|
||||||
info.WithImage(&c.containerConfig.Image),
|
|
||||||
info.WithDefaultMode(info.LegacyRuntimeMode),
|
|
||||||
)
|
|
||||||
|
|
||||||
mode := mr.ResolveRuntimeMode(c.NVIDIAContainerRuntimeConfig.Mode)
|
|
||||||
if mode == "legacy" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("invoking the NVIDIA Container Runtime Hook directly (e.g. specifying the docker --gpus flag) is not supported. Please use the NVIDIA Container Runtime (e.g. specify the --runtime=nvidia flag) instead")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ func TestGetHookConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg *hookConfig
|
var cfg hookConfig
|
||||||
getHookConfig := func() {
|
getHookConfig := func() {
|
||||||
c, _ := getHookConfig()
|
c, _ := getHookConfig()
|
||||||
cfg = c
|
cfg = *c
|
||||||
}
|
}
|
||||||
|
|
||||||
if tc.expectedPanic {
|
if tc.expectedPanic {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func getCLIPath(config config.ContainerCLIConfig) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getRootfsPath returns an absolute path. We don't need to resolve symlinks for now.
|
// getRootfsPath returns an absolute path. We don't need to resolve symlinks for now.
|
||||||
func getRootfsPath(config *containerConfig) string {
|
func getRootfsPath(config containerConfig) string {
|
||||||
rootfs, err := filepath.Abs(config.Rootfs)
|
rootfs, err := filepath.Abs(config.Rootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
@@ -82,8 +82,8 @@ func doPrestart() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hook.assertModeIsLegacy(); err != nil {
|
if !hook.NVIDIAContainerRuntimeHookConfig.SkipModeDetection && info.ResolveAutoMode(&logInterceptor{}, hook.NVIDIAContainerRuntimeConfig.Mode, container.Image) != "legacy" {
|
||||||
log.Panicf("%v", err)
|
log.Panicln("invoking the NVIDIA Container Runtime Hook directly (e.g. specifying the docker --gpus flag) is not supported. Please use the NVIDIA Container Runtime (e.g. specify the --runtime=nvidia flag) instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs := getRootfsPath(container)
|
rootfs := getRootfsPath(container)
|
||||||
|
|||||||
@@ -122,10 +122,11 @@ func TestGoodInput(t *testing.T) {
|
|||||||
err = cmdCreate.Run()
|
err = cmdCreate.Run()
|
||||||
require.NoError(t, err, "runtime should not return an error")
|
require.NoError(t, err, "runtime should not return an error")
|
||||||
|
|
||||||
// Check config.json to ensure that the NVIDIA prestart was not inserted.
|
// Check config.json for NVIDIA prestart hook
|
||||||
spec, err = cfg.getRuntimeSpec()
|
spec, err = cfg.getRuntimeSpec()
|
||||||
require.NoError(t, err, "should be no errors when reading and parsing spec from config.json")
|
require.NoError(t, err, "should be no errors when reading and parsing spec from config.json")
|
||||||
require.Empty(t, spec.Hooks, "there should be no hooks in config.json")
|
require.NotEmpty(t, spec.Hooks, "there should be hooks in config.json")
|
||||||
|
require.Equal(t, 1, nvidiaHookCount(spec.Hooks), "exactly one nvidia prestart hook should be inserted correctly into config.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NVIDIA prestart hook already present in config file
|
// NVIDIA prestart hook already present in config file
|
||||||
@@ -167,10 +168,11 @@ func TestDuplicateHook(t *testing.T) {
|
|||||||
output, err := cmdCreate.CombinedOutput()
|
output, err := cmdCreate.CombinedOutput()
|
||||||
require.NoErrorf(t, err, "runtime should not return an error", "output=%v", string(output))
|
require.NoErrorf(t, err, "runtime should not return an error", "output=%v", string(output))
|
||||||
|
|
||||||
// Check config.json to ensure that the NVIDIA prestart hook was removed.
|
// Check config.json for NVIDIA prestart hook
|
||||||
spec, err = cfg.getRuntimeSpec()
|
spec, err = cfg.getRuntimeSpec()
|
||||||
require.NoError(t, err, "should be no errors when reading and parsing spec from config.json")
|
require.NoError(t, err, "should be no errors when reading and parsing spec from config.json")
|
||||||
require.Empty(t, spec.Hooks, "there should be no hooks in config.json")
|
require.NotEmpty(t, spec.Hooks, "there should be hooks in config.json")
|
||||||
|
require.Equal(t, 1, nvidiaHookCount(spec.Hooks), "exactly one nvidia prestart hook should be inserted correctly into config.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
// addNVIDIAHook is a basic wrapper for an addHookModifier that is used for
|
// addNVIDIAHook is a basic wrapper for an addHookModifier that is used for
|
||||||
@@ -238,3 +240,18 @@ func (c testConfig) generateNewRuntimeSpec() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return number of valid NVIDIA prestart hooks in runtime spec
|
||||||
|
func nvidiaHookCount(hooks *specs.Hooks) int {
|
||||||
|
if hooks == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for _, hook := range hooks.Prestart {
|
||||||
|
if strings.Contains(hook.Path, nvidiaHook) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ type createDirectory struct {
|
|||||||
logger logger.Interface
|
logger logger.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ToolkitInstaller) createDirectory() Installer {
|
func (t *toolkitInstaller) createDirectory() Installer {
|
||||||
return &createDirectory{
|
return &createDirectory{
|
||||||
logger: t.logger,
|
logger: t.logger,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,18 +28,20 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/operator"
|
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/operator"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type executable struct {
|
type executable struct {
|
||||||
requiresKernelModule bool
|
requiresKernelModule bool
|
||||||
path string
|
path string
|
||||||
symlink string
|
symlink string
|
||||||
|
args []string
|
||||||
env map[string]string
|
env map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ToolkitInstaller) collectExecutables(destDir string) ([]Installer, error) {
|
func (t *toolkitInstaller) collectExecutables(destDir string) ([]Installer, error) {
|
||||||
configFilePath := t.ConfigFilePath(destDir)
|
configHome := filepath.Join(destDir, ".config")
|
||||||
|
configDir := filepath.Join(configHome, "nvidia-container-runtime")
|
||||||
|
configPath := filepath.Join(configDir, "config.toml")
|
||||||
|
|
||||||
executables := []executable{
|
executables := []executable{
|
||||||
{
|
{
|
||||||
@@ -54,7 +56,7 @@ func (t *ToolkitInstaller) collectExecutables(destDir string) ([]Installer, erro
|
|||||||
path: runtime.Path,
|
path: runtime.Path,
|
||||||
requiresKernelModule: true,
|
requiresKernelModule: true,
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
config.FilePathOverrideEnvVar: configFilePath,
|
"XDG_CONFIG_HOME": configHome,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
executables = append(executables, e)
|
executables = append(executables, e)
|
||||||
@@ -70,9 +72,7 @@ func (t *ToolkitInstaller) collectExecutables(destDir string) ([]Installer, erro
|
|||||||
executable{
|
executable{
|
||||||
path: "nvidia-container-runtime-hook",
|
path: "nvidia-container-runtime-hook",
|
||||||
symlink: "nvidia-container-toolkit",
|
symlink: "nvidia-container-toolkit",
|
||||||
env: map[string]string{
|
args: []string{fmt.Sprintf("-config %s", configPath)},
|
||||||
config.FilePathOverrideEnvVar: configFilePath,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -94,6 +94,7 @@ func (t *ToolkitInstaller) collectExecutables(destDir string) ([]Installer, erro
|
|||||||
Source: executablePath,
|
Source: executablePath,
|
||||||
WrappedExecutable: dotRealFilename,
|
WrappedExecutable: dotRealFilename,
|
||||||
CheckModules: executable.requiresKernelModule,
|
CheckModules: executable.requiresKernelModule,
|
||||||
|
Args: executable.args,
|
||||||
Envvars: map[string]string{
|
Envvars: map[string]string{
|
||||||
"PATH": strings.Join([]string{destDir, "$PATH"}, ":"),
|
"PATH": strings.Join([]string{destDir, "$PATH"}, ":"),
|
||||||
},
|
},
|
||||||
@@ -123,6 +124,7 @@ type wrapper struct {
|
|||||||
Envvars map[string]string
|
Envvars map[string]string
|
||||||
WrappedExecutable string
|
WrappedExecutable string
|
||||||
CheckModules bool
|
CheckModules bool
|
||||||
|
Args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type render struct {
|
type render struct {
|
||||||
@@ -163,6 +165,9 @@ fi
|
|||||||
{{$key}}={{$value}} \
|
{{$key}}={{$value}} \
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{ .DestDir }}/{{ .WrappedExecutable }} \
|
{{ .DestDir }}/{{ .WrappedExecutable }} \
|
||||||
|
{{- range $arg := .Args }}
|
||||||
|
{{$arg}} \
|
||||||
|
{{- end }}
|
||||||
"$@"
|
"$@"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,19 @@ fi
|
|||||||
PATH=/foo/bar/baz \
|
PATH=/foo/bar/baz \
|
||||||
/dest-dir/some-runtime \
|
/dest-dir/some-runtime \
|
||||||
"$@"
|
"$@"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "args are added",
|
||||||
|
w: &wrapper{
|
||||||
|
WrappedExecutable: "some-runtime",
|
||||||
|
Args: []string{"--config foo", "bar"},
|
||||||
|
},
|
||||||
|
expected: `#! /bin/sh
|
||||||
|
/dest-dir/some-runtime \
|
||||||
|
--config foo \
|
||||||
|
bar \
|
||||||
|
"$@"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type Installer interface {
|
|||||||
Install(string) error
|
Install(string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToolkitInstaller struct {
|
type toolkitInstaller struct {
|
||||||
logger logger.Interface
|
logger logger.Interface
|
||||||
ignoreErrors bool
|
ignoreErrors bool
|
||||||
sourceRoot string
|
sourceRoot string
|
||||||
@@ -43,11 +43,11 @@ type ToolkitInstaller struct {
|
|||||||
ensureTargetDirectory Installer
|
ensureTargetDirectory Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Installer = (*ToolkitInstaller)(nil)
|
var _ Installer = (*toolkitInstaller)(nil)
|
||||||
|
|
||||||
// New creates a toolkit installer with the specified options.
|
// New creates a toolkit installer with the specified options.
|
||||||
func New(opts ...Option) (*ToolkitInstaller, error) {
|
func New(opts ...Option) (Installer, error) {
|
||||||
t := &ToolkitInstaller{
|
t := &toolkitInstaller{
|
||||||
sourceRoot: "/",
|
sourceRoot: "/",
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
@@ -73,7 +73,7 @@ func New(opts ...Option) (*ToolkitInstaller, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Install ensures that the required toolkit files are installed in the specified directory.
|
// Install ensures that the required toolkit files are installed in the specified directory.
|
||||||
func (t *ToolkitInstaller) Install(destDir string) error {
|
func (t *toolkitInstaller) Install(destDir string) error {
|
||||||
var installers []Installer
|
var installers []Installer
|
||||||
|
|
||||||
installers = append(installers, t.ensureTargetDirectory)
|
installers = append(installers, t.ensureTargetDirectory)
|
||||||
@@ -98,11 +98,6 @@ func (t *ToolkitInstaller) Install(destDir string) error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ToolkitInstaller) ConfigFilePath(destDir string) string {
|
|
||||||
toolkitConfigDir := filepath.Join(destDir, ".config", "nvidia-container-runtime")
|
|
||||||
return filepath.Join(toolkitConfigDir, "config.toml")
|
|
||||||
}
|
|
||||||
|
|
||||||
type symlink struct {
|
type symlink struct {
|
||||||
linkname string
|
linkname string
|
||||||
target string
|
target string
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func TestToolkitInstaller(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
i := ToolkitInstaller{
|
i := toolkitInstaller{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
artifactRoot: r,
|
artifactRoot: r,
|
||||||
ensureTargetDirectory: createDirectory,
|
ensureTargetDirectory: createDirectory,
|
||||||
@@ -172,8 +172,8 @@ if [ "${?}" != "0" ]; then
|
|||||||
echo "nvidia driver modules are not yet loaded, invoking runc directly"
|
echo "nvidia driver modules are not yet loaded, invoking runc directly"
|
||||||
exec runc "$@"
|
exec runc "$@"
|
||||||
fi
|
fi
|
||||||
NVIDIA_CTK_CONFIG_FILE_PATH=/foo/bar/baz/.config/nvidia-container-runtime/config.toml \
|
|
||||||
PATH=/foo/bar/baz:$PATH \
|
PATH=/foo/bar/baz:$PATH \
|
||||||
|
XDG_CONFIG_HOME=/foo/bar/baz/.config \
|
||||||
/foo/bar/baz/nvidia-container-runtime.real \
|
/foo/bar/baz/nvidia-container-runtime.real \
|
||||||
"$@"
|
"$@"
|
||||||
`,
|
`,
|
||||||
@@ -187,8 +187,8 @@ if [ "${?}" != "0" ]; then
|
|||||||
echo "nvidia driver modules are not yet loaded, invoking runc directly"
|
echo "nvidia driver modules are not yet loaded, invoking runc directly"
|
||||||
exec runc "$@"
|
exec runc "$@"
|
||||||
fi
|
fi
|
||||||
NVIDIA_CTK_CONFIG_FILE_PATH=/foo/bar/baz/.config/nvidia-container-runtime/config.toml \
|
|
||||||
PATH=/foo/bar/baz:$PATH \
|
PATH=/foo/bar/baz:$PATH \
|
||||||
|
XDG_CONFIG_HOME=/foo/bar/baz/.config \
|
||||||
/foo/bar/baz/nvidia-container-runtime.cdi.real \
|
/foo/bar/baz/nvidia-container-runtime.cdi.real \
|
||||||
"$@"
|
"$@"
|
||||||
`,
|
`,
|
||||||
@@ -202,8 +202,8 @@ if [ "${?}" != "0" ]; then
|
|||||||
echo "nvidia driver modules are not yet loaded, invoking runc directly"
|
echo "nvidia driver modules are not yet loaded, invoking runc directly"
|
||||||
exec runc "$@"
|
exec runc "$@"
|
||||||
fi
|
fi
|
||||||
NVIDIA_CTK_CONFIG_FILE_PATH=/foo/bar/baz/.config/nvidia-container-runtime/config.toml \
|
|
||||||
PATH=/foo/bar/baz:$PATH \
|
PATH=/foo/bar/baz:$PATH \
|
||||||
|
XDG_CONFIG_HOME=/foo/bar/baz/.config \
|
||||||
/foo/bar/baz/nvidia-container-runtime.legacy.real \
|
/foo/bar/baz/nvidia-container-runtime.legacy.real \
|
||||||
"$@"
|
"$@"
|
||||||
`,
|
`,
|
||||||
@@ -240,9 +240,9 @@ PATH=/foo/bar/baz:$PATH \
|
|||||||
path: "/foo/bar/baz/nvidia-container-runtime-hook",
|
path: "/foo/bar/baz/nvidia-container-runtime-hook",
|
||||||
mode: 0777,
|
mode: 0777,
|
||||||
wrapper: `#! /bin/sh
|
wrapper: `#! /bin/sh
|
||||||
NVIDIA_CTK_CONFIG_FILE_PATH=/foo/bar/baz/.config/nvidia-container-runtime/config.toml \
|
|
||||||
PATH=/foo/bar/baz:$PATH \
|
PATH=/foo/bar/baz:$PATH \
|
||||||
/foo/bar/baz/nvidia-container-runtime-hook.real \
|
/foo/bar/baz/nvidia-container-runtime-hook.real \
|
||||||
|
-config /foo/bar/baz/.config/nvidia-container-runtime/config.toml \
|
||||||
"$@"
|
"$@"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
// A predefined set of library candidates are considered, with the first one
|
// A predefined set of library candidates are considered, with the first one
|
||||||
// resulting in success being installed to the toolkit folder. The install process
|
// resulting in success being installed to the toolkit folder. The install process
|
||||||
// resolves the symlink for the library and copies the versioned library itself.
|
// resolves the symlink for the library and copies the versioned library itself.
|
||||||
func (t *ToolkitInstaller) collectLibraries() ([]Installer, error) {
|
func (t *toolkitInstaller) collectLibraries() ([]Installer, error) {
|
||||||
requiredLibraries := []string{
|
requiredLibraries := []string{
|
||||||
"libnvidia-container.so.1",
|
"libnvidia-container.so.1",
|
||||||
"libnvidia-container-go.so.1",
|
"libnvidia-container-go.so.1",
|
||||||
|
|||||||
@@ -19,29 +19,29 @@ package installer
|
|||||||
|
|
||||||
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
|
|
||||||
type Option func(*ToolkitInstaller)
|
type Option func(*toolkitInstaller)
|
||||||
|
|
||||||
func WithLogger(logger logger.Interface) Option {
|
func WithLogger(logger logger.Interface) Option {
|
||||||
return func(ti *ToolkitInstaller) {
|
return func(ti *toolkitInstaller) {
|
||||||
ti.logger = logger
|
ti.logger = logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithArtifactRoot(artifactRoot *artifactRoot) Option {
|
func WithArtifactRoot(artifactRoot *artifactRoot) Option {
|
||||||
return func(ti *ToolkitInstaller) {
|
return func(ti *toolkitInstaller) {
|
||||||
ti.artifactRoot = artifactRoot
|
ti.artifactRoot = artifactRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithIgnoreErrors(ignoreErrors bool) Option {
|
func WithIgnoreErrors(ignoreErrors bool) Option {
|
||||||
return func(ti *ToolkitInstaller) {
|
return func(ti *toolkitInstaller) {
|
||||||
ti.ignoreErrors = ignoreErrors
|
ti.ignoreErrors = ignoreErrors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSourceRoot sets the root directory for locating artifacts to be installed.
|
// WithSourceRoot sets the root directory for locating artifacts to be installed.
|
||||||
func WithSourceRoot(sourceRoot string) Option {
|
func WithSourceRoot(sourceRoot string) Option {
|
||||||
return func(ti *ToolkitInstaller) {
|
return func(ti *toolkitInstaller) {
|
||||||
ti.sourceRoot = sourceRoot
|
ti.sourceRoot = sourceRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// DefaultNvidiaDriverRoot specifies the default NVIDIA driver run directory
|
// DefaultNvidiaDriverRoot specifies the default NVIDIA driver run directory
|
||||||
DefaultNvidiaDriverRoot = "/run/nvidia/driver"
|
DefaultNvidiaDriverRoot = "/run/nvidia/driver"
|
||||||
|
|
||||||
|
configFilename = "config.toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cdiOptions struct {
|
type cdiOptions struct {
|
||||||
@@ -314,7 +316,7 @@ func (t *Installer) Install(cli *cli.Context, opts *Options) error {
|
|||||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("could not install toolkit components: %w", err))
|
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("could not install toolkit components: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.installToolkitConfig(cli, opts, toolkit.ConfigFilePath(t.toolkitRoot))
|
err = t.installToolkitConfig(cli, opts)
|
||||||
if err != nil && !opts.ignoreErrors {
|
if err != nil && !opts.ignoreErrors {
|
||||||
return fmt.Errorf("error installing NVIDIA container toolkit config: %v", err)
|
return fmt.Errorf("error installing NVIDIA container toolkit config: %v", err)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -341,11 +343,13 @@ func (t *Installer) Install(cli *cli.Context, opts *Options) error {
|
|||||||
|
|
||||||
// installToolkitConfig installs the config file for the NVIDIA container toolkit ensuring
|
// installToolkitConfig installs the config file for the NVIDIA container toolkit ensuring
|
||||||
// that the settings are updated to match the desired install and nvidia driver directories.
|
// that the settings are updated to match the desired install and nvidia driver directories.
|
||||||
func (t *Installer) installToolkitConfig(c *cli.Context, opts *Options, toolkitConfigPath string) error {
|
func (t *Installer) installToolkitConfig(c *cli.Context, opts *Options) error {
|
||||||
|
toolkitConfigDir := filepath.Join(t.toolkitRoot, ".config", "nvidia-container-runtime")
|
||||||
|
toolkitConfigPath := filepath.Join(toolkitConfigDir, configFilename)
|
||||||
|
|
||||||
t.logger.Infof("Installing NVIDIA container toolkit config '%v'", toolkitConfigPath)
|
t.logger.Infof("Installing NVIDIA container toolkit config '%v'", toolkitConfigPath)
|
||||||
|
|
||||||
err := t.createDirectories(filepath.Dir(toolkitConfigPath))
|
err := t.createDirectories(toolkitConfigDir)
|
||||||
if err != nil && !opts.ignoreErrors {
|
if err != nil && !opts.ignoreErrors {
|
||||||
return fmt.Errorf("could not create required directories: %v", err)
|
return fmt.Errorf("could not create required directories: %v", err)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ devices:
|
|||||||
hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel2047
|
hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel2047
|
||||||
containerEdits:
|
containerEdits:
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_LIBCUDA_DIR=/lib/x86_64-linux-gnu
|
|
||||||
- NVIDIA_VISIBLE_DEVICES=void
|
- NVIDIA_VISIBLE_DEVICES=void
|
||||||
hooks:
|
hooks:
|
||||||
- hookName: createContainer
|
- hookName: createContainer
|
||||||
@@ -98,15 +97,6 @@ containerEdits:
|
|||||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_DEBUG=false
|
- NVIDIA_CTK_DEBUG=false
|
||||||
- hookName: createContainer
|
|
||||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
|
||||||
args:
|
|
||||||
- nvidia-cdi-hook
|
|
||||||
- create-soname-symlinks
|
|
||||||
- --folder
|
|
||||||
- /lib/x86_64-linux-gnu
|
|
||||||
env:
|
|
||||||
- NVIDIA_CTK_DEBUG=false
|
|
||||||
- hookName: createContainer
|
- hookName: createContainer
|
||||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||||
args:
|
args:
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ devices:
|
|||||||
hostPath: {{ .driverRoot }}/dev/nvidia0
|
hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||||
containerEdits:
|
containerEdits:
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_LIBCUDA_DIR=/lib/x86_64-linux-gnu
|
|
||||||
- NVIDIA_VISIBLE_DEVICES=void
|
- NVIDIA_VISIBLE_DEVICES=void
|
||||||
deviceNodes:
|
deviceNodes:
|
||||||
- path: /dev/nvidiactl
|
- path: /dev/nvidiactl
|
||||||
@@ -103,15 +102,6 @@ containerEdits:
|
|||||||
- --host-driver-version=999.88.77
|
- --host-driver-version=999.88.77
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_DEBUG=false
|
- NVIDIA_CTK_DEBUG=false
|
||||||
- hookName: createContainer
|
|
||||||
path: /usr/bin/nvidia-cdi-hook
|
|
||||||
args:
|
|
||||||
- nvidia-cdi-hook
|
|
||||||
- create-soname-symlinks
|
|
||||||
- --folder
|
|
||||||
- /lib/x86_64-linux-gnu
|
|
||||||
env:
|
|
||||||
- NVIDIA_CTK_DEBUG=false
|
|
||||||
- hookName: createContainer
|
- hookName: createContainer
|
||||||
path: /usr/bin/nvidia-cdi-hook
|
path: /usr/bin/nvidia-cdi-hook
|
||||||
args:
|
args:
|
||||||
@@ -174,7 +164,6 @@ devices:
|
|||||||
hostPath: {{ .driverRoot }}/dev/nvidia0
|
hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||||
containerEdits:
|
containerEdits:
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_LIBCUDA_DIR=/lib/x86_64-linux-gnu
|
|
||||||
- NVIDIA_VISIBLE_DEVICES=void
|
- NVIDIA_VISIBLE_DEVICES=void
|
||||||
deviceNodes:
|
deviceNodes:
|
||||||
- path: /dev/nvidiactl
|
- path: /dev/nvidiactl
|
||||||
@@ -189,15 +178,6 @@ containerEdits:
|
|||||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_DEBUG=false
|
- NVIDIA_CTK_DEBUG=false
|
||||||
- hookName: createContainer
|
|
||||||
path: /usr/bin/nvidia-cdi-hook
|
|
||||||
args:
|
|
||||||
- nvidia-cdi-hook
|
|
||||||
- create-soname-symlinks
|
|
||||||
- --folder
|
|
||||||
- /lib/x86_64-linux-gnu
|
|
||||||
env:
|
|
||||||
- NVIDIA_CTK_DEBUG=false
|
|
||||||
- hookName: createContainer
|
- hookName: createContainer
|
||||||
path: /usr/bin/nvidia-cdi-hook
|
path: /usr/bin/nvidia-cdi-hook
|
||||||
args:
|
args:
|
||||||
@@ -260,7 +240,6 @@ devices:
|
|||||||
hostPath: {{ .driverRoot }}/dev/nvidia0
|
hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||||
containerEdits:
|
containerEdits:
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_LIBCUDA_DIR=/lib/x86_64-linux-gnu
|
|
||||||
- NVIDIA_VISIBLE_DEVICES=void
|
- NVIDIA_VISIBLE_DEVICES=void
|
||||||
deviceNodes:
|
deviceNodes:
|
||||||
- path: /dev/nvidiactl
|
- path: /dev/nvidiactl
|
||||||
@@ -275,15 +254,6 @@ containerEdits:
|
|||||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_DEBUG=false
|
- NVIDIA_CTK_DEBUG=false
|
||||||
- hookName: createContainer
|
|
||||||
path: /usr/bin/nvidia-cdi-hook
|
|
||||||
args:
|
|
||||||
- nvidia-cdi-hook
|
|
||||||
- create-soname-symlinks
|
|
||||||
- --folder
|
|
||||||
- /lib/x86_64-linux-gnu
|
|
||||||
env:
|
|
||||||
- NVIDIA_CTK_DEBUG=false
|
|
||||||
- hookName: createContainer
|
- hookName: createContainer
|
||||||
path: /usr/bin/nvidia-cdi-hook
|
path: /usr/bin/nvidia-cdi-hook
|
||||||
args:
|
args:
|
||||||
@@ -337,7 +307,6 @@ devices:
|
|||||||
hostPath: {{ .driverRoot }}/dev/nvidia0
|
hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||||
containerEdits:
|
containerEdits:
|
||||||
env:
|
env:
|
||||||
- NVIDIA_CTK_LIBCUDA_DIR=/lib/x86_64-linux-gnu
|
|
||||||
- NVIDIA_VISIBLE_DEVICES=void
|
- NVIDIA_VISIBLE_DEVICES=void
|
||||||
deviceNodes:
|
deviceNodes:
|
||||||
- path: /dev/nvidiactl
|
- path: /dev/nvidiactl
|
||||||
|
|||||||
@@ -48,18 +48,14 @@ ARG VERSION="N/A"
|
|||||||
ARG GIT_COMMIT="unknown"
|
ARG GIT_COMMIT="unknown"
|
||||||
RUN make PREFIX=/artifacts/bin cmd-nvidia-ctk-installer
|
RUN make PREFIX=/artifacts/bin cmd-nvidia-ctk-installer
|
||||||
|
|
||||||
# The packaging stage collects the deb and rpm packages built for
|
# The packaging stage collects the deb and rpm packages built for supported
|
||||||
# supported architectures.
|
# architectures.
|
||||||
FROM nvcr.io/nvidia/distroless/go:v3.1.9-dev AS packaging
|
FROM nvcr.io/nvidia/cuda:12.9.0-base-ubi9 AS packaging
|
||||||
|
|
||||||
USER 0:0
|
|
||||||
SHELL ["/busybox/sh", "-c"]
|
|
||||||
RUN ln -s /busybox/sh /bin/sh
|
|
||||||
|
|
||||||
ARG ARTIFACTS_ROOT
|
ARG ARTIFACTS_ROOT
|
||||||
COPY ${ARTIFACTS_ROOT} /artifacts/packages/
|
COPY ${ARTIFACTS_ROOT} /artifacts/packages/
|
||||||
|
|
||||||
WORKDIR /artifacts
|
WORKDIR /artifacts/packages
|
||||||
|
|
||||||
# build-args are added to the manifest.txt file below.
|
# build-args are added to the manifest.txt file below.
|
||||||
ARG PACKAGE_VERSION
|
ARG PACKAGE_VERSION
|
||||||
@@ -74,14 +70,7 @@ RUN echo "#IMAGE_EPOCH=$(date '+%s')" > /artifacts/manifest.txt && \
|
|||||||
env | sed 's/^/#/g' >> /artifacts/manifest.txt && \
|
env | sed 's/^/#/g' >> /artifacts/manifest.txt && \
|
||||||
find /artifacts/packages -iname '*.deb' -o -iname '*.rpm' >> /artifacts/manifest.txt
|
find /artifacts/packages -iname '*.deb' -o -iname '*.rpm' >> /artifacts/manifest.txt
|
||||||
|
|
||||||
LABEL name="NVIDIA Container Toolkit Packages"
|
RUN mkdir /licenses && mv /NGC-DL-CONTAINER-LICENSE /licenses/NGC-DL-CONTAINER-LICENSE
|
||||||
LABEL vendor="NVIDIA"
|
|
||||||
LABEL version="${VERSION}"
|
|
||||||
LABEL release="N/A"
|
|
||||||
LABEL summary="deb and rpm packages for the NVIDIA Container Toolkit"
|
|
||||||
LABEL description="See summary"
|
|
||||||
|
|
||||||
COPY LICENSE /licenses/
|
|
||||||
|
|
||||||
# The debpackages stage is used to extract the contents of deb packages.
|
# The debpackages stage is used to extract the contents of deb packages.
|
||||||
FROM nvcr.io/nvidia/cuda:12.9.0-base-ubuntu20.04 AS debpackages
|
FROM nvcr.io/nvidia/cuda:12.9.0-base-ubuntu20.04 AS debpackages
|
||||||
@@ -127,19 +116,13 @@ RUN set -eux; \
|
|||||||
# - The extracted deb packages
|
# - The extracted deb packages
|
||||||
# - The extracted rpm packages
|
# - The extracted rpm packages
|
||||||
# - The nvidia-ctk-installer binary
|
# - The nvidia-ctk-installer binary
|
||||||
FROM scratch AS artifacts
|
FROM nvcr.io/nvidia/cuda:12.9.0-base-ubi9 AS artifacts
|
||||||
|
|
||||||
COPY --from=rpmpackages /artifacts/rpm /artifacts/rpm
|
COPY --from=rpmpackages /artifacts/rpm /artifacts/rpm
|
||||||
COPY --from=debpackages /artifacts/deb /artifacts/deb
|
COPY --from=debpackages /artifacts/deb /artifacts/deb
|
||||||
COPY --from=build /artifacts/bin /artifacts/build
|
COPY --from=build /artifacts/bin /artifacts/build
|
||||||
|
|
||||||
# The application stage contains the application used as a GPU Operator
|
FROM nvcr.io/nvidia/cuda:12.9.0-base-ubi9
|
||||||
# operand.
|
|
||||||
FROM nvcr.io/nvidia/distroless/go:v3.1.9-dev AS application
|
|
||||||
|
|
||||||
USER 0:0
|
|
||||||
SHELL ["/busybox/sh", "-c"]
|
|
||||||
RUN ln -s /busybox/sh /bin/sh
|
|
||||||
|
|
||||||
ENV NVIDIA_DISABLE_REQUIRE="true"
|
ENV NVIDIA_DISABLE_REQUIRE="true"
|
||||||
ENV NVIDIA_VISIBLE_DEVICES=void
|
ENV NVIDIA_VISIBLE_DEVICES=void
|
||||||
@@ -161,11 +144,6 @@ LABEL release="N/A"
|
|||||||
LABEL summary="Automatically Configure your Container Runtime for GPU support."
|
LABEL summary="Automatically Configure your Container Runtime for GPU support."
|
||||||
LABEL description="See summary"
|
LABEL description="See summary"
|
||||||
|
|
||||||
COPY LICENSE /licenses/
|
RUN mkdir /licenses && mv /NGC-DL-CONTAINER-LICENSE /licenses/NGC-DL-CONTAINER-LICENSE
|
||||||
|
|
||||||
ENTRYPOINT ["/work/nvidia-ctk-installer"]
|
ENTRYPOINT ["/work/nvidia-ctk-installer"]
|
||||||
|
|
||||||
# The GPU Operator exec's nvidia-toolkit in its entrypoint.
|
|
||||||
# We create a symlink here to ensure compatibility with older
|
|
||||||
# GPU Operator versions.
|
|
||||||
RUN ln -s /work/nvidia-ctk-installer /work/nvidia-toolkit
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ OUT_IMAGE_TAG = $(OUT_IMAGE_VERSION)
|
|||||||
OUT_IMAGE = $(OUT_IMAGE_NAME):$(OUT_IMAGE_TAG)
|
OUT_IMAGE = $(OUT_IMAGE_NAME):$(OUT_IMAGE_TAG)
|
||||||
|
|
||||||
##### Public rules #####
|
##### Public rules #####
|
||||||
DEFAULT_PUSH_TARGET := application
|
DEFAULT_PUSH_TARGET := ubi9
|
||||||
DISTRIBUTIONS := $(DEFAULT_PUSH_TARGET)
|
DISTRIBUTIONS := $(DEFAULT_PUSH_TARGET)
|
||||||
|
|
||||||
META_TARGETS := packaging
|
META_TARGETS := packaging
|
||||||
@@ -102,6 +102,8 @@ build: build-$(DEFAULT_PUSH_TARGET)
|
|||||||
push: push-$(DEFAULT_PUSH_TARGET)
|
push: push-$(DEFAULT_PUSH_TARGET)
|
||||||
|
|
||||||
# Test targets
|
# Test targets
|
||||||
|
test-%: DIST = $(*)
|
||||||
|
|
||||||
TEST_CASES ?= docker crio containerd
|
TEST_CASES ?= docker crio containerd
|
||||||
$(TEST_TARGETS): test-%:
|
$(TEST_TARGETS): test-%:
|
||||||
TEST_CASES="$(TEST_CASES)" bash -x $(CURDIR)/test/container/main.sh run \
|
TEST_CASES="$(TEST_CASES)" bash -x $(CURDIR)/test/container/main.sh run \
|
||||||
|
|||||||
@@ -57,14 +57,17 @@ WORKDIR $DIST_DIR
|
|||||||
COPY packaging/debian ./debian
|
COPY packaging/debian ./debian
|
||||||
COPY deployments/systemd/ .
|
COPY deployments/systemd/ .
|
||||||
|
|
||||||
|
ARG LIBNVIDIA_CONTAINER_TOOLS_VERSION
|
||||||
|
ENV LIBNVIDIA_CONTAINER_TOOLS_VERSION ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}
|
||||||
|
|
||||||
RUN dch --create --package="${PKG_NAME}" \
|
RUN dch --create --package="${PKG_NAME}" \
|
||||||
--newversion "${REVISION}" \
|
--newversion "${REVISION}" \
|
||||||
"See https://github.com/NVIDIA/nvidia-container-toolkit/blob/${GIT_COMMIT}/CHANGELOG.md for the changelog" && \
|
"See https://gitlab.com/nvidia/container-toolkit/container-toolkit/-/blob/${GIT_COMMIT}/CHANGELOG.md for the changelog" && \
|
||||||
dch --append "Bump libnvidia-container dependency to ${LIBNVIDIA_CONTAINER1_VERSION}" && \
|
dch --append "Bump libnvidia-container dependency to ${LIBNVIDIA_CONTAINER1_VERSION}" && \
|
||||||
dch -r "" && \
|
dch -r "" && \
|
||||||
if [ "$REVISION" != "$(dpkg-parsechangelog --show-field=Version)" ]; then exit 1; fi
|
if [ "$REVISION" != "$(dpkg-parsechangelog --show-field=Version)" ]; then exit 1; fi
|
||||||
|
|
||||||
CMD export DISTRIB="$(lsb_release -cs)" && \
|
CMD export DISTRIB="$(lsb_release -cs)" && \
|
||||||
debuild -eDISTRIB -eSECTION -eVERSION="${REVISION}" \
|
debuild -eDISTRIB -eSECTION -eLIBNVIDIA_CONTAINER_TOOLS_VERSION -eVERSION="${REVISION}" \
|
||||||
--dpkg-buildpackage-hook='sh debian/prepare' -i -us -uc -b && \
|
--dpkg-buildpackage-hook='sh debian/prepare' -i -us -uc -b && \
|
||||||
mv /tmp/*.deb /dist
|
mv /tmp/*.deb /dist
|
||||||
|
|||||||
@@ -48,12 +48,16 @@ WORKDIR $DIST_DIR/..
|
|||||||
COPY packaging/rpm .
|
COPY packaging/rpm .
|
||||||
COPY deployments/systemd/ .
|
COPY deployments/systemd/ .
|
||||||
|
|
||||||
|
ARG LIBNVIDIA_CONTAINER_TOOLS_VERSION
|
||||||
|
ENV LIBNVIDIA_CONTAINER_TOOLS_VERSION ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}
|
||||||
|
|
||||||
CMD arch=$(uname -m) && \
|
CMD arch=$(uname -m) && \
|
||||||
rpmbuild --clean --target=$arch -bb \
|
rpmbuild --clean --target=$arch -bb \
|
||||||
-D "_topdir $PWD" \
|
-D "_topdir $PWD" \
|
||||||
-D "release_date $(date +'%a %b %d %Y')" \
|
-D "release_date $(date +'%a %b %d %Y')" \
|
||||||
-D "git_commit ${GIT_COMMIT}" \
|
-D "git_commit ${GIT_COMMIT}" \
|
||||||
-D "version ${PKG_VERS}" \
|
-D "version ${PKG_VERS}" \
|
||||||
|
-D "libnvidia_container_tools_version ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}" \
|
||||||
-D "release ${PKG_REV}" \
|
-D "release ${PKG_REV}" \
|
||||||
SPECS/nvidia-container-toolkit.spec && \
|
SPECS/nvidia-container-toolkit.spec && \
|
||||||
mv RPMS/$arch/*.rpm /dist
|
mv RPMS/$arch/*.rpm /dist
|
||||||
|
|||||||
@@ -73,12 +73,16 @@ WORKDIR $DIST_DIR/..
|
|||||||
COPY packaging/rpm .
|
COPY packaging/rpm .
|
||||||
COPY deployments/systemd/ ${DIST_DIR}/
|
COPY deployments/systemd/ ${DIST_DIR}/
|
||||||
|
|
||||||
|
ARG LIBNVIDIA_CONTAINER_TOOLS_VERSION
|
||||||
|
ENV LIBNVIDIA_CONTAINER_TOOLS_VERSION ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}
|
||||||
|
|
||||||
CMD arch=$(uname -m) && \
|
CMD arch=$(uname -m) && \
|
||||||
rpmbuild --clean --target=$arch -bb \
|
rpmbuild --clean --target=$arch -bb \
|
||||||
-D "_topdir $PWD" \
|
-D "_topdir $PWD" \
|
||||||
-D "release_date $(date +'%a %b %d %Y')" \
|
-D "release_date $(date +'%a %b %d %Y')" \
|
||||||
-D "git_commit ${GIT_COMMIT}" \
|
-D "git_commit ${GIT_COMMIT}" \
|
||||||
-D "version ${PKG_VERS}" \
|
-D "version ${PKG_VERS}" \
|
||||||
|
-D "libnvidia_container_tools_version ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}" \
|
||||||
-D "release ${PKG_REV}" \
|
-D "release ${PKG_REV}" \
|
||||||
SPECS/nvidia-container-toolkit.spec && \
|
SPECS/nvidia-container-toolkit.spec && \
|
||||||
mv RPMS/$arch/*.rpm /dist
|
mv RPMS/$arch/*.rpm /dist
|
||||||
|
|||||||
@@ -55,14 +55,17 @@ WORKDIR $DIST_DIR
|
|||||||
COPY packaging/debian ./debian
|
COPY packaging/debian ./debian
|
||||||
COPY deployments/systemd/ .
|
COPY deployments/systemd/ .
|
||||||
|
|
||||||
|
ARG LIBNVIDIA_CONTAINER_TOOLS_VERSION
|
||||||
|
ENV LIBNVIDIA_CONTAINER_TOOLS_VERSION ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}
|
||||||
|
|
||||||
RUN dch --create --package="${PKG_NAME}" \
|
RUN dch --create --package="${PKG_NAME}" \
|
||||||
--newversion "${REVISION}" \
|
--newversion "${REVISION}" \
|
||||||
"See https://github.com/NVIDIA/nvidia-container-toolkit/blob/${GIT_COMMIT}/CHANGELOG.md for the changelog" && \
|
"See https://gitlab.com/nvidia/container-toolkit/container-toolkit/-/blob/${GIT_COMMIT}/CHANGELOG.md for the changelog" && \
|
||||||
dch --append "Bump libnvidia-container dependency to ${REVISION}" && \
|
dch --append "Bump libnvidia-container dependency to ${LIBNVIDIA_CONTAINER_TOOLS_VERSION}" && \
|
||||||
dch -r "" && \
|
dch -r "" && \
|
||||||
if [ "$REVISION" != "$(dpkg-parsechangelog --show-field=Version)" ]; then exit 1; fi
|
if [ "$REVISION" != "$(dpkg-parsechangelog --show-field=Version)" ]; then exit 1; fi
|
||||||
|
|
||||||
CMD export DISTRIB="$(lsb_release -cs)" && \
|
CMD export DISTRIB="$(lsb_release -cs)" && \
|
||||||
debuild -eDISTRIB -eSECTION -eVERSION="${REVISION}" \
|
debuild -eDISTRIB -eSECTION -eLIBNVIDIA_CONTAINER_TOOLS_VERSION -eVERSION="${REVISION}" \
|
||||||
--dpkg-buildpackage-hook='sh debian/prepare' -i -us -uc -b && \
|
--dpkg-buildpackage-hook='sh debian/prepare' -i -us -uc -b && \
|
||||||
mv /tmp/*.deb /dist
|
mv /tmp/*.deb /dist
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ docker-all: $(AMD64_TARGETS) $(X86_64_TARGETS) \
|
|||||||
--%: docker-build-%
|
--%: docker-build-%
|
||||||
@
|
@
|
||||||
|
|
||||||
|
LIBNVIDIA_CONTAINER_VERSION ?= $(LIB_VERSION)
|
||||||
|
LIBNVIDIA_CONTAINER_TAG ?= $(LIB_TAG)
|
||||||
|
|
||||||
|
LIBNVIDIA_CONTAINER_TOOLS_VERSION := $(LIBNVIDIA_CONTAINER_VERSION)$(if $(LIBNVIDIA_CONTAINER_TAG),~$(LIBNVIDIA_CONTAINER_TAG))-1
|
||||||
|
|
||||||
# private ubuntu target
|
# private ubuntu target
|
||||||
--ubuntu%: OS := ubuntu
|
--ubuntu%: OS := ubuntu
|
||||||
|
|
||||||
@@ -124,6 +129,7 @@ docker-build-%:
|
|||||||
--build-arg PKG_NAME="$(LIB_NAME)" \
|
--build-arg PKG_NAME="$(LIB_NAME)" \
|
||||||
--build-arg PKG_VERS="$(PACKAGE_VERSION)" \
|
--build-arg PKG_VERS="$(PACKAGE_VERSION)" \
|
||||||
--build-arg PKG_REV="$(PACKAGE_REVISION)" \
|
--build-arg PKG_REV="$(PACKAGE_REVISION)" \
|
||||||
|
--build-arg LIBNVIDIA_CONTAINER_TOOLS_VERSION="$(LIBNVIDIA_CONTAINER_TOOLS_VERSION)" \
|
||||||
--build-arg GIT_COMMIT="$(GIT_COMMIT)" \
|
--build-arg GIT_COMMIT="$(GIT_COMMIT)" \
|
||||||
--tag $(BUILDIMAGE) \
|
--tag $(BUILDIMAGE) \
|
||||||
--file $(DOCKERFILE) .
|
--file $(DOCKERFILE) .
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -27,6 +27,7 @@ require (
|
|||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
github.com/opencontainers/cgroups v0.0.1 // indirect
|
||||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -37,6 +37,8 @@ github.com/moby/sys/reexec v0.1.0/go.mod h1:EqjBg8F3X7iZe5pU6nRZnYCMUTXoxsjiIfHu
|
|||||||
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
|
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
|
||||||
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
|
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
|
||||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||||
|
github.com/opencontainers/cgroups v0.0.1 h1:MXjMkkFpKv6kpuirUa4USFBas573sSAY082B4CiHEVA=
|
||||||
|
github.com/opencontainers/cgroups v0.0.1/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
|
||||||
github.com/opencontainers/runc v1.3.0 h1:cvP7xbEvD0QQAs0nZKLzkVog2OPZhI/V2w3WmTmUSXI=
|
github.com/opencontainers/runc v1.3.0 h1:cvP7xbEvD0QQAs0nZKLzkVog2OPZhI/V2w3WmTmUSXI=
|
||||||
github.com/opencontainers/runc v1.3.0/go.mod h1:9wbWt42gV+KRxKRVVugNP6D5+PQciRbenB4fLVsqGPs=
|
github.com/opencontainers/runc v1.3.0/go.mod h1:9wbWt42gV+KRxKRVVugNP6D5+PQciRbenB4fLVsqGPs=
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
|
|||||||
@@ -53,6 +53,6 @@ docker run --rm \
|
|||||||
-v $(pwd):$(pwd) \
|
-v $(pwd):$(pwd) \
|
||||||
-w $(pwd) \
|
-w $(pwd) \
|
||||||
-u $(id -u):$(id -g) \
|
-u $(id -u):$(id -g) \
|
||||||
--entrypoint="sh" \
|
--entrypoint="bash" \
|
||||||
${IMAGE} \
|
${IMAGE} \
|
||||||
-c "cp -p -R /artifacts/* ${DIST_DIR}"
|
-c "cp --preserve=timestamps -R /artifacts/* ${DIST_DIR}"
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FilePathOverrideEnvVar = "NVIDIA_CTK_CONFIG_FILE_PATH"
|
configOverride = "XDG_CONFIG_HOME"
|
||||||
RelativeFilePath = "nvidia-container-runtime/config.toml"
|
configFilePath = "nvidia-container-runtime/config.toml"
|
||||||
|
|
||||||
configRootOverride = "XDG_CONFIG_HOME"
|
|
||||||
|
|
||||||
nvidiaCTKExecutable = "nvidia-ctk"
|
nvidiaCTKExecutable = "nvidia-ctk"
|
||||||
nvidiaCTKDefaultFilePath = "/usr/bin/nvidia-ctk"
|
nvidiaCTKDefaultFilePath = "/usr/bin/nvidia-ctk"
|
||||||
@@ -76,15 +74,11 @@ type Config struct {
|
|||||||
|
|
||||||
// GetConfigFilePath returns the path to the config file for the configured system
|
// GetConfigFilePath returns the path to the config file for the configured system
|
||||||
func GetConfigFilePath() string {
|
func GetConfigFilePath() string {
|
||||||
if configFilePathOverride := os.Getenv(FilePathOverrideEnvVar); configFilePathOverride != "" {
|
if XDGConfigDir := os.Getenv(configOverride); len(XDGConfigDir) != 0 {
|
||||||
return configFilePathOverride
|
return filepath.Join(XDGConfigDir, configFilePath)
|
||||||
}
|
|
||||||
configRoot := "/etc"
|
|
||||||
if XDGConfigDir := os.Getenv(configRootOverride); len(XDGConfigDir) != 0 {
|
|
||||||
configRoot = XDGConfigDir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(configRoot, RelativeFilePath)
|
return filepath.Join("/etc", configFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig sets up the config struct. Values are read from a toml file
|
// GetConfig sets up the config struct. Values are read from a toml file
|
||||||
|
|||||||
@@ -27,26 +27,9 @@ import (
|
|||||||
|
|
||||||
func TestGetConfigWithCustomConfig(t *testing.T) {
|
func TestGetConfigWithCustomConfig(t *testing.T) {
|
||||||
testDir := t.TempDir()
|
testDir := t.TempDir()
|
||||||
t.Setenv(configRootOverride, testDir)
|
t.Setenv(configOverride, testDir)
|
||||||
|
|
||||||
filename := filepath.Join(testDir, RelativeFilePath)
|
filename := filepath.Join(testDir, configFilePath)
|
||||||
|
|
||||||
// By default debug is disabled
|
|
||||||
contents := []byte("[nvidia-container-runtime]\ndebug = \"/nvidia-container-toolkit.log\"")
|
|
||||||
|
|
||||||
require.NoError(t, os.MkdirAll(filepath.Dir(filename), 0766))
|
|
||||||
require.NoError(t, os.WriteFile(filename, contents, 0600))
|
|
||||||
|
|
||||||
cfg, err := GetConfig()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "/nvidia-container-toolkit.log", cfg.NVIDIAContainerRuntimeConfig.DebugFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetConfigWithConfigFilePathOverride(t *testing.T) {
|
|
||||||
testDir := t.TempDir()
|
|
||||||
filename := filepath.Join(testDir, RelativeFilePath)
|
|
||||||
|
|
||||||
t.Setenv(FilePathOverrideEnvVar, filename)
|
|
||||||
|
|
||||||
// By default debug is disabled
|
// By default debug is disabled
|
||||||
contents := []byte("[nvidia-container-runtime]\ndebug = \"/nvidia-container-toolkit.log\"")
|
contents := []byte("[nvidia-container-runtime]\ndebug = \"/nvidia-container-toolkit.log\"")
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ type cache struct {
|
|||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
devices []Device
|
devices []Device
|
||||||
envVars []EnvVar
|
|
||||||
hooks []Hook
|
hooks []Hook
|
||||||
mounts []Mount
|
mounts []Mount
|
||||||
}
|
}
|
||||||
@@ -52,20 +51,6 @@ func (c *cache) Devices() ([]Device, error) {
|
|||||||
return c.devices, nil
|
return c.devices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cache) EnvVars() ([]EnvVar, error) {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
if c.envVars == nil {
|
|
||||||
envVars, err := c.d.EnvVars()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.envVars = envVars
|
|
||||||
}
|
|
||||||
return c.envVars, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cache) Hooks() ([]Hook, error) {
|
func (c *cache) Hooks() ([]Hook, error) {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|||||||
@@ -22,12 +22,6 @@ type Device struct {
|
|||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVar represents a discovered environment variable.
|
|
||||||
type EnvVar struct {
|
|
||||||
Name string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount represents a discovered mount.
|
// Mount represents a discovered mount.
|
||||||
type Mount struct {
|
type Mount struct {
|
||||||
HostPath string
|
HostPath string
|
||||||
@@ -48,7 +42,6 @@ type Hook struct {
|
|||||||
//go:generate moq -rm -fmt=goimports -stub -out discover_mock.go . Discover
|
//go:generate moq -rm -fmt=goimports -stub -out discover_mock.go . Discover
|
||||||
type Discover interface {
|
type Discover interface {
|
||||||
Devices() ([]Device, error)
|
Devices() ([]Device, error)
|
||||||
EnvVars() ([]EnvVar, error)
|
|
||||||
Mounts() ([]Mount, error)
|
Mounts() ([]Mount, error)
|
||||||
Hooks() ([]Hook, error)
|
Hooks() ([]Hook, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ var _ Discover = &DiscoverMock{}
|
|||||||
// DevicesFunc: func() ([]Device, error) {
|
// DevicesFunc: func() ([]Device, error) {
|
||||||
// panic("mock out the Devices method")
|
// panic("mock out the Devices method")
|
||||||
// },
|
// },
|
||||||
// EnvVarsFunc: func() ([]EnvVar, error) {
|
|
||||||
// panic("mock out the EnvVars method")
|
|
||||||
// },
|
|
||||||
// HooksFunc: func() ([]Hook, error) {
|
// HooksFunc: func() ([]Hook, error) {
|
||||||
// panic("mock out the Hooks method")
|
// panic("mock out the Hooks method")
|
||||||
// },
|
// },
|
||||||
@@ -39,9 +36,6 @@ type DiscoverMock struct {
|
|||||||
// DevicesFunc mocks the Devices method.
|
// DevicesFunc mocks the Devices method.
|
||||||
DevicesFunc func() ([]Device, error)
|
DevicesFunc func() ([]Device, error)
|
||||||
|
|
||||||
// EnvVarsFunc mocks the EnvVars method.
|
|
||||||
EnvVarsFunc func() ([]EnvVar, error)
|
|
||||||
|
|
||||||
// HooksFunc mocks the Hooks method.
|
// HooksFunc mocks the Hooks method.
|
||||||
HooksFunc func() ([]Hook, error)
|
HooksFunc func() ([]Hook, error)
|
||||||
|
|
||||||
@@ -53,9 +47,6 @@ type DiscoverMock struct {
|
|||||||
// Devices holds details about calls to the Devices method.
|
// Devices holds details about calls to the Devices method.
|
||||||
Devices []struct {
|
Devices []struct {
|
||||||
}
|
}
|
||||||
// EnvVars holds details about calls to the EnvVars method.
|
|
||||||
EnvVars []struct {
|
|
||||||
}
|
|
||||||
// Hooks holds details about calls to the Hooks method.
|
// Hooks holds details about calls to the Hooks method.
|
||||||
Hooks []struct {
|
Hooks []struct {
|
||||||
}
|
}
|
||||||
@@ -64,7 +55,6 @@ type DiscoverMock struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lockDevices sync.RWMutex
|
lockDevices sync.RWMutex
|
||||||
lockEnvVars sync.RWMutex
|
|
||||||
lockHooks sync.RWMutex
|
lockHooks sync.RWMutex
|
||||||
lockMounts sync.RWMutex
|
lockMounts sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -100,37 +90,6 @@ func (mock *DiscoverMock) DevicesCalls() []struct {
|
|||||||
return calls
|
return calls
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVars calls EnvVarsFunc.
|
|
||||||
func (mock *DiscoverMock) EnvVars() ([]EnvVar, error) {
|
|
||||||
callInfo := struct {
|
|
||||||
}{}
|
|
||||||
mock.lockEnvVars.Lock()
|
|
||||||
mock.calls.EnvVars = append(mock.calls.EnvVars, callInfo)
|
|
||||||
mock.lockEnvVars.Unlock()
|
|
||||||
if mock.EnvVarsFunc == nil {
|
|
||||||
var (
|
|
||||||
envVarsOut []EnvVar
|
|
||||||
errOut error
|
|
||||||
)
|
|
||||||
return envVarsOut, errOut
|
|
||||||
}
|
|
||||||
return mock.EnvVarsFunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnvVarsCalls gets all the calls that were made to EnvVars.
|
|
||||||
// Check the length with:
|
|
||||||
//
|
|
||||||
// len(mockedDiscover.EnvVarsCalls())
|
|
||||||
func (mock *DiscoverMock) EnvVarsCalls() []struct {
|
|
||||||
} {
|
|
||||||
var calls []struct {
|
|
||||||
}
|
|
||||||
mock.lockEnvVars.RLock()
|
|
||||||
calls = mock.calls.EnvVars
|
|
||||||
mock.lockEnvVars.RUnlock()
|
|
||||||
return calls
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks calls HooksFunc.
|
// Hooks calls HooksFunc.
|
||||||
func (mock *DiscoverMock) Hooks() ([]Hook, error) {
|
func (mock *DiscoverMock) Hooks() ([]Hook, error) {
|
||||||
callInfo := struct {
|
callInfo := struct {
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
var _ Discover = (*EnvVar)(nil)
|
|
||||||
|
|
||||||
// Devices returns an empty list of devices for a EnvVar discoverer.
|
|
||||||
func (e EnvVar) Devices() ([]Device, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnvVars returns an empty list of envs for a EnvVar discoverer.
|
|
||||||
func (e EnvVar) EnvVars() ([]EnvVar, error) {
|
|
||||||
return []EnvVar{e}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mounts returns an empty list of mounts for a EnvVar discoverer.
|
|
||||||
func (e EnvVar) Mounts() ([]Mount, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks allows the Hook type to also implement the Discoverer interface.
|
|
||||||
// It returns a single hook
|
|
||||||
func (e EnvVar) Hooks() ([]Hook, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
@@ -45,19 +45,6 @@ func (f firstOf) Devices() ([]Device, error) {
|
|||||||
return nil, errs
|
return nil, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f firstOf) EnvVars() ([]EnvVar, error) {
|
|
||||||
var errs error
|
|
||||||
for _, d := range f {
|
|
||||||
envs, err := d.EnvVars()
|
|
||||||
if err != nil {
|
|
||||||
errs = errors.Join(errs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return envs, nil
|
|
||||||
}
|
|
||||||
return nil, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f firstOf) Hooks() ([]Hook, error) {
|
func (f firstOf) Hooks() ([]Hook, error) {
|
||||||
var errs error
|
var errs error
|
||||||
for _, d := range f {
|
for _, d := range f {
|
||||||
|
|||||||
@@ -46,9 +46,6 @@ const (
|
|||||||
// An UpdateLDCacheHook is the hook used to update the ldcache in the
|
// An UpdateLDCacheHook is the hook used to update the ldcache in the
|
||||||
// container. This allows injected libraries to be discoverable.
|
// container. This allows injected libraries to be discoverable.
|
||||||
UpdateLDCacheHook = HookName("update-ldcache")
|
UpdateLDCacheHook = HookName("update-ldcache")
|
||||||
// A CreateSonameSymlinksHook is the hook used to ensure that soname symlinks
|
|
||||||
// for injected libraries exist in the container.
|
|
||||||
CreateSonameSymlinksHook = HookName("create-soname-symlinks")
|
|
||||||
|
|
||||||
defaultNvidiaCDIHookPath = "/usr/bin/nvidia-cdi-hook"
|
defaultNvidiaCDIHookPath = "/usr/bin/nvidia-cdi-hook"
|
||||||
)
|
)
|
||||||
@@ -60,11 +57,6 @@ func (h *Hook) Devices() ([]Device, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVars returns an empty list of envs for a Hook discoverer.
|
|
||||||
func (h *Hook) EnvVars() ([]EnvVar, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mounts returns an empty list of mounts for a Hook discoverer.
|
// Mounts returns an empty list of mounts for a Hook discoverer.
|
||||||
func (h *Hook) Mounts() ([]Mount, error) {
|
func (h *Hook) Mounts() ([]Mount, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -51,24 +51,30 @@ func (d ldconfig) Hooks() ([]Hook, error) {
|
|||||||
return nil, fmt.Errorf("failed to discover mounts for ldcache update: %v", err)
|
return nil, fmt.Errorf("failed to discover mounts for ldcache update: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var args []string
|
h := createLDCacheUpdateHook(
|
||||||
|
d.hookCreator,
|
||||||
if d.ldconfigPath != "" {
|
d.ldconfigPath,
|
||||||
args = append(args, "--ldconfig-path", d.ldconfigPath)
|
getLibraryPaths(mounts),
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range uniqueFolders(getLibraryPaths(mounts)) {
|
|
||||||
args = append(args, "--folder", f)
|
|
||||||
}
|
|
||||||
|
|
||||||
h := Merge(
|
|
||||||
d.hookCreator.Create(CreateSonameSymlinksHook, args...),
|
|
||||||
d.hookCreator.Create(UpdateLDCacheHook, args...),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return h.Hooks()
|
return h.Hooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createLDCacheUpdateHook locates the NVIDIA Container Toolkit CLI and creates a hook for updating the LD Cache
|
||||||
|
func createLDCacheUpdateHook(hookCreator HookCreator, ldconfig string, libraries []string) *Hook {
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
if ldconfig != "" {
|
||||||
|
args = append(args, "--ldconfig-path", ldconfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range uniqueFolders(libraries) {
|
||||||
|
args = append(args, "--folder", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hookCreator.Create(UpdateLDCacheHook, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// getLibraryPaths extracts the library dirs from the specified mounts
|
// getLibraryPaths extracts the library dirs from the specified mounts
|
||||||
func getLibraryPaths(mounts []Mount) []string {
|
func getLibraryPaths(mounts []Mount) []string {
|
||||||
var paths []string
|
var paths []string
|
||||||
|
|||||||
@@ -39,24 +39,11 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
|||||||
mounts []Mount
|
mounts []Mount
|
||||||
mountError error
|
mountError error
|
||||||
expectedError error
|
expectedError error
|
||||||
expectedHooks []Hook
|
expectedArgs []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
description: "empty mounts",
|
description: "empty mounts",
|
||||||
expectedHooks: []Hook{
|
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache"},
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks"},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "update-ldcache"},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "mount error",
|
description: "mount error",
|
||||||
@@ -79,20 +66,7 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
|||||||
Path: "/usr/local/lib/libbar.so",
|
Path: "/usr/local/lib/libbar.so",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedHooks: []Hook{
|
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib", "--folder", "/usr/local/libother"},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "host paths are ignored",
|
description: "host paths are ignored",
|
||||||
@@ -102,38 +76,12 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
|||||||
Path: "/usr/local/lib/libfoo.so",
|
Path: "/usr/local/lib/libfoo.so",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedHooks: []Hook{
|
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib"},
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks", "--folder", "/usr/local/lib"},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "update-ldcache", "--folder", "/usr/local/lib"},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "explicit ldconfig path is passed",
|
description: "explicit ldconfig path is passed",
|
||||||
ldconfigPath: testLdconfigPath,
|
ldconfigPath: testLdconfigPath,
|
||||||
expectedHooks: []Hook{
|
expectedArgs: []string{"nvidia-cdi-hook", "update-ldcache", "--ldconfig-path", testLdconfigPath},
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "create-soname-symlinks", "--ldconfig-path", testLdconfigPath},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Lifecycle: "createContainer",
|
|
||||||
Path: testNvidiaCDIHookPath,
|
|
||||||
Args: []string{"nvidia-cdi-hook", "update-ldcache", "--ldconfig-path", testLdconfigPath},
|
|
||||||
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +92,13 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
|||||||
return tc.mounts, tc.mountError
|
return tc.mounts, tc.mountError
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
expectedHook := Hook{
|
||||||
|
Path: testNvidiaCDIHookPath,
|
||||||
|
Args: tc.expectedArgs,
|
||||||
|
Lifecycle: "createContainer",
|
||||||
|
Env: []string{"NVIDIA_CTK_DEBUG=false"},
|
||||||
|
}
|
||||||
|
|
||||||
d, err := NewLDCacheUpdateHook(logger, mountMock, hookCreator, tc.ldconfigPath)
|
d, err := NewLDCacheUpdateHook(logger, mountMock, hookCreator, tc.ldconfigPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -157,7 +112,9 @@ func TestLDCacheUpdateHook(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, tc.expectedHooks, hooks)
|
require.Len(t, hooks, 1)
|
||||||
|
|
||||||
|
require.EqualValues(t, hooks[0], expectedHook)
|
||||||
|
|
||||||
devices, err := d.Devices()
|
devices, err := d.Devices()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -53,21 +53,6 @@ func (d list) Devices() ([]Device, error) {
|
|||||||
return allDevices, nil
|
return allDevices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVars returns all environment variables from the included discoverers.
|
|
||||||
func (d list) EnvVars() ([]EnvVar, error) {
|
|
||||||
var allEnvs []EnvVar
|
|
||||||
|
|
||||||
for i, di := range d {
|
|
||||||
envs, err := di.EnvVars()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error discovering envs for discoverer %v: %w", i, err)
|
|
||||||
}
|
|
||||||
allEnvs = append(allEnvs, envs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return allEnvs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mounts returns all mounts from the included discoverers
|
// Mounts returns all mounts from the included discoverers
|
||||||
func (d list) Mounts() ([]Mount, error) {
|
func (d list) Mounts() ([]Mount, error) {
|
||||||
var allMounts []Mount
|
var allMounts []Mount
|
||||||
|
|||||||
@@ -27,11 +27,6 @@ func (e None) Devices() ([]Device, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVars returns an empty list of devices
|
|
||||||
func (e None) EnvVars() ([]EnvVar, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mounts returns an empty list of mounts
|
// Mounts returns an empty list of mounts
|
||||||
func (e None) Mounts() ([]Mount, error) {
|
func (e None) Mounts() ([]Mount, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import (
|
|||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
"tags.cncf.io/container-device-interface/specs-go"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/devices"
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,19 +45,37 @@ func (d device) toEdits() (*cdi.ContainerEdits, error) {
|
|||||||
// toSpec converts a discovered Device to a CDI Spec Device. Note
|
// toSpec converts a discovered Device to a CDI Spec Device. Note
|
||||||
// that missing info is filled in when edits are applied by querying the Device node.
|
// that missing info is filled in when edits are applied by querying the Device node.
|
||||||
func (d device) toSpec() (*specs.DeviceNode, error) {
|
func (d device) toSpec() (*specs.DeviceNode, error) {
|
||||||
|
s := d.fromPathOrDefault()
|
||||||
// The HostPath field was added in the v0.5.0 CDI specification.
|
// The HostPath field was added in the v0.5.0 CDI specification.
|
||||||
// The cdi package uses strict unmarshalling when loading specs from file causing failures for
|
// The cdi package uses strict unmarshalling when loading specs from file causing failures for
|
||||||
// unexpected fields.
|
// unexpected fields.
|
||||||
// Since the behaviour for HostPath == "" and HostPath == Path are equivalent, we clear HostPath
|
// Since the behaviour for HostPath == "" and HostPath == Path are equivalent, we clear HostPath
|
||||||
// if it is equal to Path to ensure compatibility with the widest range of specs.
|
// if it is equal to Path to ensure compatibility with the widest range of specs.
|
||||||
hostPath := d.HostPath
|
if s.HostPath == d.Path {
|
||||||
if hostPath == d.Path {
|
s.HostPath = ""
|
||||||
hostPath = ""
|
|
||||||
}
|
|
||||||
s := specs.DeviceNode{
|
|
||||||
HostPath: hostPath,
|
|
||||||
Path: d.Path,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &s, nil
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromPathOrDefault attempts to return the returns the information about the
|
||||||
|
// CDI device from the specified host path.
|
||||||
|
// If this fails a minimal device is returned so that this information can be
|
||||||
|
// queried by the container runtime such as containerd.
|
||||||
|
func (d device) fromPathOrDefault() *specs.DeviceNode {
|
||||||
|
dn, err := devices.DeviceFromPath(d.HostPath, "rwm")
|
||||||
|
if err != nil {
|
||||||
|
return &specs.DeviceNode{
|
||||||
|
HostPath: d.HostPath,
|
||||||
|
Path: d.Path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &specs.DeviceNode{
|
||||||
|
HostPath: d.HostPath,
|
||||||
|
Path: d.Path,
|
||||||
|
Major: dn.Major,
|
||||||
|
Minor: dn.Minor,
|
||||||
|
FileMode: &dn.FileMode,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ func FromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
|
|||||||
return nil, fmt.Errorf("failed to discover devices: %v", err)
|
return nil, fmt.Errorf("failed to discover devices: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
envs, err := d.EnvVars()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to discover environment variables: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mounts, err := d.Mounts()
|
mounts, err := d.Mounts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to discover mounts: %v", err)
|
return nil, fmt.Errorf("failed to discover mounts: %v", err)
|
||||||
@@ -79,10 +74,6 @@ func FromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
|
|||||||
c.Append(edits)
|
c.Append(edits)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range envs {
|
|
||||||
c.Append(envvar(e).toEdits())
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range mounts {
|
for _, m := range mounts {
|
||||||
c.Append(mount(m).toEdits())
|
c.Append(mount(m).toEdits())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# 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 edits
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
|
||||||
"tags.cncf.io/container-device-interface/specs-go"
|
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
|
||||||
)
|
|
||||||
|
|
||||||
type envvar discover.EnvVar
|
|
||||||
|
|
||||||
// toEdits converts a discovered envvar to CDI Container Edits.
|
|
||||||
func (d envvar) toEdits() *cdi.ContainerEdits {
|
|
||||||
e := cdi.ContainerEdits{
|
|
||||||
ContainerEdits: &specs.ContainerEdits{
|
|
||||||
Env: []string{fmt.Sprintf("%s=%s", d.Name, d.Value)},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &e
|
|
||||||
}
|
|
||||||
@@ -23,114 +23,34 @@ import (
|
|||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A RuntimeMode is used to select a specific mode of operation for the NVIDIA Container Runtime.
|
|
||||||
type RuntimeMode string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// In LegacyRuntimeMode the nvidia-container-runtime injects the
|
|
||||||
// nvidia-container-runtime-hook as a prestart hook into the incoming
|
|
||||||
// container config. This hook invokes the nvidia-container-cli to perform
|
|
||||||
// the required modifications to the container.
|
|
||||||
LegacyRuntimeMode = RuntimeMode("legacy")
|
|
||||||
// In CSVRuntimeMode the nvidia-container-runtime processes a set of CSV
|
|
||||||
// files to determine which container modification are required. The
|
|
||||||
// contents of these CSV files are used to generate an in-memory CDI
|
|
||||||
// specification which is used to modify the container config.
|
|
||||||
CSVRuntimeMode = RuntimeMode("csv")
|
|
||||||
// In CDIRuntimeMode the nvidia-container-runtime applies the modifications
|
|
||||||
// to the container config required for the requested CDI devices in the
|
|
||||||
// same way that other CDI clients would.
|
|
||||||
CDIRuntimeMode = RuntimeMode("cdi")
|
|
||||||
// In JitCDIRuntimeMode the nvidia-container-runtime generates in-memory CDI
|
|
||||||
// specifications for requested NVIDIA devices.
|
|
||||||
JitCDIRuntimeMode = RuntimeMode("jit-cdi")
|
|
||||||
)
|
|
||||||
|
|
||||||
type RuntimeModeResolver interface {
|
|
||||||
ResolveRuntimeMode(string) RuntimeMode
|
|
||||||
}
|
|
||||||
|
|
||||||
type modeResolver struct {
|
|
||||||
logger logger.Interface
|
|
||||||
// TODO: This only needs to consider the requested devices.
|
|
||||||
image *image.CUDA
|
|
||||||
propertyExtractor info.PropertyExtractor
|
|
||||||
defaultMode RuntimeMode
|
|
||||||
}
|
|
||||||
|
|
||||||
type Option func(*modeResolver)
|
|
||||||
|
|
||||||
func WithDefaultMode(defaultMode RuntimeMode) Option {
|
|
||||||
return func(mr *modeResolver) {
|
|
||||||
mr.defaultMode = defaultMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithLogger(logger logger.Interface) Option {
|
|
||||||
return func(mr *modeResolver) {
|
|
||||||
mr.logger = logger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithImage(image *image.CUDA) Option {
|
|
||||||
return func(mr *modeResolver) {
|
|
||||||
mr.image = image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithPropertyExtractor(propertyExtractor info.PropertyExtractor) Option {
|
|
||||||
return func(mr *modeResolver) {
|
|
||||||
mr.propertyExtractor = propertyExtractor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRuntimeModeResolver(opts ...Option) RuntimeModeResolver {
|
|
||||||
r := &modeResolver{
|
|
||||||
defaultMode: JitCDIRuntimeMode,
|
|
||||||
}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(r)
|
|
||||||
}
|
|
||||||
if r.logger == nil {
|
|
||||||
r.logger = &logger.NullLogger{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveAutoMode determines the correct mode for the platform if set to "auto"
|
// ResolveAutoMode determines the correct mode for the platform if set to "auto"
|
||||||
func ResolveAutoMode(logger logger.Interface, mode string, image image.CUDA) (rmode RuntimeMode) {
|
func ResolveAutoMode(logger logger.Interface, mode string, image image.CUDA) (rmode string) {
|
||||||
r := modeResolver{
|
return resolveMode(logger, mode, image, nil)
|
||||||
logger: logger,
|
|
||||||
image: &image,
|
|
||||||
propertyExtractor: nil,
|
|
||||||
}
|
|
||||||
return r.ResolveRuntimeMode(mode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *modeResolver) ResolveRuntimeMode(mode string) (rmode RuntimeMode) {
|
func resolveMode(logger logger.Interface, mode string, image image.CUDA, propertyExtractor info.PropertyExtractor) (rmode string) {
|
||||||
if mode != "auto" {
|
if mode != "auto" {
|
||||||
m.logger.Infof("Using requested mode '%s'", mode)
|
logger.Infof("Using requested mode '%s'", mode)
|
||||||
return RuntimeMode(mode)
|
return mode
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
m.logger.Infof("Auto-detected mode as '%v'", rmode)
|
logger.Infof("Auto-detected mode as '%v'", rmode)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if m.image.OnlyFullyQualifiedCDIDevices() {
|
if image.OnlyFullyQualifiedCDIDevices() {
|
||||||
return CDIRuntimeMode
|
return "cdi"
|
||||||
}
|
}
|
||||||
|
|
||||||
nvinfo := info.New(
|
nvinfo := info.New(
|
||||||
info.WithLogger(m.logger),
|
info.WithLogger(logger),
|
||||||
info.WithPropertyExtractor(m.propertyExtractor),
|
info.WithPropertyExtractor(propertyExtractor),
|
||||||
)
|
)
|
||||||
|
|
||||||
switch nvinfo.ResolvePlatform() {
|
switch nvinfo.ResolvePlatform() {
|
||||||
case info.PlatformNVML, info.PlatformWSL:
|
case info.PlatformNVML, info.PlatformWSL:
|
||||||
return m.defaultMode
|
return "legacy"
|
||||||
case info.PlatformTegra:
|
case info.PlatformTegra:
|
||||||
return CSVRuntimeMode
|
return "csv"
|
||||||
}
|
}
|
||||||
return m.defaultMode
|
return "legacy"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,16 +43,11 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
mode: "not-auto",
|
mode: "not-auto",
|
||||||
expectedMode: "not-auto",
|
expectedMode: "not-auto",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "legacy resolves to legacy",
|
|
||||||
mode: "legacy",
|
|
||||||
expectedMode: "legacy",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "no info defaults to legacy",
|
description: "no info defaults to legacy",
|
||||||
mode: "auto",
|
mode: "auto",
|
||||||
info: map[string]bool{},
|
info: map[string]bool{},
|
||||||
expectedMode: "jit-cdi",
|
expectedMode: "legacy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "non-nvml, non-tegra, nvgpu resolves to csv",
|
description: "non-nvml, non-tegra, nvgpu resolves to csv",
|
||||||
@@ -85,14 +80,14 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
expectedMode: "csv",
|
expectedMode: "csv",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "nvml, non-tegra, non-nvgpu resolves to jit-cdi",
|
description: "nvml, non-tegra, non-nvgpu resolves to legacy",
|
||||||
mode: "auto",
|
mode: "auto",
|
||||||
info: map[string]bool{
|
info: map[string]bool{
|
||||||
"nvml": true,
|
"nvml": true,
|
||||||
"tegra": false,
|
"tegra": false,
|
||||||
"nvgpu": false,
|
"nvgpu": false,
|
||||||
},
|
},
|
||||||
expectedMode: "jit-cdi",
|
expectedMode: "legacy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "nvml, non-tegra, nvgpu resolves to csv",
|
description: "nvml, non-tegra, nvgpu resolves to csv",
|
||||||
@@ -105,14 +100,14 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
expectedMode: "csv",
|
expectedMode: "csv",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "nvml, tegra, non-nvgpu resolves to jit-cdi",
|
description: "nvml, tegra, non-nvgpu resolves to legacy",
|
||||||
mode: "auto",
|
mode: "auto",
|
||||||
info: map[string]bool{
|
info: map[string]bool{
|
||||||
"nvml": true,
|
"nvml": true,
|
||||||
"tegra": true,
|
"tegra": true,
|
||||||
"nvgpu": false,
|
"nvgpu": false,
|
||||||
},
|
},
|
||||||
expectedMode: "jit-cdi",
|
expectedMode: "legacy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "nvml, tegra, nvgpu resolves to csv",
|
description: "nvml, tegra, nvgpu resolves to csv",
|
||||||
@@ -141,7 +136,7 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "at least one non-cdi device resolves to jit-cdi",
|
description: "at least one non-cdi device resolves to legacy",
|
||||||
mode: "auto",
|
mode: "auto",
|
||||||
envmap: map[string]string{
|
envmap: map[string]string{
|
||||||
"NVIDIA_VISIBLE_DEVICES": "nvidia.com/gpu=0,0",
|
"NVIDIA_VISIBLE_DEVICES": "nvidia.com/gpu=0,0",
|
||||||
@@ -151,7 +146,7 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
"tegra": false,
|
"tegra": false,
|
||||||
"nvgpu": false,
|
"nvgpu": false,
|
||||||
},
|
},
|
||||||
expectedMode: "jit-cdi",
|
expectedMode: "legacy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "at least one non-cdi device resolves to csv",
|
description: "at least one non-cdi device resolves to csv",
|
||||||
@@ -175,7 +170,7 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
expectedMode: "cdi",
|
expectedMode: "cdi",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cdi mount and non-CDI devices resolves to jit-cdi",
|
description: "cdi mount and non-CDI devices resolves to legacy",
|
||||||
mode: "auto",
|
mode: "auto",
|
||||||
mounts: []string{
|
mounts: []string{
|
||||||
"/var/run/nvidia-container-devices/cdi/nvidia.com/gpu/0",
|
"/var/run/nvidia-container-devices/cdi/nvidia.com/gpu/0",
|
||||||
@@ -186,7 +181,7 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
"tegra": false,
|
"tegra": false,
|
||||||
"nvgpu": false,
|
"nvgpu": false,
|
||||||
},
|
},
|
||||||
expectedMode: "jit-cdi",
|
expectedMode: "legacy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "cdi mount and non-CDI envvar resolves to cdi",
|
description: "cdi mount and non-CDI envvar resolves to cdi",
|
||||||
@@ -204,6 +199,22 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedMode: "cdi",
|
expectedMode: "cdi",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "non-cdi mount and CDI envvar resolves to legacy",
|
||||||
|
mode: "auto",
|
||||||
|
envmap: map[string]string{
|
||||||
|
"NVIDIA_VISIBLE_DEVICES": "nvidia.com/gpu=0",
|
||||||
|
},
|
||||||
|
mounts: []string{
|
||||||
|
"/var/run/nvidia-container-devices/0",
|
||||||
|
},
|
||||||
|
info: map[string]bool{
|
||||||
|
"nvml": true,
|
||||||
|
"tegra": false,
|
||||||
|
"nvgpu": false,
|
||||||
|
},
|
||||||
|
expectedMode: "legacy",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@@ -240,12 +251,7 @@ func TestResolveAutoMode(t *testing.T) {
|
|||||||
image.WithAcceptDeviceListAsVolumeMounts(true),
|
image.WithAcceptDeviceListAsVolumeMounts(true),
|
||||||
image.WithAcceptEnvvarUnprivileged(true),
|
image.WithAcceptEnvvarUnprivileged(true),
|
||||||
)
|
)
|
||||||
mr := NewRuntimeModeResolver(
|
mode := resolveMode(logger, tc.mode, image, properties)
|
||||||
WithLogger(logger),
|
|
||||||
WithImage(&image),
|
|
||||||
WithPropertyExtractor(properties),
|
|
||||||
)
|
|
||||||
mode := mr.ResolveRuntimeMode(tc.mode)
|
|
||||||
require.EqualValues(t, tc.expectedMode, mode)
|
require.EqualValues(t, tc.expectedMode, mode)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,206 +0,0 @@
|
|||||||
/**
|
|
||||||
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# 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 ldconfig
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ldsoconfdFilenamePattern specifies the pattern for the filename
|
|
||||||
// in ld.so.conf.d that includes references to the specified directories.
|
|
||||||
// The 00-nvcr prefix is chosen to ensure that these libraries have a
|
|
||||||
// higher precedence than other libraries on the system, but lower than
|
|
||||||
// the 00-cuda-compat that is included in some containers.
|
|
||||||
ldsoconfdFilenamePattern = "00-nvcr-*.conf"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Ldconfig struct {
|
|
||||||
ldconfigPath string
|
|
||||||
inRoot string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRunner creates an exec.Cmd that can be used to run ldconfig.
|
|
||||||
func NewRunner(id string, ldconfigPath string, containerRoot string, additionalargs ...string) (*exec.Cmd, error) {
|
|
||||||
args := []string{
|
|
||||||
id,
|
|
||||||
strings.TrimPrefix(config.NormalizeLDConfigPath("@"+ldconfigPath), "@"),
|
|
||||||
containerRoot,
|
|
||||||
}
|
|
||||||
args = append(args, additionalargs...)
|
|
||||||
|
|
||||||
return createReexecCommand(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates an Ldconfig struct that is used to perform operations on the
|
|
||||||
// ldcache and libraries in a particular root (e.g. a container).
|
|
||||||
func New(ldconfigPath string, inRoot string) (*Ldconfig, error) {
|
|
||||||
l := &Ldconfig{
|
|
||||||
ldconfigPath: ldconfigPath,
|
|
||||||
inRoot: inRoot,
|
|
||||||
}
|
|
||||||
if ldconfigPath == "" {
|
|
||||||
return nil, fmt.Errorf("an ldconfig path must be specified")
|
|
||||||
}
|
|
||||||
if inRoot == "" || inRoot == "/" {
|
|
||||||
return nil, fmt.Errorf("ldconfig must be run in the non-system root")
|
|
||||||
}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSonameSymlinks uses ldconfig to create the soname symlinks in the
|
|
||||||
// specified directories.
|
|
||||||
func (l *Ldconfig) CreateSonameSymlinks(directories ...string) error {
|
|
||||||
if len(directories) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ldconfigPath, err := l.prepareRoot()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{
|
|
||||||
filepath.Base(ldconfigPath),
|
|
||||||
// Explicitly disable updating the LDCache.
|
|
||||||
"-N",
|
|
||||||
// Specify -n to only process the specified directories.
|
|
||||||
"-n",
|
|
||||||
}
|
|
||||||
args = append(args, directories...)
|
|
||||||
|
|
||||||
return SafeExec(ldconfigPath, args, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Ldconfig) UpdateLDCache(directories ...string) error {
|
|
||||||
ldconfigPath, err := l.prepareRoot()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{
|
|
||||||
filepath.Base(ldconfigPath),
|
|
||||||
// Explicitly specify using /etc/ld.so.conf since the host's ldconfig may
|
|
||||||
// be configured to use a different config file by default.
|
|
||||||
"-f", "/etc/ld.so.conf",
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.ldcacheExists() {
|
|
||||||
args = append(args, "-C", "/etc/ld.so.cache")
|
|
||||||
} else {
|
|
||||||
args = append(args, "-N")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the ld.so.conf.d directory exists, we create a config file there
|
|
||||||
// containing the required directories, otherwise we add the specified
|
|
||||||
// directories to the ldconfig command directly.
|
|
||||||
if l.ldsoconfdDirectoryExists() {
|
|
||||||
err := createLdsoconfdFile(ldsoconfdFilenamePattern, directories...)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to update ld.so.conf.d: %w", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args = append(args, directories...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return SafeExec(ldconfigPath, args, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Ldconfig) prepareRoot() (string, error) {
|
|
||||||
// To prevent leaking the parent proc filesystem, we create a new proc mount
|
|
||||||
// in the specified root.
|
|
||||||
if err := mountProc(l.inRoot); err != nil {
|
|
||||||
return "", fmt.Errorf("error mounting /proc: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We mount the host ldconfig before we pivot root since host paths are not
|
|
||||||
// visible after the pivot root operation.
|
|
||||||
ldconfigPath, err := mountLdConfig(l.ldconfigPath, l.inRoot)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("error mounting host ldconfig: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We pivot to the container root for the new process, this further limits
|
|
||||||
// access to the host.
|
|
||||||
if err := pivotRoot(l.inRoot); err != nil {
|
|
||||||
return "", fmt.Errorf("error running pivot_root: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ldconfigPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Ldconfig) ldcacheExists() bool {
|
|
||||||
if _, err := os.Stat("/etc/ld.so.cache"); err != nil && os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Ldconfig) ldsoconfdDirectoryExists() bool {
|
|
||||||
info, err := os.Stat("/etc/ld.so.conf.d")
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return info.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// createLdsoconfdFile creates a file at /etc/ld.so.conf.d/.
|
|
||||||
// The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
|
|
||||||
// contains the specified directories on each line.
|
|
||||||
func createLdsoconfdFile(pattern string, dirs ...string) error {
|
|
||||||
if len(dirs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ldsoconfdDir := "/etc/ld.so.conf.d"
|
|
||||||
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
|
|
||||||
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create config file: %w", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = configFile.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
added := make(map[string]bool)
|
|
||||||
for _, dir := range dirs {
|
|
||||||
if added[dir] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = fmt.Fprintf(configFile, "%s\n", dir)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to update config file: %w", err)
|
|
||||||
}
|
|
||||||
added[dir] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// The created file needs to be world readable for the cases where the container is run as a non-root user.
|
|
||||||
if err := configFile.Chmod(0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to chmod config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,6 @@ package modifier
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||||
|
|
||||||
@@ -28,27 +27,17 @@ import (
|
|||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/modifier/cdi"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/modifier/cdi"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
|
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
|
||||||
)
|
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
|
||||||
|
|
||||||
const (
|
|
||||||
automaticDeviceVendor = "runtime.nvidia.com"
|
|
||||||
automaticDeviceClass = "gpu"
|
|
||||||
automaticDeviceKind = automaticDeviceVendor + "/" + automaticDeviceClass
|
|
||||||
automaticDevicePrefix = automaticDeviceKind + "="
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCDIModifier creates an OCI spec modifier that determines the modifications to make based on the
|
// NewCDIModifier creates an OCI spec modifier that determines the modifications to make based on the
|
||||||
// CDI specifications available on the system. The NVIDIA_VISIBLE_DEVICES environment variable is
|
// CDI specifications available on the system. The NVIDIA_VISIBLE_DEVICES environment variable is
|
||||||
// used to select the devices to include.
|
// used to select the devices to include.
|
||||||
func NewCDIModifier(logger logger.Interface, cfg *config.Config, image image.CUDA, isJitCDI bool) (oci.SpecModifier, error) {
|
func NewCDIModifier(logger logger.Interface, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
||||||
defaultKind := cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.DefaultKind
|
|
||||||
if isJitCDI {
|
|
||||||
defaultKind = automaticDeviceKind
|
|
||||||
}
|
|
||||||
deviceRequestor := newCDIDeviceRequestor(
|
deviceRequestor := newCDIDeviceRequestor(
|
||||||
logger,
|
logger,
|
||||||
image,
|
image,
|
||||||
defaultKind,
|
cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.DefaultKind,
|
||||||
)
|
)
|
||||||
devices := deviceRequestor.DeviceRequests()
|
devices := deviceRequestor.DeviceRequests()
|
||||||
if len(devices) == 0 {
|
if len(devices) == 0 {
|
||||||
@@ -118,34 +107,17 @@ func (c *cdiDeviceRequestor) DeviceRequests() []string {
|
|||||||
func filterAutomaticDevices(devices []string) []string {
|
func filterAutomaticDevices(devices []string) []string {
|
||||||
var automatic []string
|
var automatic []string
|
||||||
for _, device := range devices {
|
for _, device := range devices {
|
||||||
if !strings.HasPrefix(device, automaticDevicePrefix) {
|
vendor, class, _ := parser.ParseDevice(device)
|
||||||
continue
|
if vendor == "runtime.nvidia.com" && class == "gpu" {
|
||||||
|
automatic = append(automatic, device)
|
||||||
}
|
}
|
||||||
automatic = append(automatic, device)
|
|
||||||
}
|
}
|
||||||
return automatic
|
return automatic
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, devices []string) (oci.SpecModifier, error) {
|
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, devices []string) (oci.SpecModifier, error) {
|
||||||
logger.Debugf("Generating in-memory CDI specs for devices %v", devices)
|
logger.Debugf("Generating in-memory CDI specs for devices %v", devices)
|
||||||
|
spec, err := generateAutomaticCDISpec(logger, cfg, devices)
|
||||||
var identifiers []string
|
|
||||||
for _, device := range devices {
|
|
||||||
identifiers = append(identifiers, strings.TrimPrefix(device, automaticDevicePrefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
cdilib, err := nvcdi.New(
|
|
||||||
nvcdi.WithLogger(logger),
|
|
||||||
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
|
|
||||||
nvcdi.WithDriverRoot(cfg.NVIDIAContainerCLIConfig.Root),
|
|
||||||
nvcdi.WithVendor(automaticDeviceVendor),
|
|
||||||
nvcdi.WithClass(automaticDeviceClass),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to construct CDI library: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
spec, err := cdilib.GetSpec(identifiers...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to generate CDI spec: %w", err)
|
return nil, fmt.Errorf("failed to generate CDI spec: %w", err)
|
||||||
}
|
}
|
||||||
@@ -160,6 +132,27 @@ func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, de
|
|||||||
return cdiDeviceRequestor, nil
|
return cdiDeviceRequestor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devices []string) (spec.Interface, error) {
|
||||||
|
cdilib, err := nvcdi.New(
|
||||||
|
nvcdi.WithLogger(logger),
|
||||||
|
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
|
||||||
|
nvcdi.WithDriverRoot(cfg.NVIDIAContainerCLIConfig.Root),
|
||||||
|
nvcdi.WithVendor("runtime.nvidia.com"),
|
||||||
|
nvcdi.WithClass("gpu"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to construct CDI library: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var identifiers []string
|
||||||
|
for _, device := range devices {
|
||||||
|
_, _, id := parser.ParseDevice(device)
|
||||||
|
identifiers = append(identifiers, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cdilib.GetSpec(identifiers...)
|
||||||
|
}
|
||||||
|
|
||||||
type deduplicatedDeviceRequestor struct {
|
type deduplicatedDeviceRequestor struct {
|
||||||
deviceRequestor
|
deviceRequestor
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,18 +70,6 @@ func TestDeviceRequests(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedDevices: []string{"nvidia.com/gpu=0", "example.com/class=device"},
|
expectedDevices: []string{"nvidia.com/gpu=0", "example.com/class=device"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "cdi devices from envvar with default kind",
|
|
||||||
input: cdiDeviceRequestor{
|
|
||||||
defaultKind: "runtime.nvidia.com/gpu",
|
|
||||||
},
|
|
||||||
spec: &specs.Spec{
|
|
||||||
Process: &specs.Process{
|
|
||||||
Env: []string{"NVIDIA_VISIBLE_DEVICES=all"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedDevices: []string{"runtime.nvidia.com/gpu=all"},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "no matching annotations",
|
description: "no matching annotations",
|
||||||
prefixes: []string{"not-prefix/"},
|
prefixes: []string{"not-prefix/"},
|
||||||
|
|||||||
@@ -41,11 +41,6 @@ func (d *byPathHookDiscoverer) Devices() ([]discover.Device, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVars returns the empty list for the by-path hook discoverer
|
|
||||||
func (d *byPathHookDiscoverer) EnvVars() ([]discover.EnvVar, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks returns the hooks for the GPU device.
|
// Hooks returns the hooks for the GPU device.
|
||||||
// The following hooks are detected:
|
// The following hooks are detected:
|
||||||
// 1. A hook to create /dev/dri/by-path symlinks
|
// 1. A hook to create /dev/dri/by-path symlinks
|
||||||
|
|||||||
@@ -106,10 +106,6 @@ func (d *nvsandboxutilsDGPU) Devices() ([]discover.Device, error) {
|
|||||||
return devices, nil
|
return devices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *nvsandboxutilsDGPU) EnvVars() ([]discover.EnvVar, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks returns a hook to create the by-path symlinks for the discovered devices.
|
// Hooks returns a hook to create the by-path symlinks for the discovered devices.
|
||||||
func (d *nvsandboxutilsDGPU) Hooks() ([]discover.Hook, error) {
|
func (d *nvsandboxutilsDGPU) Hooks() ([]discover.Hook, error) {
|
||||||
if len(d.deviceLinks) == 0 {
|
if len(d.deviceLinks) == 0 {
|
||||||
|
|||||||
@@ -101,14 +101,14 @@ func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Sp
|
|||||||
return modifiers, nil
|
return modifiers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newModeModifier(logger logger.Interface, mode info.RuntimeMode, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
func newModeModifier(logger logger.Interface, mode string, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
||||||
switch mode {
|
switch mode {
|
||||||
case info.LegacyRuntimeMode:
|
case "legacy":
|
||||||
return modifier.NewStableRuntimeModifier(logger, cfg.NVIDIAContainerRuntimeHookConfig.Path), nil
|
return modifier.NewStableRuntimeModifier(logger, cfg.NVIDIAContainerRuntimeHookConfig.Path), nil
|
||||||
case info.CSVRuntimeMode:
|
case "csv":
|
||||||
return modifier.NewCSVModifier(logger, cfg, image)
|
return modifier.NewCSVModifier(logger, cfg, image)
|
||||||
case info.CDIRuntimeMode, info.JitCDIRuntimeMode:
|
case "cdi":
|
||||||
return modifier.NewCDIModifier(logger, cfg, image, mode == info.JitCDIRuntimeMode)
|
return modifier.NewCDIModifier(logger, cfg, image)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("invalid runtime mode: %v", cfg.NVIDIAContainerRuntimeConfig.Mode)
|
return nil, fmt.Errorf("invalid runtime mode: %v", cfg.NVIDIAContainerRuntimeConfig.Mode)
|
||||||
@@ -119,7 +119,7 @@ func newModeModifier(logger logger.Interface, mode info.RuntimeMode, cfg *config
|
|||||||
// The image is also used to determine the runtime mode to apply.
|
// The image is also used to determine the runtime mode to apply.
|
||||||
// If a non-CDI mode is detected we ensure that the image does not process
|
// If a non-CDI mode is detected we ensure that the image does not process
|
||||||
// annotation devices.
|
// annotation devices.
|
||||||
func initRuntimeModeAndImage(logger logger.Interface, cfg *config.Config, ociSpec oci.Spec) (info.RuntimeMode, *image.CUDA, error) {
|
func initRuntimeModeAndImage(logger logger.Interface, cfg *config.Config, ociSpec oci.Spec) (string, *image.CUDA, error) {
|
||||||
rawSpec, err := ociSpec.Load()
|
rawSpec, err := ociSpec.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("failed to load OCI spec: %v", err)
|
return "", nil, fmt.Errorf("failed to load OCI spec: %v", err)
|
||||||
@@ -136,13 +136,9 @@ func initRuntimeModeAndImage(logger logger.Interface, cfg *config.Config, ociSpe
|
|||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
modeResolver := info.NewRuntimeModeResolver(
|
mode := info.ResolveAutoMode(logger, cfg.NVIDIAContainerRuntimeConfig.Mode, image)
|
||||||
info.WithLogger(logger),
|
|
||||||
info.WithImage(&image),
|
|
||||||
)
|
|
||||||
mode := modeResolver.ResolveRuntimeMode(cfg.NVIDIAContainerRuntimeConfig.Mode)
|
|
||||||
// We update the mode here so that we can continue passing just the config to other functions.
|
// We update the mode here so that we can continue passing just the config to other functions.
|
||||||
cfg.NVIDIAContainerRuntimeConfig.Mode = string(mode)
|
cfg.NVIDIAContainerRuntimeConfig.Mode = mode
|
||||||
|
|
||||||
if mode == "cdi" || len(cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.AnnotationPrefixes) == 0 {
|
if mode == "cdi" || len(cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.AnnotationPrefixes) == 0 {
|
||||||
return mode, &image, nil
|
return mode, &image, nil
|
||||||
@@ -158,12 +154,12 @@ func initRuntimeModeAndImage(logger logger.Interface, cfg *config.Config, ociSpe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// supportedModifierTypes returns the modifiers supported for a specific runtime mode.
|
// supportedModifierTypes returns the modifiers supported for a specific runtime mode.
|
||||||
func supportedModifierTypes(mode info.RuntimeMode) []string {
|
func supportedModifierTypes(mode string) []string {
|
||||||
switch mode {
|
switch mode {
|
||||||
case info.CDIRuntimeMode, info.JitCDIRuntimeMode:
|
case "cdi":
|
||||||
// For CDI mode we make no additional modifications.
|
// For CDI mode we make no additional modifications.
|
||||||
return []string{"nvidia-hook-remover", "mode"}
|
return []string{"nvidia-hook-remover", "mode"}
|
||||||
case info.CSVRuntimeMode:
|
case "csv":
|
||||||
// For CSV mode we support mode and feature-gated modification.
|
// For CSV mode we support mode and feature-gated modification.
|
||||||
return []string{"nvidia-hook-remover", "feature-gated", "mode"}
|
return []string{"nvidia-hook-remover", "feature-gated", "mode"}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Build-Depends: debhelper (>= 9)
|
|||||||
|
|
||||||
Package: nvidia-container-toolkit
|
Package: nvidia-container-toolkit
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: ${misc:Depends}, nvidia-container-toolkit-base (= @VERSION@), libnvidia-container-tools (= @VERSION@), libnvidia-container-tools (<< 2.0.0)
|
Depends: ${misc:Depends}, nvidia-container-toolkit-base (= @VERSION@), libnvidia-container-tools (>= @LIBNVIDIA_CONTAINER_TOOLS_VERSION@), libnvidia-container-tools (<< 2.0.0)
|
||||||
Breaks: nvidia-container-runtime (<= 3.5.0-1), nvidia-container-runtime-hook
|
Breaks: nvidia-container-runtime (<= 3.5.0-1), nvidia-container-runtime-hook
|
||||||
Replaces: nvidia-container-runtime (<= 3.5.0-1), nvidia-container-runtime-hook
|
Replaces: nvidia-container-runtime (<= 3.5.0-1), nvidia-container-runtime-hook
|
||||||
Description: NVIDIA Container toolkit
|
Description: NVIDIA Container toolkit
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
sed -i "s;@SECTION@;${SECTION:+$SECTION/};g" debian/control
|
sed -i "s;@SECTION@;${SECTION:+$SECTION/};g" debian/control
|
||||||
|
sed -i "s;@LIBNVIDIA_CONTAINER_TOOLS_VERSION@;${LIBNVIDIA_CONTAINER_TOOLS_VERSION:+$LIBNVIDIA_CONTAINER_TOOLS_VERSION};g" debian/control
|
||||||
sed -i "s;@VERSION@;${VERSION:+$VERSION};g" debian/control
|
sed -i "s;@VERSION@;${VERSION:+$VERSION};g" debian/control
|
||||||
|
|
||||||
if [ -n "$DISTRIB" ]; then
|
if [ -n "$DISTRIB" ]; then
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Source8: nvidia-cdi-refresh.path
|
|||||||
Obsoletes: nvidia-container-runtime <= 3.5.0-1, nvidia-container-runtime-hook <= 1.4.0-2
|
Obsoletes: nvidia-container-runtime <= 3.5.0-1, nvidia-container-runtime-hook <= 1.4.0-2
|
||||||
Provides: nvidia-container-runtime
|
Provides: nvidia-container-runtime
|
||||||
Provides: nvidia-container-runtime-hook
|
Provides: nvidia-container-runtime-hook
|
||||||
Requires: libnvidia-container-tools == %{version}-%{release}, libnvidia-container-tools < 2.0.0
|
Requires: libnvidia-container-tools >= %{libnvidia_container_tools_version}, libnvidia-container-tools < 2.0.0
|
||||||
Requires: nvidia-container-toolkit-base == %{version}-%{release}
|
Requires: nvidia-container-toolkit-base == %{version}-%{release}
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@@ -85,8 +85,8 @@ fi
|
|||||||
%changelog
|
%changelog
|
||||||
# As of 1.10.0-1 we generate the release information automatically
|
# As of 1.10.0-1 we generate the release information automatically
|
||||||
* %{release_date} NVIDIA CORPORATION <cudatools@nvidia.com> %{version}-%{release}
|
* %{release_date} NVIDIA CORPORATION <cudatools@nvidia.com> %{version}-%{release}
|
||||||
- See https://github.com/NVIDIA/nvidia-container-toolkit/blob/%{git_commit}/CHANGELOG.md
|
- See https://gitlab.com/nvidia/container-toolkit/container-toolkit/-/blob/%{git_commit}/CHANGELOG.md
|
||||||
- Bump libnvidia-container dependency to libnvidia-container-tools == %{version}-%{release}
|
- Bump libnvidia-container dependency to libnvidia-container-tools >= %{libnvidia_container_tools_version}
|
||||||
|
|
||||||
# The BASE package consists of the NVIDIA Container Runtime and the NVIDIA Container Toolkit CLI.
|
# The BASE package consists of the NVIDIA Container Runtime and the NVIDIA Container Toolkit CLI.
|
||||||
# This allows the package to be installed on systems where no NVIDIA Container CLI is available.
|
# This allows the package to be installed on systems where no NVIDIA Container CLI is available.
|
||||||
|
|||||||
@@ -56,9 +56,6 @@ const (
|
|||||||
EnableCudaCompatHook = discover.EnableCudaCompatHook
|
EnableCudaCompatHook = discover.EnableCudaCompatHook
|
||||||
// An UpdateLDCacheHook is used to update the ldcache in the container.
|
// An UpdateLDCacheHook is used to update the ldcache in the container.
|
||||||
UpdateLDCacheHook = discover.UpdateLDCacheHook
|
UpdateLDCacheHook = discover.UpdateLDCacheHook
|
||||||
// A CreateSonameSymlinksHook is the hook used to ensure that soname symlinks
|
|
||||||
// for injected libraries exist in the container.
|
|
||||||
CreateSonameSymlinksHook = discover.CreateSonameSymlinksHook
|
|
||||||
|
|
||||||
// Deprecated: Use CreateSymlinksHook instead.
|
// Deprecated: Use CreateSymlinksHook instead.
|
||||||
HookCreateSymlinks = CreateSymlinksHook
|
HookCreateSymlinks = CreateSymlinksHook
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ func (l *nvcdilib) newDriverVersionDiscoverer(version string) (discover.Discover
|
|||||||
|
|
||||||
// NewDriverLibraryDiscoverer creates a discoverer for the libraries associated with the specified driver version.
|
// NewDriverLibraryDiscoverer creates a discoverer for the libraries associated with the specified driver version.
|
||||||
func (l *nvcdilib) NewDriverLibraryDiscoverer(version string) (discover.Discover, error) {
|
func (l *nvcdilib) NewDriverLibraryDiscoverer(version string) (discover.Discover, error) {
|
||||||
libraryPaths, libCudaDirectoryPath, err := getVersionLibs(l.logger, l.driver, version)
|
libraryPaths, err := getVersionLibs(l.logger, l.driver, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get libraries for driver version: %v", err)
|
return nil, fmt.Errorf("failed to get libraries for driver version: %v", err)
|
||||||
}
|
}
|
||||||
@@ -116,12 +116,6 @@ func (l *nvcdilib) NewDriverLibraryDiscoverer(version string) (discover.Discover
|
|||||||
disableDeviceNodeModification := l.hookCreator.Create(DisableDeviceNodeModificationHook)
|
disableDeviceNodeModification := l.hookCreator.Create(DisableDeviceNodeModificationHook)
|
||||||
discoverers = append(discoverers, disableDeviceNodeModification)
|
discoverers = append(discoverers, disableDeviceNodeModification)
|
||||||
|
|
||||||
environmentVariable := &discover.EnvVar{
|
|
||||||
Name: "NVIDIA_CTK_LIBCUDA_DIR",
|
|
||||||
Value: libCudaDirectoryPath,
|
|
||||||
}
|
|
||||||
discoverers = append(discoverers, environmentVariable)
|
|
||||||
|
|
||||||
d := discover.Merge(discoverers...)
|
d := discover.Merge(discoverers...)
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
@@ -209,41 +203,39 @@ func NewDriverBinariesDiscoverer(logger logger.Interface, driverRoot string) dis
|
|||||||
// getVersionLibs checks the LDCache for libraries ending in the specified driver version.
|
// getVersionLibs checks the LDCache for libraries ending in the specified driver version.
|
||||||
// Although the ldcache at the specified driverRoot is queried, the paths are returned relative to this driverRoot.
|
// Although the ldcache at the specified driverRoot is queried, the paths are returned relative to this driverRoot.
|
||||||
// This allows the standard mount location logic to be used for resolving the mounts.
|
// This allows the standard mount location logic to be used for resolving the mounts.
|
||||||
func getVersionLibs(logger logger.Interface, driver *root.Driver, version string) ([]string, string, error) {
|
func getVersionLibs(logger logger.Interface, driver *root.Driver, version string) ([]string, error) {
|
||||||
logger.Infof("Using driver version %v", version)
|
logger.Infof("Using driver version %v", version)
|
||||||
|
|
||||||
libCudaPaths, err := cuda.New(
|
libCudaPaths, err := cuda.New(
|
||||||
driver.Libraries(),
|
driver.Libraries(),
|
||||||
).Locate("." + version)
|
).Locate("." + version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", fmt.Errorf("failed to locate libcuda.so.%v: %v", version, err)
|
return nil, fmt.Errorf("failed to locate libcuda.so.%v: %v", version, err)
|
||||||
}
|
}
|
||||||
libCudaDirectoryPath := filepath.Dir(libCudaPaths[0])
|
libRoot := filepath.Dir(libCudaPaths[0])
|
||||||
|
|
||||||
libraries := lookup.NewFileLocator(
|
libraries := lookup.NewFileLocator(
|
||||||
lookup.WithLogger(logger),
|
lookup.WithLogger(logger),
|
||||||
lookup.WithSearchPaths(
|
lookup.WithSearchPaths(
|
||||||
libCudaDirectoryPath,
|
libRoot,
|
||||||
filepath.Join(libCudaDirectoryPath, "vdpau"),
|
filepath.Join(libRoot, "vdpau"),
|
||||||
),
|
),
|
||||||
lookup.WithOptional(true),
|
lookup.WithOptional(true),
|
||||||
)
|
)
|
||||||
|
|
||||||
libs, err := libraries.Locate("*.so." + version)
|
libs, err := libraries.Locate("*.so." + version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", fmt.Errorf("failed to locate libraries for driver version %v: %v", version, err)
|
return nil, fmt.Errorf("failed to locate libraries for driver version %v: %v", version, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if driver.Root == "/" || driver.Root == "" {
|
if driver.Root == "/" || driver.Root == "" {
|
||||||
return libs, libCudaDirectoryPath, nil
|
return libs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
libCudaDirectoryPath = driver.RelativeToRoot(libCudaDirectoryPath)
|
|
||||||
|
|
||||||
var relative []string
|
var relative []string
|
||||||
for _, l := range libs {
|
for _, l := range libs {
|
||||||
relative = append(relative, strings.TrimPrefix(l, driver.Root))
|
relative = append(relative, strings.TrimPrefix(l, driver.Root))
|
||||||
}
|
}
|
||||||
|
|
||||||
return relative, libCudaDirectoryPath, nil
|
return relative, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ func (d *deviceFolderPermissions) Devices() ([]discover.Device, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvVars are empty for this discoverer
|
|
||||||
func (d *deviceFolderPermissions) EnvVars() ([]discover.EnvVar, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks returns a set of hooks that sets the file mode to 755 of parent folders for nested device nodes.
|
// Hooks returns a set of hooks that sets the file mode to 755 of parent folders for nested device nodes.
|
||||||
func (d *deviceFolderPermissions) Hooks() ([]discover.Hook, error) {
|
func (d *deviceFolderPermissions) Hooks() ([]discover.Hook, error) {
|
||||||
folders, err := d.getDeviceSubfolders()
|
folders, err := d.getDeviceSubfolders()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
function assert_usage() {
|
function assert_usage() {
|
||||||
echo "Incorrect arguments: $*" >&2
|
echo "Incorrect arguments: $*" >&2
|
||||||
echo "$(basename "${BASH_SOURCE[0]}") PACKAGE_IMAGE_NAME:PACKAGE_IMAGE_TAG" >&2
|
echo "$(basename "${BASH_SOURCE[0]}") PACKAGE_IMAGE_NAME:PACKAGE_IMAGE_TAG" >&2
|
||||||
echo -e "\\tPACKAGE_IMAGE: container image holding packages [e.g. ghcr.io/nvidia/container-toolkit]" >&2
|
echo -e "\\tPACKAGE_IMAGE: container image holding packages [e.g. registry.gitlab.com/nvidia/container-toolkit/container-toolkit/staging/container-toolkit]" >&2
|
||||||
echo -e "\\tPACKAGE_TAG: tag for container image holding packages. [e.g. 1a2b3c4-packaging]" >&2
|
echo -e "\\tPACKAGE_TAG: tag for container image holding packages. [e.g. 1a2b3c4-packaging]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -70,9 +70,9 @@ function copy-file() {
|
|||||||
-v "$(pwd):$(pwd)" \
|
-v "$(pwd):$(pwd)" \
|
||||||
-w "$(pwd)" \
|
-w "$(pwd)" \
|
||||||
-u "$(id -u):$(id -g)" \
|
-u "$(id -u):$(id -g)" \
|
||||||
--entrypoint="sh" \
|
--entrypoint="bash" \
|
||||||
"${image}" \
|
"${image}" \
|
||||||
-c "cp -p ${path_in_image} ${path_on_host}"
|
-c "cp ${path_in_image} ${path_on_host}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ function upload_archive() {
|
|||||||
props+=("platform=${os}-${arch}")
|
props+=("platform=${os}-${arch}")
|
||||||
props+=("changelist=${GIT_COMMIT_SHORT}")
|
props+=("changelist=${GIT_COMMIT_SHORT}")
|
||||||
props+=("branch=${branch}")
|
props+=("branch=${branch}")
|
||||||
props+=("source=https://github.com/NVIDIA/nvidia-container-toolkit")
|
props+=("source=https://gitlab.com/nvidia/container-toolkit/container-toolkit")
|
||||||
# Package properties:
|
# Package properties:
|
||||||
props+=("package.epoch=${IMAGE_EPOCH}")
|
props+=("package.epoch=${IMAGE_EPOCH}")
|
||||||
props+=("package.version=${VERSION}")
|
props+=("package.version=${VERSION}")
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ function copy_file() {
|
|||||||
-v "$(pwd):$(pwd)" \
|
-v "$(pwd):$(pwd)" \
|
||||||
-w "$(pwd)" \
|
-w "$(pwd)" \
|
||||||
-u "$(id -u):$(id -g)" \
|
-u "$(id -u):$(id -g)" \
|
||||||
--entrypoint="sh" \
|
--entrypoint="bash" \
|
||||||
"${image}" \
|
"${image}" \
|
||||||
-c "cp -p ${path_in_image} ${path_on_host}"
|
-c "cp ${path_in_image} ${path_on_host}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,3 @@ spec:
|
|||||||
install: false
|
install: false
|
||||||
nvidiaDriver:
|
nvidiaDriver:
|
||||||
install: true
|
install: true
|
||||||
branch: 550
|
|
||||||
|
|||||||
@@ -173,10 +173,10 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
|
|||||||
|
|
||||||
When("Testing CUDA Forward compatibility", Ordered, func() {
|
When("Testing CUDA Forward compatibility", Ordered, func() {
|
||||||
BeforeAll(func(ctx context.Context) {
|
BeforeAll(func(ctx context.Context) {
|
||||||
_, _, err := runner.Run("docker pull nvcr.io/nvidia/cuda:12.9.0-base-ubi8")
|
_, _, err := runner.Run("docker pull nvcr.io/nvidia/cuda:12.8.0-base-ubi8")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
compatOutput, _, err := runner.Run("docker run --rm -i -e NVIDIA_VISIBLE_DEVICES=void nvcr.io/nvidia/cuda:12.9.0-base-ubi8 bash -c \"ls /usr/local/cuda/compat/libcuda.*.*\"")
|
compatOutput, _, err := runner.Run("docker run --rm -i -e NVIDIA_VISIBLE_DEVICES=void nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ls /usr/local/cuda/compat/libcuda.*.*\"")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(compatOutput).ToNot(BeEmpty())
|
Expect(compatOutput).ToNot(BeEmpty())
|
||||||
|
|
||||||
@@ -199,21 +199,21 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("should work with the nvidia runtime in legacy mode", func(ctx context.Context) {
|
It("should work with the nvidia runtime in legacy mode", func(ctx context.Context) {
|
||||||
ldconfigOut, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia --gpus all nvcr.io/nvidia/cuda:12.9.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
|
ldconfigOut, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia --gpus all nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda-12.9/compat/"))
|
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda/compat"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should work with the nvidia runtime in CDI mode", func(ctx context.Context) {
|
It("should work with the nvidia runtime in CDI mode", func(ctx context.Context) {
|
||||||
ldconfigOut, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all nvcr.io/nvidia/cuda:12.9.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
|
ldconfigOut, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda-12.9/compat/"))
|
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda/compat"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should work with nvidia-container-runtime-hook", func(ctx context.Context) {
|
It("should NOT work with nvidia-container-runtime-hook", func(ctx context.Context) {
|
||||||
ldconfigOut, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=runc --gpus all nvcr.io/nvidia/cuda:12.9.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
|
ldconfigOut, _, err := runner.Run("docker run --rm -i -e NVIDIA_DISABLE_REQUIRE=true --runtime=runc --gpus all nvcr.io/nvidia/cuda:12.8.0-base-ubi8 bash -c \"ldconfig -p | grep libcuda.so.1\"")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(ldconfigOut).To(ContainSubstring("/usr/local/cuda-12.9/compat/"))
|
Expect(ldconfigOut).To(ContainSubstring("/usr/lib64"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -235,26 +235,4 @@ var _ = Describe("docker", Ordered, ContinueOnFailure, func() {
|
|||||||
Expect(output).To(Equal("ModifyDeviceFiles: 0\n"))
|
Expect(output).To(Equal("ModifyDeviceFiles: 0\n"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("A container is run using CDI", Ordered, func() {
|
|
||||||
BeforeAll(func(ctx context.Context) {
|
|
||||||
_, _, err := runner.Run("docker pull ubuntu")
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("should include libcuda.so in the ldcache", func(ctx context.Context) {
|
|
||||||
ldcacheOutput, _, err := runner.Run("docker run --rm -i --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=runtime.nvidia.com/gpu=all ubuntu bash -c \"ldconfig -p | grep 'libcuda.so'\"")
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(ldcacheOutput).ToNot(BeEmpty())
|
|
||||||
|
|
||||||
ldcacheLines := strings.Split(ldcacheOutput, "\n")
|
|
||||||
var libs []string
|
|
||||||
for _, line := range ldcacheLines {
|
|
||||||
parts := strings.SplitN(line, " (", 2)
|
|
||||||
libs = append(libs, strings.TrimSpace(parts[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
Expect(libs).To(ContainElements([]string{"libcuda.so", "libcuda.so.1"}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
2
third_party/libnvidia-container
vendored
2
third_party/libnvidia-container
vendored
Submodule third_party/libnvidia-container updated: 9d6a23b996...710a0f1304
201
vendor/github.com/opencontainers/cgroups/LICENSE
generated
vendored
Normal file
201
vendor/github.com/opencontainers/cgroups/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
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.
|
||||||
174
vendor/github.com/opencontainers/cgroups/devices/config/device.go
generated
vendored
Normal file
174
vendor/github.com/opencontainers/cgroups/devices/config/device.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Wildcard = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
Rule
|
||||||
|
|
||||||
|
// Path to the device.
|
||||||
|
Path string `json:"path"`
|
||||||
|
|
||||||
|
// FileMode permission bits for the device.
|
||||||
|
FileMode os.FileMode `json:"file_mode"`
|
||||||
|
|
||||||
|
// Uid of the device.
|
||||||
|
Uid uint32 `json:"uid"`
|
||||||
|
|
||||||
|
// Gid of the device.
|
||||||
|
Gid uint32 `json:"gid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions is a cgroupv1-style string to represent device access. It
|
||||||
|
// has to be a string for backward compatibility reasons, hence why it has
|
||||||
|
// methods to do set operations.
|
||||||
|
type Permissions string
|
||||||
|
|
||||||
|
const (
|
||||||
|
deviceRead uint = (1 << iota)
|
||||||
|
deviceWrite
|
||||||
|
deviceMknod
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Permissions) toSet() uint {
|
||||||
|
var set uint
|
||||||
|
for _, perm := range p {
|
||||||
|
switch perm {
|
||||||
|
case 'r':
|
||||||
|
set |= deviceRead
|
||||||
|
case 'w':
|
||||||
|
set |= deviceWrite
|
||||||
|
case 'm':
|
||||||
|
set |= deviceMknod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromSet(set uint) Permissions {
|
||||||
|
var perm string
|
||||||
|
if set&deviceRead == deviceRead {
|
||||||
|
perm += "r"
|
||||||
|
}
|
||||||
|
if set&deviceWrite == deviceWrite {
|
||||||
|
perm += "w"
|
||||||
|
}
|
||||||
|
if set&deviceMknod == deviceMknod {
|
||||||
|
perm += "m"
|
||||||
|
}
|
||||||
|
return Permissions(perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union returns the union of the two sets of Permissions.
|
||||||
|
func (p Permissions) Union(o Permissions) Permissions {
|
||||||
|
lhs := p.toSet()
|
||||||
|
rhs := o.toSet()
|
||||||
|
return fromSet(lhs | rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difference returns the set difference of the two sets of Permissions.
|
||||||
|
// In set notation, A.Difference(B) gives you A\B.
|
||||||
|
func (p Permissions) Difference(o Permissions) Permissions {
|
||||||
|
lhs := p.toSet()
|
||||||
|
rhs := o.toSet()
|
||||||
|
return fromSet(lhs &^ rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection computes the intersection of the two sets of Permissions.
|
||||||
|
func (p Permissions) Intersection(o Permissions) Permissions {
|
||||||
|
lhs := p.toSet()
|
||||||
|
rhs := o.toSet()
|
||||||
|
return fromSet(lhs & rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns whether the set of permissions in a Permissions is
|
||||||
|
// empty.
|
||||||
|
func (p Permissions) IsEmpty() bool {
|
||||||
|
return p == Permissions("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns whether the set of permissions is a subset of valid
|
||||||
|
// permissions (namely, {r,w,m}).
|
||||||
|
func (p Permissions) IsValid() bool {
|
||||||
|
return p == fromSet(p.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Type rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
WildcardDevice Type = 'a'
|
||||||
|
BlockDevice Type = 'b'
|
||||||
|
CharDevice Type = 'c' // or 'u'
|
||||||
|
FifoDevice Type = 'p'
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Type) IsValid() bool {
|
||||||
|
switch t {
|
||||||
|
case WildcardDevice, BlockDevice, CharDevice, FifoDevice:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Type) CanMknod() bool {
|
||||||
|
switch t {
|
||||||
|
case BlockDevice, CharDevice, FifoDevice:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Type) CanCgroup() bool {
|
||||||
|
switch t {
|
||||||
|
case WildcardDevice, BlockDevice, CharDevice:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
// Type of device ('c' for char, 'b' for block). If set to 'a', this rule
|
||||||
|
// acts as a wildcard and all fields other than Allow are ignored.
|
||||||
|
Type Type `json:"type"`
|
||||||
|
|
||||||
|
// Major is the device's major number.
|
||||||
|
Major int64 `json:"major"`
|
||||||
|
|
||||||
|
// Minor is the device's minor number.
|
||||||
|
Minor int64 `json:"minor"`
|
||||||
|
|
||||||
|
// Permissions is the set of permissions that this rule applies to (in the
|
||||||
|
// cgroupv1 format -- any combination of "rwm").
|
||||||
|
Permissions Permissions `json:"permissions"`
|
||||||
|
|
||||||
|
// Allow specifies whether this rule is allowed.
|
||||||
|
Allow bool `json:"allow"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Rule) CgroupString() string {
|
||||||
|
var (
|
||||||
|
major = strconv.FormatInt(d.Major, 10)
|
||||||
|
minor = strconv.FormatInt(d.Minor, 10)
|
||||||
|
)
|
||||||
|
if d.Major == Wildcard {
|
||||||
|
major = "*"
|
||||||
|
}
|
||||||
|
if d.Minor == Wildcard {
|
||||||
|
minor = "*"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%c %s:%s %s", d.Type, major, minor, d.Permissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Rule) Mkdev() (uint64, error) {
|
||||||
|
return mkDev(d)
|
||||||
|
}
|
||||||
14
vendor/github.com/opencontainers/cgroups/devices/config/mknod_unix.go
generated
vendored
Normal file
14
vendor/github.com/opencontainers/cgroups/devices/config/mknod_unix.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mkDev(d *Rule) (uint64, error) {
|
||||||
|
if d.Major == Wildcard || d.Minor == Wildcard {
|
||||||
|
return 0, errors.New("cannot mkdev() device with wildcards")
|
||||||
|
}
|
||||||
|
return unix.Mkdev(uint32(d.Major), uint32(d.Minor)), nil
|
||||||
|
}
|
||||||
20
vendor/github.com/opencontainers/runc/libcontainer/devices/device_deprecated.go
generated
vendored
Normal file
20
vendor/github.com/opencontainers/runc/libcontainer/devices/device_deprecated.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package devices
|
||||||
|
|
||||||
|
import "github.com/opencontainers/cgroups/devices/config"
|
||||||
|
|
||||||
|
// Deprecated: use [github.com/opencontainers/cgroups/devices/config].
|
||||||
|
const (
|
||||||
|
Wildcard = config.Wildcard
|
||||||
|
WildcardDevice = config.WildcardDevice
|
||||||
|
BlockDevice = config.BlockDevice
|
||||||
|
CharDevice = config.CharDevice
|
||||||
|
FifoDevice = config.FifoDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: use [github.com/opencontainers/cgroups/devices/config].
|
||||||
|
type (
|
||||||
|
Device = config.Device
|
||||||
|
Permissions = config.Permissions
|
||||||
|
Type = config.Type
|
||||||
|
Rule = config.Rule
|
||||||
|
)
|
||||||
112
vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go
generated
vendored
Normal file
112
vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package devices
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotADevice denotes that a file is not a valid linux device.
|
||||||
|
var ErrNotADevice = errors.New("not a device node")
|
||||||
|
|
||||||
|
// Testing dependencies
|
||||||
|
var (
|
||||||
|
unixLstat = unix.Lstat
|
||||||
|
osReadDir = os.ReadDir
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeviceFromPath takes the path to a device and its cgroup_permissions (which
|
||||||
|
// cannot be easily queried) to look up the information about a linux device
|
||||||
|
// and returns that information as a Device struct.
|
||||||
|
func DeviceFromPath(path, permissions string) (*Device, error) {
|
||||||
|
var stat unix.Stat_t
|
||||||
|
err := unixLstat(path, &stat)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
devType Type
|
||||||
|
mode = stat.Mode
|
||||||
|
devNumber = uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS.
|
||||||
|
major = unix.Major(devNumber)
|
||||||
|
minor = unix.Minor(devNumber)
|
||||||
|
)
|
||||||
|
switch mode & unix.S_IFMT {
|
||||||
|
case unix.S_IFBLK:
|
||||||
|
devType = BlockDevice
|
||||||
|
case unix.S_IFCHR:
|
||||||
|
devType = CharDevice
|
||||||
|
case unix.S_IFIFO:
|
||||||
|
devType = FifoDevice
|
||||||
|
default:
|
||||||
|
return nil, ErrNotADevice
|
||||||
|
}
|
||||||
|
return &Device{
|
||||||
|
Rule: Rule{
|
||||||
|
Type: devType,
|
||||||
|
Major: int64(major),
|
||||||
|
Minor: int64(minor),
|
||||||
|
Permissions: Permissions(permissions),
|
||||||
|
},
|
||||||
|
Path: path,
|
||||||
|
FileMode: os.FileMode(mode &^ unix.S_IFMT),
|
||||||
|
Uid: stat.Uid,
|
||||||
|
Gid: stat.Gid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostDevices returns all devices that can be found under /dev directory.
|
||||||
|
func HostDevices() ([]*Device, error) {
|
||||||
|
return GetDevices("/dev")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDevices recursively traverses a directory specified by path
|
||||||
|
// and returns all devices found there.
|
||||||
|
func GetDevices(path string) ([]*Device, error) {
|
||||||
|
files, err := osReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var out []*Device
|
||||||
|
for _, f := range files {
|
||||||
|
switch {
|
||||||
|
case f.IsDir():
|
||||||
|
switch f.Name() {
|
||||||
|
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
|
||||||
|
// ".udev" added to address https://github.com/opencontainers/runc/issues/2093
|
||||||
|
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev":
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
sub, err := GetDevices(filepath.Join(path, f.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, sub...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case f.Name() == "console":
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, ErrNotADevice) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if device.Type == FifoDevice {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, device)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -37,8 +37,12 @@ github.com/moby/sys/reexec
|
|||||||
# github.com/moby/sys/symlink v0.3.0
|
# github.com/moby/sys/symlink v0.3.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/moby/sys/symlink
|
github.com/moby/sys/symlink
|
||||||
|
# github.com/opencontainers/cgroups v0.0.1
|
||||||
|
## explicit; go 1.23.0
|
||||||
|
github.com/opencontainers/cgroups/devices/config
|
||||||
# github.com/opencontainers/runc v1.3.0
|
# github.com/opencontainers/runc v1.3.0
|
||||||
## explicit; go 1.23.0
|
## explicit; go 1.23.0
|
||||||
|
github.com/opencontainers/runc/libcontainer/devices
|
||||||
github.com/opencontainers/runc/libcontainer/exeseal
|
github.com/opencontainers/runc/libcontainer/exeseal
|
||||||
github.com/opencontainers/runc/libcontainer/system
|
github.com/opencontainers/runc/libcontainer/system
|
||||||
github.com/opencontainers/runc/libcontainer/utils
|
github.com/opencontainers/runc/libcontainer/utils
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
LIB_NAME := nvidia-container-toolkit
|
LIB_NAME := nvidia-container-toolkit
|
||||||
LIB_VERSION := 1.18.0
|
LIB_VERSION := 1.17.4
|
||||||
LIB_TAG := rc.1
|
LIB_TAG :=
|
||||||
|
|
||||||
# The package version is the combination of the library version and tag.
|
# The package version is the combination of the library version and tag.
|
||||||
# If the tag is specified the two components are joined with a tilde (~).
|
# If the tag is specified the two components are joined with a tilde (~).
|
||||||
|
|||||||
Reference in New Issue
Block a user