mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Compare commits
174 Commits
v1.16.0-rc
...
v1.17.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bc0315448 | ||
|
|
3fb1615d26 | ||
|
|
9e4696bf7d | ||
|
|
8c9d3d8f65 | ||
|
|
efb18a72ad | ||
|
|
75376d3df2 | ||
|
|
7e0cd45b1c | ||
|
|
a04e3ac4f7 | ||
|
|
92779e71b3 | ||
|
|
23f1ba3e93 | ||
|
|
d0d85a8c5c | ||
|
|
bfea673d6a | ||
|
|
6a6a3e6055 | ||
|
|
fa59d12973 | ||
|
|
d78868cd31 | ||
|
|
74b1e5ea8c | ||
|
|
88608781b6 | ||
|
|
fa5a4ac499 | ||
|
|
9f1bd62c42 | ||
|
|
9534249936 | ||
|
|
e1ea0056b9 | ||
|
|
c802c3089c | ||
|
|
771ac6b88a | ||
|
|
0f7aba9c3c | ||
|
|
3c07ea0b17 | ||
|
|
183dff9161 | ||
|
|
5e3e91a010 | ||
|
|
dc0e191093 | ||
|
|
8a6c1944a5 | ||
|
|
5d057dce66 | ||
|
|
5931136879 | ||
|
|
1145ce2283 | ||
|
|
38790c5df0 | ||
|
|
e5175c270e | ||
|
|
d18a2b6fc7 | ||
|
|
2987c4d670 | ||
|
|
2e6712d2bc | ||
|
|
92df542f2f | ||
|
|
1991b3ef2a | ||
|
|
cdf39fbad3 | ||
|
|
c30ca0fdc3 | ||
|
|
b077e2648d | ||
|
|
457d71c170 | ||
|
|
bc9180b59d | ||
|
|
ec8dfaf779 | ||
|
|
c129122da6 | ||
|
|
0abf800000 | ||
|
|
1d9d0acf7d | ||
|
|
17f14278a9 | ||
|
|
1fa5bbf351 | ||
|
|
f794d09df1 | ||
|
|
17a2377ad5 | ||
|
|
b90ee5d100 | ||
|
|
1ef3f4048f | ||
|
|
7fb31bd1dc | ||
|
|
e2fe591535 | ||
|
|
adf3708d0b | ||
|
|
a06d838b1c | ||
|
|
f477dc0df1 | ||
|
|
879bb9ffd5 | ||
|
|
4604e3b6c8 | ||
|
|
a9ca6995f7 | ||
|
|
7cd2aef0d8 | ||
|
|
19482dac6f | ||
|
|
78c4ca8a12 | ||
|
|
b12bdfc52a | ||
|
|
82ae2e615a | ||
|
|
4f440dedda | ||
|
|
3ee678f4f6 | ||
|
|
103375e504 | ||
|
|
5bedbc2b50 | ||
|
|
94337b7427 | ||
|
|
046a05921f | ||
|
|
6ca2700a17 | ||
|
|
0d626cfbb7 | ||
|
|
10bafd1d09 | ||
|
|
bf2bdfd35e | ||
|
|
f126877254 | ||
|
|
006aebf31e | ||
|
|
6c5f4eea63 | ||
|
|
b0b7c7c9ee | ||
|
|
b466270a24 | ||
|
|
d806f1045b | ||
|
|
35ee96ac41 | ||
|
|
f8141aab27 | ||
|
|
98ffe2aa67 | ||
|
|
79c59aeb7f | ||
|
|
906531fee3 | ||
|
|
0e68f60c0b | ||
|
|
563db0e0be | ||
|
|
7b770f63c3 | ||
|
|
dcbf5bc81f | ||
|
|
978d439cf8 | ||
|
|
aa946f3f59 | ||
|
|
a5a5833c14 | ||
|
|
8e07d90802 | ||
|
|
2aadbbf22d | ||
|
|
88f9414849 | ||
|
|
53c2dc6301 | ||
|
|
3121663537 | ||
|
|
4b6de8036d | ||
|
|
dc2ccdd2fa | ||
|
|
5145b0a4b6 | ||
|
|
a819cfdab4 | ||
|
|
629db79b4f | ||
|
|
8693dd6962 | ||
|
|
51cc619eab | ||
|
|
9b72161b63 | ||
|
|
d925b6596b | ||
|
|
c02b144ed4 | ||
|
|
16fdef50e6 | ||
|
|
b061446694 | ||
|
|
9c2476c98d | ||
|
|
70da6cfa50 | ||
|
|
a4bfccc3fe | ||
|
|
66f50d91bd | ||
|
|
ba1ed3232f | ||
|
|
c490baab63 | ||
|
|
32486cf1e9 | ||
|
|
b9c3185d72 | ||
|
|
e383b75a4d | ||
|
|
5827434cae | ||
|
|
6ad699c095 | ||
|
|
2450b002a8 | ||
|
|
03d1acc7b0 | ||
|
|
39120d5878 | ||
|
|
da5e3ce8c3 | ||
|
|
b47f954b7c | ||
|
|
f959c0daaa | ||
|
|
c3c0cdcc89 | ||
|
|
e3936fd623 | ||
|
|
f4987580d2 | ||
|
|
f3fceab317 | ||
|
|
ff2ba2bbc5 | ||
|
|
0c554cbf7e | ||
|
|
beb921fafe | ||
|
|
45030d1169 | ||
|
|
2f37055bc6 | ||
|
|
5316c2a618 | ||
|
|
89e4ae70c8 | ||
|
|
c68b4be12f | ||
|
|
89c12c1368 | ||
|
|
9d4294450d | ||
|
|
070b40d62a | ||
|
|
13862f3c75 | ||
|
|
05449807d1 | ||
|
|
b7948428ff | ||
|
|
d2750e86c3 | ||
|
|
0e4759cf36 | ||
|
|
d767e4cfe9 | ||
|
|
e454caf577 | ||
|
|
fa46c01331 | ||
|
|
122136fe92 | ||
|
|
fa16d83494 | ||
|
|
faabbd36d6 | ||
|
|
a470818ba7 | ||
|
|
404a2763cb | ||
|
|
87a63a6159 | ||
|
|
d8ccd50349 | ||
|
|
95694f082b | ||
|
|
c5c124b882 | ||
|
|
a67c3a1bb5 | ||
|
|
51911974fb | ||
|
|
73a6c4d521 | ||
|
|
eb7983af41 | ||
|
|
2273664328 | ||
|
|
49acaef503 | ||
|
|
fb6a767ada | ||
|
|
76f419e3ba | ||
|
|
df3a7729ce | ||
|
|
509993e138 | ||
|
|
fce56f905b | ||
|
|
832fcba5cd | ||
|
|
b646372998 |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -22,7 +22,7 @@ updates:
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
# This defines a specific dependabot rule for the latest release-* branch.
|
||||
target-branch: release-1.15
|
||||
target-branch: release-1.16
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -34,7 +34,7 @@ updates:
|
||||
- maintenance
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: release-1.15
|
||||
target-branch: release-1.16
|
||||
directory: "/deployments/container"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
52
.github/workflows/code_scanning.yaml
vendored
Normal file
52
.github/workflows/code_scanning.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze Go code with CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 360
|
||||
permissions:
|
||||
security-events: write
|
||||
packages: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: go
|
||||
build-mode: manual
|
||||
- shell: bash
|
||||
run: |
|
||||
make build
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:go"
|
||||
4
.github/workflows/golang.yaml
vendored
4
.github/workflows/golang.yaml
vendored
@@ -49,7 +49,9 @@ jobs:
|
||||
args: -v --timeout 5m
|
||||
skip-cache: true
|
||||
- name: Check golang modules
|
||||
run: make check-vendor
|
||||
run: |
|
||||
make check-vendor
|
||||
make -C deployments/devel check-modules
|
||||
test:
|
||||
name: Unit test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
run:
|
||||
deadline: 10m
|
||||
timeout: 10m
|
||||
|
||||
linters:
|
||||
enable:
|
||||
@@ -23,6 +23,8 @@ issues:
|
||||
exclude:
|
||||
# The legacy hook relies on spec.Hooks.Prestart, which is deprecated as of the v1.2.0 OCI runtime spec.
|
||||
- "SA1019:(.+).Prestart is deprecated(.+)"
|
||||
# TODO: We should address each of the following integer overflows.
|
||||
- "G115: integer overflow conversion(.+)"
|
||||
exclude-rules:
|
||||
# Exclude the gocritic dupSubExpr issue for cgo files.
|
||||
- path: internal/dxcore/dxcore.go
|
||||
@@ -34,3 +36,8 @@ issues:
|
||||
linters:
|
||||
- errcheck
|
||||
text: config.Delete
|
||||
# RENDERD refers to the Render Device and not the past tense of render.
|
||||
- path: .*.go
|
||||
linters:
|
||||
- misspell
|
||||
text: "`RENDERD` is a misspelling of `RENDERED`"
|
||||
|
||||
61
CHANGELOG.md
61
CHANGELOG.md
@@ -1,5 +1,64 @@
|
||||
# NVIDIA Container Toolkit Changelog
|
||||
|
||||
## v1.17.0
|
||||
- Promote v1.17.0-rc.2 to v1.17.0
|
||||
- Fix bug when using just-in-time CDI spec generation
|
||||
- Check for valid paths in create-symlinks hook
|
||||
|
||||
## v1.17.0-rc.2
|
||||
- Fix bug in locating libcuda.so from ldcache
|
||||
- Fix bug in sorting of symlink chain
|
||||
- Remove unsupported print-ldcache command
|
||||
- Remove csv-filename support from create-symlinks
|
||||
|
||||
### Changes in the Toolkit Container
|
||||
- Fallback to `crio-status` if `crio status` does not work when configuring the crio runtime
|
||||
|
||||
## v1.17.0-rc.1
|
||||
- Allow IMEX channels to be requested as volume mounts
|
||||
- Fix typo in error message
|
||||
- Add disable-imex-channel-creation feature flag
|
||||
- Add -z,lazy to LDFLAGS
|
||||
- Add imex channels to management CDI spec
|
||||
- Add support to fetch current container runtime config from the command line.
|
||||
- Add creation of select driver symlinks to CDI spec generation.
|
||||
- Remove support for config overrides when configuring runtimes.
|
||||
- Skip explicit creation of libnvidia-allocator.so.1 symlink
|
||||
- Add vdpau as as a driver library search path.
|
||||
- Add support for using libnvsandboxutils to generate CDI specifications.
|
||||
|
||||
### Changes in the Toolkit Container
|
||||
|
||||
- Allow opt-in features to be selected when deploying the toolkit-container.
|
||||
- Bump CUDA base image version to 12.6.2
|
||||
- Remove support for config overrides when configuring runtimes.
|
||||
|
||||
### Changes in libnvidia-container
|
||||
|
||||
- Add no-create-imex-channels command line option.
|
||||
|
||||
## v1.16.2
|
||||
- Exclude libnvidia-allocator from graphics mounts. This fixes a bug that leaks mounts when a container is started with bi-directional mount propagation.
|
||||
- Use empty string for default runtime-config-override. This removes a redundant warning for runtimes (e.g. Docker) where this is not applicable.
|
||||
|
||||
### Changes in the Toolkit Container
|
||||
- Bump CUDA base image version to 12.6.0
|
||||
|
||||
### Changes in libnvidia-container
|
||||
- Add no-gsp-firmware command line option
|
||||
- Add no-fabricmanager command line option
|
||||
- Add no-persistenced command line option
|
||||
- Skip directories and symlinks when mounting libraries.
|
||||
|
||||
## v1.16.1
|
||||
- Fix bug with processing errors during CDI spec generation for MIG devices
|
||||
|
||||
## v1.16.0
|
||||
- Promote v1.16.0-rc.2 to v1.16.0
|
||||
|
||||
### Changes in the Toolkit Container
|
||||
- Bump CUDA base image version to 12.5.1
|
||||
|
||||
## v1.16.0-rc.2
|
||||
- Use relative path to locate driver libraries
|
||||
- Add RelativeToRoot function to Driver
|
||||
@@ -113,7 +172,7 @@
|
||||
## v1.14.0-rc.2
|
||||
* Fix bug causing incorrect nvidia-smi symlink to be created on WSL2 systems with multiple driver roots.
|
||||
* Remove dependency on coreutils when installing package on RPM-based systems.
|
||||
* Create ouput folders if required when running `nvidia-ctk runtime configure`
|
||||
* Create output folders if required when running `nvidia-ctk runtime configure`
|
||||
* Generate default config as post-install step.
|
||||
* Added support for detecting GSP firmware at custom paths when generating CDI specifications.
|
||||
* Added logic to skip the extraction of image requirements if `NVIDIA_DISABLE_REQUIRES` is set to `true`.
|
||||
|
||||
2
Makefile
2
Makefile
@@ -60,7 +60,7 @@ endif
|
||||
cmds: $(CMD_TARGETS)
|
||||
|
||||
ifneq ($(shell uname),Darwin)
|
||||
EXTLDFLAGS = -Wl,--export-dynamic -Wl,--unresolved-symbols=ignore-in-object-files
|
||||
EXTLDFLAGS = -Wl,--export-dynamic -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-z,lazy
|
||||
else
|
||||
EXTLDFLAGS = -Wl,-undefined,dynamic_lookup
|
||||
endif
|
||||
|
||||
36
RELEASE.md
Normal file
36
RELEASE.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Release Process
|
||||
|
||||
The NVIDIA Container Toolkit consists of the following artifacts:
|
||||
- The NVIDIA Container Toolkit container
|
||||
- Packages for debian-based systems
|
||||
- Packages for rpm-based systems
|
||||
|
||||
# Release Process Checklist:
|
||||
- [ ] Create a release PR:
|
||||
- [ ] Run the `./hack/prepare-release.sh` script to update the version in all the needed files. This also creates a [release issue](https://github.com/NVIDIA/cloud-native-team/issues?q=is%3Aissue+is%3Aopen+label%3Arelease)
|
||||
- [ ] Run the `./hack/generate-changelog.sh` script to generate the a draft changelog and update `CHANGELOG.md` with the changes.
|
||||
- [ ] Create a PR from the created `bump-release-{{ .VERSION }}` branch.
|
||||
- [ ] Merge the release PR
|
||||
- [ ] Tag the release and push the tag to the `internal` mirror:
|
||||
- [ ] Image release pipeline: https://gitlab-master.nvidia.com/dl/container-dev/container-toolkit/-/pipelines/16466098
|
||||
- [ ] Wait for the image release to complete.
|
||||
- [ ] Push the tag to the the upstream GitHub repo.
|
||||
- [ ] Wait for the [`Release`](https://github.com/NVIDIA/k8s-device-plugin/actions/workflows/release.yaml) GitHub Action to complete
|
||||
- [ ] Publish the [draft release](https://github.com/NVIDIA/k8s-device-plugin/releases) created by the GitHub Action
|
||||
- [ ] Publish the packages to the gh-pages branch of the libnvidia-container repo
|
||||
- [ ] Create a KitPick
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
*Note*: This assumes that we have the release tag checked out locally.
|
||||
|
||||
- If the `Release` GitHub Action fails:
|
||||
- Check the logs for the error first.
|
||||
- Create the helm packages locally by running:
|
||||
```bash
|
||||
./hack/prepare-artifacts.sh {{ .VERSION }}
|
||||
```
|
||||
- Create the draft release by running:
|
||||
```bash
|
||||
./hack/create-release.sh {{ .VERSION }}
|
||||
```
|
||||
@@ -17,18 +17,18 @@
|
||||
package symlinks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/sys/symlink"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
@@ -36,8 +36,6 @@ type command struct {
|
||||
}
|
||||
|
||||
type config struct {
|
||||
hostRoot string
|
||||
filenames cli.StringSlice
|
||||
links cli.StringSlice
|
||||
containerSpec string
|
||||
}
|
||||
@@ -50,39 +48,30 @@ func NewCommand(logger logger.Interface) *cli.Command {
|
||||
return c.build()
|
||||
}
|
||||
|
||||
// build
|
||||
// build creates the create-symlink command.
|
||||
func (m command) build() *cli.Command {
|
||||
cfg := config{}
|
||||
|
||||
// Create the '' command
|
||||
c := cli.Command{
|
||||
Name: "create-symlinks",
|
||||
Usage: "A hook to create symlinks in the container. This can be used to process CSV mount specs",
|
||||
Usage: "A hook to create symlinks in the container.",
|
||||
Action: func(c *cli.Context) error {
|
||||
return m.run(c, &cfg)
|
||||
},
|
||||
}
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "host-root",
|
||||
Usage: "The root on the host filesystem to use to resolve symlinks",
|
||||
Destination: &cfg.hostRoot,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "csv-filename",
|
||||
Usage: "Specify a (CSV) filename to process",
|
||||
Destination: &cfg.filenames,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "link",
|
||||
Usage: "Specify a specific link to create. The link is specified as target::link",
|
||||
Destination: &cfg.links,
|
||||
},
|
||||
// The following flags are testing-only flags.
|
||||
&cli.StringFlag{
|
||||
Name: "container-spec",
|
||||
Usage: "Specify the path to the OCI container spec. If empty or '-' the spec will be read from STDIN",
|
||||
Usage: "Specify the path to the OCI container spec. If empty or '-' the spec will be read from STDIN. This is only intended for testing.",
|
||||
Destination: &cfg.containerSpec,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -100,90 +89,60 @@ func (m command) run(c *cli.Context, cfg *config) error {
|
||||
return fmt.Errorf("failed to determined container root: %v", err)
|
||||
}
|
||||
|
||||
csvFiles := cfg.filenames.Value()
|
||||
|
||||
chainLocator := lookup.NewSymlinkChainLocator(
|
||||
lookup.WithLogger(m.logger),
|
||||
lookup.WithRoot(cfg.hostRoot),
|
||||
)
|
||||
|
||||
var candidates []string
|
||||
for _, file := range csvFiles {
|
||||
mountSpecs, err := csv.NewCSVFileParser(m.logger, file).Parse()
|
||||
if err != nil {
|
||||
m.logger.Debugf("Skipping CSV file %v: %v", file, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ms := range mountSpecs {
|
||||
if ms.Type != csv.MountSpecSym {
|
||||
continue
|
||||
}
|
||||
targets, err := chainLocator.Locate(ms.Path)
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to locate symlink %v", ms.Path)
|
||||
}
|
||||
candidates = append(candidates, targets...)
|
||||
}
|
||||
}
|
||||
|
||||
created := make(map[string]bool)
|
||||
// candidates is a list of absolute paths to symlinks in a chain, or the final target of the chain.
|
||||
for _, candidate := range candidates {
|
||||
target, err := symlinks.Resolve(candidate)
|
||||
if err != nil {
|
||||
m.logger.Debugf("Skipping invalid link: %v", err)
|
||||
continue
|
||||
} else if target == candidate {
|
||||
m.logger.Debugf("%v is not a symlink", candidate)
|
||||
for _, l := range cfg.links.Value() {
|
||||
if created[l] {
|
||||
m.logger.Debugf("Link %v already processed", l)
|
||||
continue
|
||||
}
|
||||
|
||||
err = m.createLink(created, cfg.hostRoot, containerRoot, target, candidate)
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to create link %v: %v", []string{target, candidate}, err)
|
||||
}
|
||||
}
|
||||
|
||||
links := cfg.links.Value()
|
||||
for _, l := range links {
|
||||
parts := strings.Split(l, "::")
|
||||
if len(parts) != 2 {
|
||||
m.logger.Warningf("Invalid link specification %v", l)
|
||||
continue
|
||||
return fmt.Errorf("invalid symlink specification %v", l)
|
||||
}
|
||||
|
||||
err := m.createLink(created, cfg.hostRoot, containerRoot, parts[0], parts[1])
|
||||
err := m.createLink(containerRoot, parts[0], parts[1])
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to create link %v: %v", parts, err)
|
||||
return fmt.Errorf("failed to create link %v: %w", parts, err)
|
||||
}
|
||||
created[l] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (m command) createLink(created map[string]bool, hostRoot string, containerRoot string, target string, link string) error {
|
||||
linkPath, err := changeRoot(hostRoot, containerRoot, link)
|
||||
// createLink creates a symbolic link in the specified container root.
|
||||
// This is equivalent to:
|
||||
//
|
||||
// chroot {{ .containerRoot }} ln -s {{ .target }} {{ .link }}
|
||||
//
|
||||
// If the specified link already exists and points to the same target, this
|
||||
// operation is a no-op. If the link points to a different target, an error is
|
||||
// returned.
|
||||
//
|
||||
// Note that if the link path resolves to an absolute path oudside of the
|
||||
// specified root, this is treated as an absolute path in this root.
|
||||
func (m command) createLink(containerRoot string, targetPath string, link string) error {
|
||||
linkPath := filepath.Join(containerRoot, link)
|
||||
|
||||
exists, err := doesLinkExist(targetPath, linkPath)
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to resolve path for link %v relative to %v: %v", link, containerRoot, err)
|
||||
return fmt.Errorf("failed to check if link exists: %w", err)
|
||||
}
|
||||
if created[linkPath] {
|
||||
m.logger.Debugf("Link %v already created", linkPath)
|
||||
if exists {
|
||||
m.logger.Debugf("Link %s already exists", linkPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
targetPath, err := changeRoot(hostRoot, "/", target)
|
||||
resolvedLinkPath, err := symlink.FollowSymlinkInScope(linkPath, containerRoot)
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to resolve path for target %v relative to %v: %v", target, "/", err)
|
||||
return fmt.Errorf("failed to follow path for link %v relative to %v: %w", link, containerRoot, err)
|
||||
}
|
||||
|
||||
m.logger.Infof("Symlinking %v to %v", linkPath, targetPath)
|
||||
err = os.MkdirAll(filepath.Dir(linkPath), 0755)
|
||||
m.logger.Infof("Symlinking %v to %v", resolvedLinkPath, targetPath)
|
||||
err = os.MkdirAll(filepath.Dir(resolvedLinkPath), 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory: %v", err)
|
||||
}
|
||||
err = os.Symlink(target, linkPath)
|
||||
err = os.Symlink(targetPath, resolvedLinkPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create symlink: %v", err)
|
||||
}
|
||||
@@ -191,41 +150,18 @@ func (m command) createLink(created map[string]bool, hostRoot string, containerR
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeRoot(current string, new string, path string) (string, error) {
|
||||
if !filepath.IsAbs(path) {
|
||||
return path, nil
|
||||
// doesLinkExist returns true if link exists and points to target.
|
||||
// An error is returned if link exists but points to a different target.
|
||||
func doesLinkExist(target string, link string) (bool, error) {
|
||||
currentTarget, err := symlinks.Resolve(link)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
relative := path
|
||||
if current != "" {
|
||||
r, err := filepath.Rel(current, path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
relative = r
|
||||
}
|
||||
|
||||
return filepath.Join(new, relative), nil
|
||||
}
|
||||
|
||||
// Locate returns the link target of the specified filename or an empty slice if the
|
||||
// specified filename is not a symlink.
|
||||
func (m command) Locate(filename string) ([]string, error) {
|
||||
info, err := os.Lstat(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get file info: %v", info)
|
||||
return false, fmt.Errorf("failed to resolve existing symlink %s: %w", link, err)
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink == 0 {
|
||||
m.logger.Debugf("%v is not a symlink", filename)
|
||||
return nil, nil
|
||||
if currentTarget == target {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
target, err := os.Readlink(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error checking symlink: %v", err)
|
||||
}
|
||||
|
||||
m.logger.Debugf("Resolved link: '%v' => '%v'", filename, target)
|
||||
|
||||
return []string{target}, nil
|
||||
return true, fmt.Errorf("unexpected link target: %s", currentTarget)
|
||||
}
|
||||
|
||||
282
cmd/nvidia-cdi-hook/create-symlinks/create-symlinks_test.go
Normal file
282
cmd/nvidia-cdi-hook/create-symlinks/create-symlinks_test.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package symlinks
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
|
||||
)
|
||||
|
||||
func TestDoesLinkExist(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(
|
||||
t,
|
||||
makeFs(tmpDir,
|
||||
dirOrLink{path: "/a/b/c", target: "d"},
|
||||
dirOrLink{path: "/a/b/e", target: "/a/b/f"},
|
||||
),
|
||||
)
|
||||
|
||||
exists, err := doesLinkExist("d", filepath.Join(tmpDir, "/a/b/c"))
|
||||
require.NoError(t, err)
|
||||
require.True(t, exists)
|
||||
|
||||
exists, err = doesLinkExist("/a/b/f", filepath.Join(tmpDir, "/a/b/e"))
|
||||
require.NoError(t, err)
|
||||
require.True(t, exists)
|
||||
|
||||
_, err = doesLinkExist("different-target", filepath.Join(tmpDir, "/a/b/c"))
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = doesLinkExist("/a/b/d", filepath.Join(tmpDir, "/a/b/c"))
|
||||
require.Error(t, err)
|
||||
|
||||
exists, err = doesLinkExist("foo", filepath.Join(tmpDir, "/a/b/does-not-exist"))
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
}
|
||||
|
||||
func TestCreateLink(t *testing.T) {
|
||||
type link struct {
|
||||
path string
|
||||
target string
|
||||
}
|
||||
type expectedLink struct {
|
||||
link
|
||||
err error
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
containerContents []dirOrLink
|
||||
link link
|
||||
expectedCreateError error
|
||||
expectedLinks []expectedLink
|
||||
}{
|
||||
{
|
||||
description: "link to / resolves to container root",
|
||||
containerContents: []dirOrLink{
|
||||
{path: "/lib/foo", target: "/"},
|
||||
},
|
||||
link: link{
|
||||
path: "/lib/foo/libfoo.so",
|
||||
target: "libfoo.so.1",
|
||||
},
|
||||
expectedLinks: []expectedLink{
|
||||
{
|
||||
link: link{
|
||||
path: "{{ .containerRoot }}/libfoo.so",
|
||||
target: "libfoo.so.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "link to / resolves to container root; parent relative link",
|
||||
containerContents: []dirOrLink{
|
||||
{path: "/lib/foo", target: "/"},
|
||||
},
|
||||
link: link{
|
||||
path: "/lib/foo/libfoo.so",
|
||||
target: "../libfoo.so.1",
|
||||
},
|
||||
expectedLinks: []expectedLink{
|
||||
{
|
||||
link: link{
|
||||
path: "{{ .containerRoot }}/libfoo.so",
|
||||
target: "../libfoo.so.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "link to / resolves to container root; absolute link",
|
||||
containerContents: []dirOrLink{
|
||||
{path: "/lib/foo", target: "/"},
|
||||
},
|
||||
link: link{
|
||||
path: "/lib/foo/libfoo.so",
|
||||
target: "/a-path-in-container/foo/libfoo.so.1",
|
||||
},
|
||||
expectedLinks: []expectedLink{
|
||||
{
|
||||
link: link{
|
||||
path: "{{ .containerRoot }}/libfoo.so",
|
||||
target: "/a-path-in-container/foo/libfoo.so.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
// We also check that the target is NOT created.
|
||||
link: link{
|
||||
path: "{{ .containerRoot }}/a-path-in-container/foo/libfoo.so.1",
|
||||
},
|
||||
err: os.ErrNotExist,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t, makeFs(containerRoot, tc.containerContents...))
|
||||
|
||||
// nvidia-cdi-hook create-symlinks --link linkSpec
|
||||
err := getTestCommand().createLink(containerRoot, tc.link.target, tc.link.path)
|
||||
// TODO: We may be able to replace this with require.ErrorIs.
|
||||
if tc.expectedCreateError != nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, expectedLink := range tc.expectedLinks {
|
||||
path := strings.ReplaceAll(expectedLink.path, "{{ .containerRoot }}", containerRoot)
|
||||
path = strings.ReplaceAll(path, "{{ .hostRoot }}", hostRoot)
|
||||
if expectedLink.target != "" {
|
||||
target, err := symlinks.Resolve(path)
|
||||
require.ErrorIs(t, err, expectedLink.err)
|
||||
require.Equal(t, expectedLink.target, target)
|
||||
} else {
|
||||
_, err := os.Stat(path)
|
||||
require.ErrorIs(t, err, expectedLink.err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateLinkRelativePath(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t, makeFs(containerRoot, dirOrLink{path: "/lib/"}))
|
||||
|
||||
// nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so
|
||||
err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so")
|
||||
require.NoError(t, err)
|
||||
|
||||
target, err := symlinks.Resolve(filepath.Join(containerRoot, "/lib/libfoo.so"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "libfoo.so.1", target)
|
||||
}
|
||||
|
||||
func TestCreateLinkAbsolutePath(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t, makeFs(containerRoot, dirOrLink{path: "/lib/"}))
|
||||
|
||||
// nvidia-cdi-hook create-symlinks --link /lib/libfoo.so.1::/lib/libfoo.so
|
||||
err := getTestCommand().createLink(containerRoot, "/lib/libfoo.so.1", "/lib/libfoo.so")
|
||||
require.NoError(t, err)
|
||||
|
||||
target, err := symlinks.Resolve(filepath.Join(containerRoot, "/lib/libfoo.so"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "/lib/libfoo.so.1", target)
|
||||
}
|
||||
|
||||
func TestCreateLinkAlreadyExists(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t, makeFs(containerRoot, dirOrLink{path: "/lib/libfoo.so", target: "libfoo.so.1"}))
|
||||
|
||||
// nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so
|
||||
err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so")
|
||||
require.NoError(t, err)
|
||||
target, err := symlinks.Resolve(filepath.Join(containerRoot, "lib/libfoo.so"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "libfoo.so.1", target)
|
||||
}
|
||||
|
||||
func TestCreateLinkAlreadyExistsDifferentTarget(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t, makeFs(containerRoot, dirOrLink{path: "/lib/libfoo.so", target: "different-target"}))
|
||||
|
||||
// nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so
|
||||
err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so")
|
||||
require.Error(t, err)
|
||||
target, err := symlinks.Resolve(filepath.Join(containerRoot, "lib/libfoo.so"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "different-target", target)
|
||||
}
|
||||
|
||||
func TestCreateLinkOutOfBounds(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t,
|
||||
makeFs(containerRoot,
|
||||
dirOrLink{path: "/lib"},
|
||||
dirOrLink{path: "/lib/foo", target: hostRoot},
|
||||
),
|
||||
)
|
||||
|
||||
path, err := symlinks.Resolve(filepath.Join(containerRoot, "/lib/foo"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hostRoot, path)
|
||||
|
||||
// nvidia-cdi-hook create-symlinks --link ../libfoo.so.1::/lib/foo/libfoo.so
|
||||
_ = getTestCommand().createLink(containerRoot, "../libfoo.so.1", "/lib/foo/libfoo.so")
|
||||
// TODO: We need to enabled this check once we have updated the implementation.
|
||||
// require.Error(t, err)
|
||||
_, err = os.Lstat(filepath.Join(hostRoot, "libfoo.so"))
|
||||
require.ErrorIs(t, err, os.ErrNotExist)
|
||||
_, err = os.Lstat(filepath.Join(containerRoot, hostRoot, "libfoo.so"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
type dirOrLink struct {
|
||||
path string
|
||||
target string
|
||||
}
|
||||
|
||||
func makeFs(tmpdir string, fs ...dirOrLink) error {
|
||||
if err := os.MkdirAll(tmpdir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range fs {
|
||||
s.path = filepath.Join(tmpdir, s.path)
|
||||
if s.target == "" {
|
||||
_ = os.MkdirAll(s.path, 0o755)
|
||||
continue
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(s.path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Symlink(s.target, s.path); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTestCommand creates a command for running tests against.
|
||||
func getTestCommand() *command {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
return &command{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/mod/semver"
|
||||
@@ -15,31 +13,15 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
)
|
||||
|
||||
const (
|
||||
envCUDAVersion = "CUDA_VERSION"
|
||||
envNVRequirePrefix = "NVIDIA_REQUIRE_"
|
||||
envNVRequireCUDA = envNVRequirePrefix + "CUDA"
|
||||
envNVDisableRequire = "NVIDIA_DISABLE_REQUIRE"
|
||||
envNVVisibleDevices = "NVIDIA_VISIBLE_DEVICES"
|
||||
envNVMigConfigDevices = "NVIDIA_MIG_CONFIG_DEVICES"
|
||||
envNVMigMonitorDevices = "NVIDIA_MIG_MONITOR_DEVICES"
|
||||
envNVImexChannels = "NVIDIA_IMEX_CHANNELS"
|
||||
envNVDriverCapabilities = "NVIDIA_DRIVER_CAPABILITIES"
|
||||
)
|
||||
|
||||
const (
|
||||
capSysAdmin = "CAP_SYS_ADMIN"
|
||||
)
|
||||
|
||||
const (
|
||||
deviceListAsVolumeMountsRoot = "/var/run/nvidia-container-devices"
|
||||
)
|
||||
|
||||
type nvidiaConfig struct {
|
||||
Devices string
|
||||
Devices []string
|
||||
MigConfigDevices string
|
||||
MigMonitorDevices string
|
||||
ImexChannels string
|
||||
ImexChannels []string
|
||||
DriverCapabilities string
|
||||
// Requirements defines the requirements DSL for the container to run.
|
||||
// This is empty if no specific requirements are needed, or if requirements are
|
||||
@@ -77,23 +59,14 @@ type LinuxCapabilities struct {
|
||||
Ambient []string `json:"ambient,omitempty" platform:"linux"`
|
||||
}
|
||||
|
||||
// Mount from OCI runtime spec
|
||||
// https://github.com/opencontainers/runtime-spec/blob/v1.0.0/specs-go/config.go#L103
|
||||
type Mount struct {
|
||||
Destination string `json:"destination"`
|
||||
Type string `json:"type,omitempty" platform:"linux,solaris"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// Spec from OCI runtime spec
|
||||
// We use pointers to structs, similarly to the latest version of runtime-spec:
|
||||
// https://github.com/opencontainers/runtime-spec/blob/v1.0.0/specs-go/config.go#L5-L28
|
||||
type Spec struct {
|
||||
Version *string `json:"ociVersion"`
|
||||
Process *Process `json:"process,omitempty"`
|
||||
Root *Root `json:"root,omitempty"`
|
||||
Mounts []Mount `json:"mounts,omitempty"`
|
||||
Version *string `json:"ociVersion"`
|
||||
Process *Process `json:"process,omitempty"`
|
||||
Root *Root `json:"root,omitempty"`
|
||||
Mounts []specs.Mount `json:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
// HookState holds state information about the hook
|
||||
@@ -172,82 +145,30 @@ func isPrivileged(s *Spec) bool {
|
||||
return image.IsPrivileged(&fullSpec)
|
||||
}
|
||||
|
||||
func getDevicesFromEnvvar(image image.CUDA, swarmResourceEnvvars []string) *string {
|
||||
func getDevicesFromEnvvar(containerImage image.CUDA, swarmResourceEnvvars []string) []string {
|
||||
// We check if the image has at least one of the Swarm resource envvars defined and use this
|
||||
// if specified.
|
||||
var hasSwarmEnvvar bool
|
||||
for _, envvar := range swarmResourceEnvvars {
|
||||
if image.HasEnvvar(envvar) {
|
||||
hasSwarmEnvvar = true
|
||||
break
|
||||
if containerImage.HasEnvvar(envvar) {
|
||||
return containerImage.DevicesFromEnvvars(swarmResourceEnvvars...).List()
|
||||
}
|
||||
}
|
||||
|
||||
var devices []string
|
||||
if hasSwarmEnvvar {
|
||||
devices = image.DevicesFromEnvvars(swarmResourceEnvvars...).List()
|
||||
} else {
|
||||
devices = image.DevicesFromEnvvars(envNVVisibleDevices).List()
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
devicesString := strings.Join(devices, ",")
|
||||
|
||||
return &devicesString
|
||||
return containerImage.VisibleDevicesFromEnvVar()
|
||||
}
|
||||
|
||||
func getDevicesFromMounts(mounts []Mount) *string {
|
||||
var devices []string
|
||||
for _, m := range mounts {
|
||||
root := filepath.Clean(deviceListAsVolumeMountsRoot)
|
||||
source := filepath.Clean(m.Source)
|
||||
destination := filepath.Clean(m.Destination)
|
||||
|
||||
// Only consider mounts who's host volume is /dev/null
|
||||
if source != "/dev/null" {
|
||||
continue
|
||||
}
|
||||
// Only consider container mount points that begin with 'root'
|
||||
if len(destination) < len(root) {
|
||||
continue
|
||||
}
|
||||
if destination[:len(root)] != root {
|
||||
continue
|
||||
}
|
||||
// Grab the full path beyond 'root' and add it to the list of devices
|
||||
device := destination[len(root):]
|
||||
if len(device) > 0 && device[0] == '/' {
|
||||
device = device[1:]
|
||||
}
|
||||
if len(device) == 0 {
|
||||
continue
|
||||
}
|
||||
devices = append(devices, device)
|
||||
}
|
||||
|
||||
if devices == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := strings.Join(devices, ",")
|
||||
return &ret
|
||||
}
|
||||
|
||||
func getDevices(hookConfig *HookConfig, image image.CUDA, mounts []Mount, privileged bool) *string {
|
||||
func getDevices(hookConfig *HookConfig, image image.CUDA, privileged bool) []string {
|
||||
// If enabled, try and get the device list from volume mounts first
|
||||
if hookConfig.AcceptDeviceListAsVolumeMounts {
|
||||
devices := getDevicesFromMounts(mounts)
|
||||
if devices != nil {
|
||||
devices := image.VisibleDevicesFromMounts()
|
||||
if len(devices) > 0 {
|
||||
return devices
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to reading from the environment variable if privileges are correct
|
||||
devices := getDevicesFromEnvvar(image, hookConfig.getSwarmResourceEnvvars())
|
||||
if devices == nil {
|
||||
if len(devices) == 0 {
|
||||
return nil
|
||||
}
|
||||
if privileged || hookConfig.AcceptEnvvarUnprivileged {
|
||||
@@ -260,12 +181,12 @@ func getDevices(hookConfig *HookConfig, image image.CUDA, mounts []Mount, privil
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMigConfigDevices(image image.CUDA) *string {
|
||||
return getMigDevices(image, envNVMigConfigDevices)
|
||||
func getMigConfigDevices(i image.CUDA) *string {
|
||||
return getMigDevices(i, image.EnvVarNvidiaMigConfigDevices)
|
||||
}
|
||||
|
||||
func getMigMonitorDevices(image image.CUDA) *string {
|
||||
return getMigDevices(image, envNVMigMonitorDevices)
|
||||
func getMigMonitorDevices(i image.CUDA) *string {
|
||||
return getMigDevices(i, image.EnvVarNvidiaMigMonitorDevices)
|
||||
}
|
||||
|
||||
func getMigDevices(image image.CUDA, envvar string) *string {
|
||||
@@ -276,12 +197,24 @@ func getMigDevices(image image.CUDA, envvar string) *string {
|
||||
return &devices
|
||||
}
|
||||
|
||||
func getImexChannels(image image.CUDA) *string {
|
||||
if !image.HasEnvvar(envNVImexChannels) {
|
||||
func getImexChannels(hookConfig *HookConfig, image image.CUDA, privileged bool) []string {
|
||||
// If enabled, try and get the device list from volume mounts first
|
||||
if hookConfig.AcceptDeviceListAsVolumeMounts {
|
||||
devices := image.ImexChannelsFromMounts()
|
||||
if len(devices) > 0 {
|
||||
return devices
|
||||
}
|
||||
}
|
||||
devices := image.ImexChannelsFromEnvVar()
|
||||
if len(devices) == 0 {
|
||||
return nil
|
||||
}
|
||||
chans := image.Getenv(envNVImexChannels)
|
||||
return &chans
|
||||
|
||||
if privileged || hookConfig.AcceptEnvvarUnprivileged {
|
||||
return devices
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HookConfig) getDriverCapabilities(cudaImage image.CUDA, legacyImage bool) image.DriverCapabilities {
|
||||
@@ -291,8 +224,8 @@ func (c *HookConfig) getDriverCapabilities(cudaImage image.CUDA, legacyImage boo
|
||||
|
||||
capabilities := supportedDriverCapabilities.Intersection(image.DefaultDriverCapabilities)
|
||||
|
||||
capsEnvSpecified := cudaImage.HasEnvvar(envNVDriverCapabilities)
|
||||
capsEnv := cudaImage.Getenv(envNVDriverCapabilities)
|
||||
capsEnvSpecified := cudaImage.HasEnvvar(image.EnvVarNvidiaDriverCapabilities)
|
||||
capsEnv := cudaImage.Getenv(image.EnvVarNvidiaDriverCapabilities)
|
||||
|
||||
if !capsEnvSpecified && legacyImage {
|
||||
// Environment variable unset with legacy image: set all capabilities.
|
||||
@@ -311,14 +244,12 @@ func (c *HookConfig) getDriverCapabilities(cudaImage image.CUDA, legacyImage boo
|
||||
return capabilities
|
||||
}
|
||||
|
||||
func getNvidiaConfig(hookConfig *HookConfig, image image.CUDA, mounts []Mount, privileged bool) *nvidiaConfig {
|
||||
func getNvidiaConfig(hookConfig *HookConfig, image image.CUDA, privileged bool) *nvidiaConfig {
|
||||
legacyImage := image.IsLegacy()
|
||||
|
||||
var devices string
|
||||
if d := getDevices(hookConfig, image, mounts, privileged); d != nil {
|
||||
devices = *d
|
||||
} else {
|
||||
// 'nil' devices means this is not a GPU container.
|
||||
devices := getDevices(hookConfig, image, privileged)
|
||||
if len(devices) == 0 {
|
||||
// empty devices means this is not a GPU container.
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -338,10 +269,7 @@ func getNvidiaConfig(hookConfig *HookConfig, image image.CUDA, mounts []Mount, p
|
||||
log.Panicln("cannot set MIG_MONITOR_DEVICES in non privileged container")
|
||||
}
|
||||
|
||||
var imexChannels string
|
||||
if c := getImexChannels(image); c != nil {
|
||||
imexChannels = *c
|
||||
}
|
||||
imexChannels := getImexChannels(hookConfig, image, privileged)
|
||||
|
||||
driverCapabilities := hookConfig.getDriverCapabilities(image, legacyImage).String()
|
||||
|
||||
@@ -376,6 +304,7 @@ func getContainerConfig(hook HookConfig) (config containerConfig) {
|
||||
|
||||
image, err := image.New(
|
||||
image.WithEnv(s.Process.Env),
|
||||
image.WithMounts(s.Mounts),
|
||||
image.WithDisableRequire(hook.DisableRequire),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -387,6 +316,6 @@ func getContainerConfig(hook HookConfig) (config containerConfig) {
|
||||
Pid: h.Pid,
|
||||
Rootfs: s.Root.Path,
|
||||
Image: image,
|
||||
Nvidia: getNvidiaConfig(&hook, image, s.Mounts, privileged),
|
||||
Nvidia: getNvidiaConfig(&hook, image, privileged),
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -95,6 +95,9 @@ func doPrestart() {
|
||||
if cli.LoadKmods {
|
||||
args = append(args, "--load-kmods")
|
||||
}
|
||||
if hook.Features.DisableImexChannelCreation.IsEnabled() {
|
||||
args = append(args, "--no-create-imex-channels")
|
||||
}
|
||||
if cli.NoPivot {
|
||||
args = append(args, "--no-pivot")
|
||||
}
|
||||
@@ -117,8 +120,8 @@ func doPrestart() {
|
||||
if cli.NoCgroups {
|
||||
args = append(args, "--no-cgroups")
|
||||
}
|
||||
if len(nvidia.Devices) > 0 {
|
||||
args = append(args, fmt.Sprintf("--device=%s", nvidia.Devices))
|
||||
if devicesString := strings.Join(nvidia.Devices, ","); len(devicesString) > 0 {
|
||||
args = append(args, fmt.Sprintf("--device=%s", devicesString))
|
||||
}
|
||||
if len(nvidia.MigConfigDevices) > 0 {
|
||||
args = append(args, fmt.Sprintf("--mig-config=%s", nvidia.MigConfigDevices))
|
||||
@@ -126,8 +129,8 @@ func doPrestart() {
|
||||
if len(nvidia.MigMonitorDevices) > 0 {
|
||||
args = append(args, fmt.Sprintf("--mig-monitor=%s", nvidia.MigMonitorDevices))
|
||||
}
|
||||
if len(nvidia.ImexChannels) > 0 {
|
||||
args = append(args, fmt.Sprintf("--imex-channel=%s", nvidia.ImexChannels))
|
||||
if imexString := strings.Join(nvidia.ImexChannels, ","); len(imexString) > 0 {
|
||||
args = append(args, fmt.Sprintf("--imex-channel=%s", imexString))
|
||||
}
|
||||
|
||||
for _, cap := range strings.Split(nvidia.DriverCapabilities, ",") {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
@@ -29,6 +28,7 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/crio"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/docker"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/ocihook"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,13 +44,17 @@ const (
|
||||
defaultContainerdConfigFilePath = "/etc/containerd/config.toml"
|
||||
defaultCrioConfigFilePath = "/etc/crio/crio.conf"
|
||||
defaultDockerConfigFilePath = "/etc/docker/daemon.json"
|
||||
|
||||
defaultConfigSource = configSourceFile
|
||||
configSourceCommand = "command"
|
||||
configSourceFile = "file"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
|
||||
// NewCommand constructs an configure command with the specified logger
|
||||
// NewCommand constructs a configure command with the specified logger
|
||||
func NewCommand(logger logger.Interface) *cli.Command {
|
||||
c := command{
|
||||
logger: logger,
|
||||
@@ -64,6 +68,7 @@ type config struct {
|
||||
dryRun bool
|
||||
runtime string
|
||||
configFilePath string
|
||||
configSource string
|
||||
mode string
|
||||
hookFilePath string
|
||||
|
||||
@@ -120,6 +125,12 @@ func (m command) build() *cli.Command {
|
||||
Usage: "the config mode for runtimes that support multiple configuration mechanisms",
|
||||
Destination: &config.mode,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config-source",
|
||||
Usage: "the source to retrieve the container runtime configuration; one of [command, file]\"",
|
||||
Destination: &config.configSource,
|
||||
Value: defaultConfigSource,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "oci-hook-path",
|
||||
Usage: "the path to the OCI runtime hook to create if --config-mode=oci-hook is specified. If no path is specified, the generated hook is output to STDOUT.\n\tNote: The use of OCI hooks is deprecated.",
|
||||
@@ -156,13 +167,6 @@ func (m command) build() *cli.Command {
|
||||
Usage: "Enable CDI in the configured runtime",
|
||||
Destination: &config.cdi.enabled,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "runtime-config-override",
|
||||
Destination: &config.runtimeConfigOverrideJSON,
|
||||
Usage: "specify additional runtime options as a JSON string. The paths are relative to the runtime config.",
|
||||
Value: "{}",
|
||||
EnvVars: []string{"RUNTIME_CONFIG_OVERRIDE"},
|
||||
},
|
||||
}
|
||||
|
||||
return &configure
|
||||
@@ -209,6 +213,18 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
|
||||
config.runtimeConfigOverrideJSON = ""
|
||||
}
|
||||
|
||||
switch config.configSource {
|
||||
case configSourceCommand:
|
||||
if config.runtime == "docker" {
|
||||
m.logger.Warningf("A %v Config Source is not supported for %v; using %v", config.configSource, config.runtime, configSourceFile)
|
||||
config.configSource = configSourceFile
|
||||
}
|
||||
case configSourceFile:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("unrecognized Config Source: %v", config.configSource)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -227,18 +243,25 @@ func (m command) configureWrapper(c *cli.Context, config *config) error {
|
||||
func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
configFilePath := config.resolveConfigFilePath()
|
||||
|
||||
var cfg engine.Interface
|
||||
var err error
|
||||
configSource, err := config.resolveConfigSource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cfg engine.Interface
|
||||
switch config.runtime {
|
||||
case "containerd":
|
||||
cfg, err = containerd.New(
|
||||
containerd.WithLogger(m.logger),
|
||||
containerd.WithPath(configFilePath),
|
||||
containerd.WithConfigSource(configSource),
|
||||
)
|
||||
case "crio":
|
||||
cfg, err = crio.New(
|
||||
crio.WithLogger(m.logger),
|
||||
crio.WithPath(configFilePath),
|
||||
crio.WithConfigSource(configSource),
|
||||
)
|
||||
case "docker":
|
||||
cfg, err = docker.New(
|
||||
@@ -252,16 +275,10 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
return fmt.Errorf("unable to load config for runtime %v: %v", config.runtime, err)
|
||||
}
|
||||
|
||||
runtimeConfigOverride, err := config.runtimeConfigOverride()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse config overrides: %w", err)
|
||||
}
|
||||
|
||||
err = cfg.AddRuntime(
|
||||
config.nvidiaRuntime.name,
|
||||
config.nvidiaRuntime.path,
|
||||
config.nvidiaRuntime.setAsDefault,
|
||||
runtimeConfigOverride,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update config: %v", err)
|
||||
@@ -272,7 +289,7 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
return fmt.Errorf("failed to enable CDI in %s: %w", config.runtime, err)
|
||||
}
|
||||
|
||||
outputPath := config.getOuputConfigPath()
|
||||
outputPath := config.getOutputConfigPath()
|
||||
n, err := cfg.Save(outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to flush config: %v", err)
|
||||
@@ -306,28 +323,37 @@ func (c *config) resolveConfigFilePath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// getOuputConfigPath returns the configured config path or "" if dry-run is enabled
|
||||
func (c *config) getOuputConfigPath() string {
|
||||
// resolveConfigSource returns the default config source or the user provided config source
|
||||
func (c *config) resolveConfigSource() (toml.Loader, error) {
|
||||
switch c.configSource {
|
||||
case configSourceCommand:
|
||||
return c.getCommandConfigSource(), nil
|
||||
case configSourceFile:
|
||||
return toml.FromFile(c.configFilePath), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized config source: %s", c.configSource)
|
||||
}
|
||||
}
|
||||
|
||||
// getConfigSourceCommand returns the default cli command to fetch the current runtime config
|
||||
func (c *config) getCommandConfigSource() toml.Loader {
|
||||
switch c.runtime {
|
||||
case "containerd":
|
||||
return containerd.CommandLineSource("")
|
||||
case "crio":
|
||||
return crio.CommandLineSource("")
|
||||
}
|
||||
return toml.Empty
|
||||
}
|
||||
|
||||
// getOutputConfigPath returns the configured config path or "" if dry-run is enabled
|
||||
func (c *config) getOutputConfigPath() string {
|
||||
if c.dryRun {
|
||||
return ""
|
||||
}
|
||||
return c.resolveConfigFilePath()
|
||||
}
|
||||
|
||||
// runtimeConfigOverride converts the specified runtimeConfigOverride JSON string to a map.
|
||||
func (o *config) runtimeConfigOverride() (map[string]interface{}, error) {
|
||||
if o.runtimeConfigOverrideJSON == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
runtimeOptions := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(o.runtimeConfigOverrideJSON), &runtimeOptions); err != nil {
|
||||
return nil, fmt.Errorf("failed to read %v as JSON: %w", o.runtimeConfigOverrideJSON, err)
|
||||
}
|
||||
|
||||
return runtimeOptions, nil
|
||||
}
|
||||
|
||||
// configureOCIHook creates and configures the OCI hook for the NVIDIA runtime
|
||||
func (m *command) configureOCIHook(c *cli.Context, config *config) error {
|
||||
err := ocihook.CreateHook(config.hookFilePath, config.nvidiaRuntime.hookPath)
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
# Copyright (c) NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package createdevicenodes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
|
||||
type options struct {
|
||||
driverRoot string
|
||||
}
|
||||
|
||||
// NewCommand constructs a command sub-command with the specified logger
|
||||
func NewCommand(logger logger.Interface) *cli.Command {
|
||||
c := command{
|
||||
logger: logger,
|
||||
}
|
||||
return c.build()
|
||||
}
|
||||
|
||||
// build
|
||||
func (m command) build() *cli.Command {
|
||||
opts := options{}
|
||||
|
||||
c := cli.Command{
|
||||
Name: "print-ldcache",
|
||||
Usage: "A utility to print the contents of the ldcache",
|
||||
Before: func(c *cli.Context) error {
|
||||
return m.validateFlags(c, &opts)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
return m.run(c, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "driver-root",
|
||||
Usage: "the path to the driver root. Device nodes will be created at `DRIVER_ROOT`/dev",
|
||||
Value: "/",
|
||||
Destination: &opts.driverRoot,
|
||||
EnvVars: []string{"NVIDIA_DRIVER_ROOT", "DRIVER_ROOT"},
|
||||
},
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (m command) validateFlags(r *cli.Context, opts *options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m command) run(c *cli.Context, opts *options) error {
|
||||
cache, err := ldcache.New(m.logger, opts.driverRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create ldcache: %v", err)
|
||||
}
|
||||
|
||||
lib32, lib64 := cache.List()
|
||||
|
||||
if len(lib32) == 0 {
|
||||
m.logger.Info("No 32-bit libraries found")
|
||||
} else {
|
||||
m.logger.Infof("%d 32-bit libraries found", len(lib32))
|
||||
for _, lib := range lib32 {
|
||||
m.logger.Infof("%v", lib)
|
||||
}
|
||||
}
|
||||
if len(lib64) == 0 {
|
||||
m.logger.Info("No 64-bit libraries found")
|
||||
} else {
|
||||
m.logger.Infof("%d 64-bit libraries found", len(lib64))
|
||||
for _, lib := range lib64 {
|
||||
m.logger.Infof("%v", lib)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
devchar "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/create-dev-char-symlinks"
|
||||
devicenodes "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/create-device-nodes"
|
||||
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/system/print-ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
@@ -47,7 +46,6 @@ func (m command) build() *cli.Command {
|
||||
system.Subcommands = []*cli.Command{
|
||||
devchar.NewCommand(m.logger),
|
||||
devicenodes.NewCommand(m.logger),
|
||||
ldcache.NewCommand(m.logger),
|
||||
}
|
||||
|
||||
return &system
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
|
||||
FROM nvidia/cuda:12.5.0-base-ubuntu20.04
|
||||
FROM nvidia/cuda:12.6.2-base-ubuntu20.04
|
||||
|
||||
ARG ARTIFACTS_ROOT
|
||||
COPY ${ARTIFACTS_ROOT} /artifacts/packages/
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
ARG VERSION="N/A"
|
||||
|
||||
FROM nvidia/cuda:12.5.0-base-ubi8 as build
|
||||
FROM nvidia/cuda:12.6.2-base-ubi8 as build
|
||||
|
||||
RUN yum install -y \
|
||||
wget make git gcc \
|
||||
@@ -48,7 +48,7 @@ COPY . .
|
||||
RUN GOPATH=/artifacts go install -ldflags="-s -w -X 'main.Version=${VERSION}'" ./tools/...
|
||||
|
||||
|
||||
FROM nvidia/cuda:12.5.0-base-ubi8
|
||||
FROM nvidia/cuda:12.6.2-base-ubi8
|
||||
|
||||
ENV NVIDIA_DISABLE_REQUIRE="true"
|
||||
ENV NVIDIA_VISIBLE_DEVICES=void
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
ARG VERSION="N/A"
|
||||
|
||||
FROM nvidia/cuda:12.5.0-base-ubuntu20.04 as build
|
||||
FROM nvidia/cuda:12.6.2-base-ubuntu20.04 as build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget make git gcc \
|
||||
@@ -47,7 +47,7 @@ COPY . .
|
||||
RUN GOPATH=/artifacts go install -ldflags="-s -w -X 'main.Version=${VERSION}'" ./tools/...
|
||||
|
||||
|
||||
FROM nvcr.io/nvidia/cuda:12.5.0-base-ubuntu20.04
|
||||
FROM nvcr.io/nvidia/cuda:12.6.2-base-ubuntu20.04
|
||||
|
||||
# Remove the CUDA repository configurations to avoid issues with rotated GPG keys
|
||||
RUN rm -f /etc/apt/sources.list.d/cuda.list
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
# This Dockerfile is also used to define the golang version used in this project
|
||||
# This allows dependabot to manage this version in addition to other images.
|
||||
FROM golang:1.22.5
|
||||
FROM golang:1.23.2
|
||||
|
||||
WORKDIR /work
|
||||
COPY * .
|
||||
|
||||
@@ -34,3 +34,10 @@ DOCKERFILE_CONTEXT = deployments/devel
|
||||
--tag $(BUILDIMAGE) \
|
||||
-f $(DOCKERFILE_DEVEL) \
|
||||
$(DOCKERFILE_CONTEXT)
|
||||
|
||||
modules:
|
||||
go mod tidy
|
||||
go mod verify
|
||||
|
||||
check-modules: modules
|
||||
git diff --quiet HEAD -- go.mod go.sum
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
module github.com/NVIDIA/k8s-device-plugin/deployments/devel
|
||||
|
||||
go 1.22
|
||||
go 1.23
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/golangci/golangci-lint v1.59.1
|
||||
github.com/matryer/moq v0.3.4
|
||||
github.com/golangci/golangci-lint v1.61.0
|
||||
github.com/matryer/moq v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.1 // indirect
|
||||
github.com/4meepo/tagalign v1.3.4 // indirect
|
||||
github.com/Abirdcfly/dupword v0.0.14 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.1 // indirect
|
||||
github.com/Antonboom/errname v0.1.13 // indirect
|
||||
github.com/Antonboom/nilnil v0.1.9 // indirect
|
||||
github.com/Antonboom/testifylint v1.3.1 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/Crocmagnon/fatcontext v0.2.2 // indirect
|
||||
github.com/Antonboom/testifylint v1.4.3 // indirect
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||
github.com/Crocmagnon/fatcontext v0.5.2 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
|
||||
github.com/alecthomas/go-check-sumtype v0.1.4 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.4 // indirect
|
||||
@@ -30,7 +32,7 @@ require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.1 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.2.1 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.4.1 // indirect
|
||||
github.com/breml/bidichk v0.2.7 // indirect
|
||||
github.com/breml/errchkjson v0.3.6 // indirect
|
||||
github.com/butuzov/ireturn v0.3.0 // indirect
|
||||
@@ -40,9 +42,9 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/charithe/durationcheck v0.0.10 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/ckaznocha/intrange v0.1.2 // indirect
|
||||
github.com/ckaznocha/intrange v0.2.0 // indirect
|
||||
github.com/curioswitch/go-reassign v0.2.0 // indirect
|
||||
github.com/daixiang0/gci v0.13.4 // indirect
|
||||
github.com/daixiang0/gci v0.13.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
||||
github.com/ettle/strcase v0.2.0 // indirect
|
||||
@@ -60,13 +62,13 @@ require (
|
||||
github.com/go-toolsmith/astp v1.1.0 // indirect
|
||||
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
||||
github.com/go-toolsmith/typep v1.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect
|
||||
github.com/golangci/misspell v0.6.0 // indirect
|
||||
github.com/golangci/modinfo v0.3.4 // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.1 // indirect
|
||||
@@ -85,7 +87,7 @@ require (
|
||||
github.com/jgautheron/goconst v1.7.1 // indirect
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
|
||||
github.com/jjti/go-spancheck v0.6.1 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.2 // indirect
|
||||
github.com/julz/importas v0.1.0 // indirect
|
||||
github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect
|
||||
github.com/kisielk/errcheck v1.7.0 // indirect
|
||||
@@ -107,44 +109,44 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgechev/revive v1.3.7 // indirect
|
||||
github.com/mgechev/revive v1.3.9 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moricho/tparallel v0.3.1 // indirect
|
||||
github.com/moricho/tparallel v0.3.2 // indirect
|
||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.5.2 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.6.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.4.2 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
|
||||
github.com/quasilyte/gogrep v0.5.0 // indirect
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||
github.com/ryancurrah/gomodguard v1.3.2 // indirect
|
||||
github.com/ryancurrah/gomodguard v1.3.5 // indirect
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.26.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.21.2 // indirect
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
github.com/sivchari/tenv v1.7.1 // indirect
|
||||
github.com/sivchari/tenv v1.10.0 // indirect
|
||||
github.com/sonatard/noctx v0.0.2 // indirect
|
||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
@@ -153,39 +155,38 @@ require (
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
|
||||
github.com/tdakkota/asciicheck v0.2.0 // indirect
|
||||
github.com/tetafro/godot v1.4.16 // indirect
|
||||
github.com/tetafro/godot v1.4.17 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
|
||||
github.com/timonwong/loggercheck v0.9.4 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0 // indirect
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
||||
github.com/ultraware/funlen v0.1.0 // indirect
|
||||
github.com/ultraware/whitespace v0.1.1 // indirect
|
||||
github.com/uudashr/gocognit v1.1.2 // indirect
|
||||
github.com/uudashr/gocognit v1.1.3 // indirect
|
||||
github.com/xen0n/gosmopolitan v1.2.2 // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
github.com/yeya24/promlinter v0.3.0 // indirect
|
||||
github.com/ykadowak/zerologlint v0.1.5 // indirect
|
||||
gitlab.com/bosi/decorder v0.4.2 // indirect
|
||||
go-simpler.org/musttag v0.12.2 // indirect
|
||||
go-simpler.org/sloglint v0.7.1 // indirect
|
||||
go-simpler.org/sloglint v0.7.2 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
honnef.co/go/tools v0.4.7 // indirect
|
||||
mvdan.cc/gofumpt v0.6.0 // indirect
|
||||
honnef.co/go/tools v0.5.1 // indirect
|
||||
mvdan.cc/gofumpt v0.7.0 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect
|
||||
)
|
||||
|
||||
@@ -37,26 +37,26 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8=
|
||||
github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0=
|
||||
github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8=
|
||||
github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI=
|
||||
github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY=
|
||||
github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM=
|
||||
github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM=
|
||||
github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns=
|
||||
github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ=
|
||||
github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ=
|
||||
github.com/Antonboom/testifylint v1.3.1 h1:Uam4q1Q+2b6H7gvk9RQFw6jyVDdpzIirFOOrbs14eG4=
|
||||
github.com/Antonboom/testifylint v1.3.1/go.mod h1:NV0hTlteCkViPW9mSR4wEMfwp+Hs1T3dY60bkvSfhpM=
|
||||
github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck=
|
||||
github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk=
|
||||
github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0=
|
||||
github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA=
|
||||
github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA=
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ=
|
||||
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
|
||||
@@ -90,8 +90,8 @@ github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJ
|
||||
github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM=
|
||||
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
|
||||
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
|
||||
github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFiM=
|
||||
github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo=
|
||||
github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw=
|
||||
github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo=
|
||||
github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY=
|
||||
github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ=
|
||||
github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA=
|
||||
@@ -115,15 +115,15 @@ github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+U
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI=
|
||||
github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE=
|
||||
github.com/ckaznocha/intrange v0.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs=
|
||||
github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
|
||||
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
|
||||
github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw=
|
||||
github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
|
||||
github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c=
|
||||
github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -141,8 +141,8 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA=
|
||||
github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
@@ -160,8 +160,10 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
@@ -184,14 +186,14 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
|
||||
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
|
||||
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
|
||||
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w=
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -224,10 +226,10 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g=
|
||||
github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM=
|
||||
github.com/golangci/golangci-lint v1.59.1 h1:CRRLu1JbhK5avLABFJ/OHVSQ0Ie5c4ulsOId1h3TTks=
|
||||
github.com/golangci/golangci-lint v1.59.1/go.mod h1:jX5Oif4C7P0j9++YB2MMJmoNrb01NJ8ITqKWNLewThg=
|
||||
github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME=
|
||||
github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE=
|
||||
github.com/golangci/golangci-lint v1.61.0 h1:VvbOLaRVWmyxCnUIMTbf1kDsaJbTzH20FAMXTAlQGu8=
|
||||
github.com/golangci/golangci-lint v1.61.0/go.mod h1:e4lztIrJJgLPhWvFPDkhiMwEFRrWlmFbrZea3FsJyN8=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA=
|
||||
@@ -264,8 +266,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
@@ -301,8 +303,8 @@ github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjz
|
||||
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jjti/go-spancheck v0.6.1 h1:ZK/wE5Kyi1VX3PJpUO2oEgeoI4FWOUm7Shb2Gbv5obI=
|
||||
github.com/jjti/go-spancheck v0.6.1/go.mod h1:vF1QkOO159prdo6mHRxak2CpzDpHAfKiPUDP/NeRnX8=
|
||||
github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk=
|
||||
github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -359,8 +361,8 @@ github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/matryer/moq v0.3.4 h1:czCFIos9rI2tyOehN9ktc/6bQ76N9J4xQ2n3dk063ac=
|
||||
github.com/matryer/moq v0.3.4/go.mod h1:wqm9QObyoMuUtH81zFfs3EK6mXEcByy+TjvSROOXJ2U=
|
||||
github.com/matryer/moq v0.5.0 h1:h2PJUYjZSiyEahzVogDRmrgL9Bsx9xYAl8l+LPfmwL8=
|
||||
github.com/matryer/moq v0.5.0/go.mod h1:39GTnrD0mVWHPvWdYj5ki/lxfhLQEtHcLh+tWoYF/iE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
@@ -370,8 +372,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE=
|
||||
github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA=
|
||||
github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A=
|
||||
github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -381,8 +383,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA=
|
||||
github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI=
|
||||
github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI=
|
||||
github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
|
||||
@@ -395,10 +397,10 @@ github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbn
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=
|
||||
github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||
@@ -408,16 +410,16 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polyfloyd/go-errorlint v1.5.2 h1:SJhVik3Umsjh7mte1vE0fVZ5T1gznasQG3PV7U5xFdA=
|
||||
github.com/polyfloyd/go-errorlint v1.5.2/go.mod h1:sH1QC1pxxi0fFecsVIzBmxtrgd9IF/SkJpA6wqyKAJs=
|
||||
github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY=
|
||||
github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@@ -442,8 +444,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs=
|
||||
github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
|
||||
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo=
|
||||
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo=
|
||||
@@ -456,8 +458,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryancurrah/gomodguard v1.3.2 h1:CuG27ulzEB1Gu5Dk5gP8PFxSOZ3ptSdP5iI/3IXxM18=
|
||||
github.com/ryancurrah/gomodguard v1.3.2/go.mod h1:LqdemiFomEjcxOqirbQCb3JFvSxH2JUYMerTFd3sF2o=
|
||||
github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU=
|
||||
github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE=
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc=
|
||||
@@ -466,10 +468,10 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6Ng
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.26.0 h1:LONR2hNVKxRmzIrZR0PhSF3mhCAzvnr+DcUiHgREfXE=
|
||||
github.com/sashamelentyev/usestdlibvars v1.26.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
|
||||
github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQwv8AyxzuwsVohljJgp4L0ZqiCgtACsPsc=
|
||||
github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs=
|
||||
github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI=
|
||||
github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
|
||||
github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M=
|
||||
github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
@@ -481,8 +483,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
|
||||
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
|
||||
github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak=
|
||||
github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
|
||||
github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0=
|
||||
github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY=
|
||||
github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00=
|
||||
github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
|
||||
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
|
||||
@@ -491,8 +493,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -521,30 +523,28 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8=
|
||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
|
||||
github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM=
|
||||
github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
|
||||
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
|
||||
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
|
||||
github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0=
|
||||
github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
|
||||
github.com/tetafro/godot v1.4.17 h1:pGzu+Ye7ZUEFx7LHU0dAKmCOXWsPjl7qA6iMGndsjPs=
|
||||
github.com/tetafro/godot v1.4.17/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
|
||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M=
|
||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ=
|
||||
github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4=
|
||||
github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3bRDFASUs=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
|
||||
github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI=
|
||||
github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4=
|
||||
github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ=
|
||||
github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
|
||||
github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI=
|
||||
github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k=
|
||||
github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
|
||||
github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
|
||||
github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
|
||||
github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
|
||||
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
|
||||
@@ -566,8 +566,8 @@ go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
|
||||
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
|
||||
go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs=
|
||||
go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM=
|
||||
go-simpler.org/sloglint v0.7.1 h1:qlGLiqHbN5islOxjeLXoPtUdZXb669RW+BDQ+xOSNoU=
|
||||
go-simpler.org/sloglint v0.7.1/go.mod h1:OlaVDRh/FKKd4X4sIMbsz8st97vomydceL146Fthh/c=
|
||||
go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY=
|
||||
go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -601,8 +601,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8=
|
||||
@@ -635,8 +635,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -675,8 +675,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -696,8 +696,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -743,7 +743,6 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -752,8 +751,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -770,8 +769,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -830,14 +829,13 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -917,13 +915,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
@@ -944,10 +943,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
|
||||
honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
|
||||
mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo=
|
||||
mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA=
|
||||
honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
|
||||
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
|
||||
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
|
||||
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U=
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
9
go.mod
9
go.mod
@@ -3,16 +3,17 @@ module github.com/NVIDIA/nvidia-container-toolkit
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/NVIDIA/go-nvlib v0.6.0
|
||||
github.com/NVIDIA/go-nvlib v0.6.1
|
||||
github.com/NVIDIA/go-nvml v0.12.4-0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/moby/sys/symlink v0.3.0
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
golang.org/x/mod v0.19.0
|
||||
golang.org/x/sys v0.22.0
|
||||
github.com/urfave/cli/v2 v2.27.4
|
||||
golang.org/x/mod v0.20.0
|
||||
golang.org/x/sys v0.26.0
|
||||
tags.cncf.io/container-device-interface v0.8.0
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0
|
||||
)
|
||||
|
||||
18
go.sum
18
go.sum
@@ -1,5 +1,5 @@
|
||||
github.com/NVIDIA/go-nvlib v0.6.0 h1:zAMBzCYT9xeyRQo0tb7HJbStkzajD6e5joyaQqJ2OGU=
|
||||
github.com/NVIDIA/go-nvlib v0.6.0/go.mod h1:9UrsLGx/q1OrENygXjOuM5Ey5KCtiZhbvBlbUIxtGWY=
|
||||
github.com/NVIDIA/go-nvlib v0.6.1 h1:0/5FvaKvDJoJeJ+LFlh+NDQMxMlVw9wOXrOVrGXttfE=
|
||||
github.com/NVIDIA/go-nvlib v0.6.1/go.mod h1:9UrsLGx/q1OrENygXjOuM5Ey5KCtiZhbvBlbUIxtGWY=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-0 h1:4tkbB3pT1O77JGr0gQ6uD8FrsUPqP1A/EOEm2wI1TUg=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-0/go.mod h1:8Llmj+1Rr+9VGGwZuRer5N/aCjxGuR5nPb/9ebBiIEQ=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
@@ -28,6 +28,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
|
||||
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/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
@@ -58,8 +60,8 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
|
||||
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
|
||||
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
|
||||
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -69,13 +71,13 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -30,8 +30,7 @@ REPOSITORY=NVIDIA/nvidia-container-toolkit
|
||||
|
||||
echo "Creating draft release"
|
||||
./hack/generate-changelog.sh --version ${VERSION} | \
|
||||
grep -v "### Version v" | \
|
||||
echo gh release create ${VERSION} --notes-file "-" \
|
||||
gh release create ${VERSION} --notes-file "-" \
|
||||
--draft \
|
||||
--title "${VERSION}" \
|
||||
-R "${REPOSITORY}" \
|
||||
@@ -43,5 +42,7 @@ echo "Uploading release artifacts for ${VERSION}"
|
||||
PACKAGE_ROOT=release-${VERSION}-${REPO}
|
||||
|
||||
gh release upload ${VERSION} \
|
||||
${PACKAGE_ROOT}/nvidia-container-toolkit-${VERSION}.*.tar.gz \
|
||||
${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_*.tar.gz \
|
||||
${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_checksums.txt \
|
||||
--clobber \
|
||||
-R ${REPOSITORY}
|
||||
|
||||
@@ -33,7 +33,7 @@ EOF
|
||||
LIB_VERSION=$(awk -F= '/^LIB_VERSION/ { print $2 }' versions.mk | tr -d '[:space:]')
|
||||
LIB_TAG=$(awk -F= '/^LIB_TAG/ { print $2 }' versions.mk | tr -d '[:space:]')
|
||||
|
||||
VERSION="v${LIB_VERSION}${LIB_TAG+-${LIB_TAG}}"
|
||||
VERSION="v${LIB_VERSION}${LIB_TAG:+-${LIB_TAG}}"
|
||||
>&2 echo "VERSION=$VERSION"
|
||||
|
||||
REFERENCE=
|
||||
@@ -66,6 +66,11 @@ remote=$( git remote -v | grep -E "NVIDIA/nvidia-container-toolkit(\.git)?\s" |
|
||||
>&2 echo "Detected remote as '${remote}'"
|
||||
git fetch ${remote} --tags
|
||||
|
||||
SHA=$(git rev-parse ${VERSION})
|
||||
if [[ $? -ne 0 ]]; then
|
||||
SHA="HEAD"
|
||||
fi
|
||||
|
||||
# if REFERENCE is not set, get the latest tag
|
||||
if [ -z "$REFERENCE" ]; then
|
||||
most_recent_tag=$(git tag --sort=-creatordate | head -1)
|
||||
@@ -79,9 +84,30 @@ fi
|
||||
>&2 echo "Using ${REFERENCE} as previous version"
|
||||
|
||||
# Print the changelog
|
||||
echo "# Changelog"
|
||||
echo "## What's Changed"
|
||||
echo ""
|
||||
echo "## $VERSION"
|
||||
if [[ ${VERSION} != v*-rc.* ]]; then
|
||||
echo "- Promote $REFERENCE to $VERSION"
|
||||
fi
|
||||
|
||||
# Iterate over the commit messages and ignore the ones that start with "Merge" or "Bump"
|
||||
git log --pretty=format:"%s" $REFERENCE..@ | grep -Ev "(^Merge )|(^Bump)|(no-rel-?note)|(^---)" | sed 's/^\(.*\)/- \1/g'
|
||||
git log --pretty=format:"%s" $REFERENCE..$SHA -- ':!deployments/container' ':!tools' | grep -Ev "(^Merge )|(^Bump)|(no-rel-?note)|(^---)" | sed 's/^\(.*\)/- \1/g'
|
||||
|
||||
echo ""
|
||||
echo "### Changes in the Toolkit Container"
|
||||
echo ""
|
||||
git log --pretty=format:"%s" $REFERENCE..$SHA -- deployments/container tools | grep -Ev "(^Merge )|(no-rel-?note)|(^---)" | sed 's/^\(.*\)/- \1/g'
|
||||
|
||||
LIB_NVIDIA_CONTAINER_REFERENCE=$( git ls-tree $REFERENCE third_party/libnvidia-container --object-only )
|
||||
LIB_NVIDIA_CONTAINER_VERSION=$( git ls-tree $SHA third_party/libnvidia-container --object-only )
|
||||
|
||||
echo ""
|
||||
if [[ $(git -C third_party/libnvidia-container log --pretty=format:"%s" $LIB_NVIDIA_CONTAINER_REFERENCE..$LIB_NVIDIA_CONTAINER_VERSION | grep -Ev "(^Merge )|(^Bump)|(no-rel-?note)|(^---)" | sed 's/^\(.*\)/- \1/g' | wc -l) -gt 0 ]]; then
|
||||
echo "### Changes in libnvidia-container"
|
||||
echo ""
|
||||
git -C third_party/libnvidia-container log --pretty=format:"%s" $LIB_NVIDIA_CONTAINER_REFERENCE..$LIB_NVIDIA_CONTAINER_VERSION | grep -Ev "(^Merge )|(^Bump)|(no-rel-?note)|(^---)" | sed 's/^\(.*\)/- \1/g'
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "**Full Changelog**: https://github.com/NVIDIA/nvidia-container-toolkit/compare/${REFERENCE}...${VERSION}"
|
||||
echo ""
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../scripts && pwd )"
|
||||
SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../hack && pwd )"
|
||||
|
||||
DOCKERFILE_ROOT=${SCRIPTS_DIR}/../deployments/devel
|
||||
|
||||
|
||||
@@ -47,7 +47,42 @@ PACKAGE_ROOT=release-${VERSION}-${REPO}
|
||||
PACKAGE_VERSION=${VERSION/-/\~}
|
||||
PACKAGE_VERSION=${PACKAGE_VERSION#v}
|
||||
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit-${VERSION}.deb.amd64.tar.gz ${PACKAGE_ROOT}/packages/ubuntu18.04/amd64/*_${PACKAGE_VERSION}-1_amd64.deb
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit-${VERSION}.deb.arm64.tar.gz ${PACKAGE_ROOT}/packages/ubuntu18.04/arm64/*_${PACKAGE_VERSION}-1_arm64.deb
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit-${VERSION}.rpm.aarch64.tar.gz ${PACKAGE_ROOT}/packages/centos7/aarch64/*-${PACKAGE_VERSION}-1.aarch64.rpm
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit-${VERSION}.rpm.x86_64.tar.gz ${PACKAGE_ROOT}/packages/centos7/x86_64/*-${PACKAGE_VERSION}-1.x86_64.rpm
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_deb_amd64.tar.gz ${PACKAGE_ROOT}/packages/ubuntu18.04/amd64/*_${PACKAGE_VERSION}-1_amd64.deb
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_deb_arm64.tar.gz ${PACKAGE_ROOT}/packages/ubuntu18.04/arm64/*_${PACKAGE_VERSION}-1_arm64.deb
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_rpm_aarch64.tar.gz ${PACKAGE_ROOT}/packages/centos7/aarch64/*-${PACKAGE_VERSION}-1.aarch64.rpm
|
||||
tar -czvf ${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_rpm_x86_64.tar.gz ${PACKAGE_ROOT}/packages/centos7/x86_64/*-${PACKAGE_VERSION}-1.x86_64.rpm
|
||||
|
||||
is_command() (
|
||||
command -v "$1" >/dev/null
|
||||
)
|
||||
|
||||
hash_sha256() (
|
||||
TARGET=${1:-/dev/stdin}
|
||||
if is_command gsha256sum; then
|
||||
hash=$(gsha256sum "$TARGET") || return 1
|
||||
echo "$hash" | cut -d ' ' -f 1
|
||||
elif is_command sha256sum; then
|
||||
hash=$(sha256sum "$TARGET") || return 1
|
||||
echo "$hash" | cut -d ' ' -f 1
|
||||
elif is_command shasum; then
|
||||
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||
echo "$hash" | cut -d ' ' -f 1
|
||||
elif is_command openssl; then
|
||||
hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
|
||||
echo "$hash" | cut -d ' ' -f a
|
||||
else
|
||||
log_err "hash_sha256 unable to find command to compute sha-256 hash"
|
||||
return 1
|
||||
fi
|
||||
)
|
||||
|
||||
files=$( ls ${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_*.tar.gz )
|
||||
|
||||
CHECKSUM_FILE=${PACKAGE_ROOT}/nvidia-container-toolkit_${VERSION#v}_checksums.txt
|
||||
rm -f ${CHECKSUM_FILE}
|
||||
|
||||
set -e
|
||||
for f in ${files}; do
|
||||
hash_f=$(hash_sha256 $f)
|
||||
echo "${hash_f} $f" >> $CHECKSUM_FILE
|
||||
done
|
||||
|
||||
@@ -138,18 +138,19 @@ else
|
||||
fi
|
||||
|
||||
# TODO: We need to ensure that this tooling also works on `release-*` branches.
|
||||
if [[ "$FORCE_BRANCH" != "yes" && "$(git rev-parse --abbrev-ref HEAD)" != "main" ]]; then
|
||||
echo "Release scripts should be run on 'main'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git fetch
|
||||
git diff --quiet FETCH_HEAD
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Local changes detected:"
|
||||
git diff FETCH_HEAD | cat
|
||||
echo "Exiting"
|
||||
exit 1
|
||||
if [[ "$FORCE" != "yes" ]]; then
|
||||
if [[ "$(git rev-parse --abbrev-ref HEAD)" != "main" ]]; then
|
||||
echo "Release scripts should be run on 'main'"
|
||||
exit 1
|
||||
fi
|
||||
git fetch
|
||||
git diff --quiet FETCH_HEAD
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Local changes detected:"
|
||||
git diff FETCH_HEAD | cat
|
||||
echo "Exiting"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create a release issue.
|
||||
@@ -160,16 +161,21 @@ cat RELEASE.md | sed "s/{{ .VERSION }}/$release/g" | \
|
||||
--title "Release nvidia-container-toolkit $release" \
|
||||
--label release
|
||||
|
||||
|
||||
echo "Creating a version bump branch: bump-release-${release}"
|
||||
git checkout -f -b bump-release-${release}
|
||||
|
||||
# Patch versions.mk
|
||||
LIB_VERSION=${${release%-*}#v}
|
||||
LIB_TAG=${release#*-}
|
||||
LIB_VERSION=${release%-*}
|
||||
LIB_VERSION=${LIB_VERSION#v}
|
||||
if [[ ${release} == v*-rc.* ]]; then
|
||||
LIB_TAG_STRING=" ${release#*-}"
|
||||
else
|
||||
LIB_TAG_STRING=
|
||||
fi
|
||||
|
||||
echo Patching versions.mk to refer to $release
|
||||
$SED -i "s/^LIB_VERSION.*$/LIB_VERSION := $LIB_VERSION/" versions.mk
|
||||
$SED -i "s/^LIB_TAG.*$/LIB_TAG := $LIB_TAG/" versions.mk
|
||||
$SED -i "s/^LIB_TAG.*$/LIB_TAG :=$LIB_TAG_STRING/" versions.mk
|
||||
|
||||
git add versions.mk
|
||||
git commit -s -m "Bump version for $release release"
|
||||
|
||||
@@ -16,70 +16,22 @@
|
||||
|
||||
package config
|
||||
|
||||
type featureName string
|
||||
|
||||
const (
|
||||
FeatureGDS = featureName("gds")
|
||||
FeatureMOFED = featureName("mofed")
|
||||
FeatureNVSWITCH = featureName("nvswitch")
|
||||
FeatureGDRCopy = featureName("gdrcopy")
|
||||
)
|
||||
|
||||
// features specifies a set of named features.
|
||||
type features struct {
|
||||
GDS *feature `toml:"gds,omitempty"`
|
||||
MOFED *feature `toml:"mofed,omitempty"`
|
||||
NVSWITCH *feature `toml:"nvswitch,omitempty"`
|
||||
GDRCopy *feature `toml:"gdrcopy,omitempty"`
|
||||
// DisableImexChannelCreation ensures that the implicit creation of
|
||||
// requested IMEX channels is skipped when invoking the nvidia-container-cli.
|
||||
DisableImexChannelCreation *feature `toml:"disable-imex-channel-creation,omitempty"`
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
type feature bool
|
||||
|
||||
// IsEnabled checks whether a specified named feature is enabled.
|
||||
// An optional list of environments to check for feature-specific environment
|
||||
// variables can also be supplied.
|
||||
func (fs features) IsEnabled(n featureName, in ...getenver) bool {
|
||||
featureEnvvars := map[featureName]string{
|
||||
FeatureGDS: "NVIDIA_GDS",
|
||||
FeatureMOFED: "NVIDIA_MOFED",
|
||||
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
|
||||
FeatureGDRCopy: "NVIDIA_GDRCOPY",
|
||||
}
|
||||
|
||||
envvar := featureEnvvars[n]
|
||||
switch n {
|
||||
case FeatureGDS:
|
||||
return fs.GDS.isEnabled(envvar, in...)
|
||||
case FeatureMOFED:
|
||||
return fs.MOFED.isEnabled(envvar, in...)
|
||||
case FeatureNVSWITCH:
|
||||
return fs.NVSWITCH.isEnabled(envvar, in...)
|
||||
case FeatureGDRCopy:
|
||||
return fs.GDRCopy.isEnabled(envvar, in...)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isEnabled checks whether a feature is enabled.
|
||||
// If the enabled value is explicitly set, this is returned, otherwise the
|
||||
// associated envvar is checked in the specified getenver for the string "enabled"
|
||||
// A CUDA container / image can be passed here.
|
||||
func (f *feature) isEnabled(envvar string, ins ...getenver) bool {
|
||||
// IsEnabled checks whether a feature is explicitly enabled.
|
||||
//
|
||||
//nolint:unused
|
||||
func (f *feature) IsEnabled() bool {
|
||||
if f != nil {
|
||||
return bool(*f)
|
||||
}
|
||||
if envvar == "" {
|
||||
return false
|
||||
}
|
||||
for _, in := range ins {
|
||||
if in.Getenv(envvar) == "enabled" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type getenver interface {
|
||||
Getenv(string) string
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func New(opt ...Option) (CUDA, error) {
|
||||
// build creates a CUDA image from the builder.
|
||||
func (b builder) build() (CUDA, error) {
|
||||
if b.disableRequire {
|
||||
b.env[envNVDisableRequire] = "true"
|
||||
b.env[EnvVarNvidiaDisableRequire] = "true"
|
||||
}
|
||||
|
||||
c := CUDA{
|
||||
|
||||
@@ -28,12 +28,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
envCUDAVersion = "CUDA_VERSION"
|
||||
envNVRequirePrefix = "NVIDIA_REQUIRE_"
|
||||
envNVRequireCUDA = envNVRequirePrefix + "CUDA"
|
||||
envNVRequireJetpack = envNVRequirePrefix + "JETPACK"
|
||||
envNVDisableRequire = "NVIDIA_DISABLE_REQUIRE"
|
||||
envNVDriverCapabilities = "NVIDIA_DRIVER_CAPABILITIES"
|
||||
DeviceListAsVolumeMountsRoot = "/var/run/nvidia-container-devices"
|
||||
|
||||
volumeMountDevicePrefixCDI = "cdi/"
|
||||
volumeMountDevicePrefixImex = "imex/"
|
||||
)
|
||||
|
||||
// CUDA represents a CUDA image that can be used for GPU computing. This wraps
|
||||
@@ -80,8 +78,8 @@ func (i CUDA) HasEnvvar(key string) bool {
|
||||
// image is considered legacy if it has a CUDA_VERSION environment variable defined
|
||||
// and no NVIDIA_REQUIRE_CUDA environment variable defined.
|
||||
func (i CUDA) IsLegacy() bool {
|
||||
legacyCudaVersion := i.env[envCUDAVersion]
|
||||
cudaRequire := i.env[envNVRequireCUDA]
|
||||
legacyCudaVersion := i.env[EnvVarCudaVersion]
|
||||
cudaRequire := i.env[EnvVarNvidiaRequireCuda]
|
||||
return len(legacyCudaVersion) > 0 && len(cudaRequire) == 0
|
||||
}
|
||||
|
||||
@@ -95,7 +93,7 @@ func (i CUDA) GetRequirements() ([]string, error) {
|
||||
// All variables with the "NVIDIA_REQUIRE_" prefix are passed to nvidia-container-cli
|
||||
var requirements []string
|
||||
for name, value := range i.env {
|
||||
if strings.HasPrefix(name, envNVRequirePrefix) && !strings.HasPrefix(name, envNVRequireJetpack) {
|
||||
if strings.HasPrefix(name, NvidiaRequirePrefix) && !strings.HasPrefix(name, EnvVarNvidiaRequireJetpack) {
|
||||
requirements = append(requirements, value)
|
||||
}
|
||||
}
|
||||
@@ -113,7 +111,7 @@ func (i CUDA) GetRequirements() ([]string, error) {
|
||||
// HasDisableRequire checks for the value of the NVIDIA_DISABLE_REQUIRE. If set
|
||||
// to a valid (true) boolean value this can be used to disable the requirement checks
|
||||
func (i CUDA) HasDisableRequire() bool {
|
||||
if disable, exists := i.env[envNVDisableRequire]; exists {
|
||||
if disable, exists := i.env[EnvVarNvidiaDisableRequire]; exists {
|
||||
// i.logger.Debugf("NVIDIA_DISABLE_REQUIRE=%v; skipping requirement checks", disable)
|
||||
d, _ := strconv.ParseBool(disable)
|
||||
return d
|
||||
@@ -157,7 +155,7 @@ func (i CUDA) DevicesFromEnvvars(envVars ...string) VisibleDevices {
|
||||
|
||||
// GetDriverCapabilities returns the requested driver capabilities.
|
||||
func (i CUDA) GetDriverCapabilities() DriverCapabilities {
|
||||
env := i.env[envNVDriverCapabilities]
|
||||
env := i.env[EnvVarNvidiaDriverCapabilities]
|
||||
|
||||
capabilities := make(DriverCapabilities)
|
||||
for _, c := range strings.Split(env, ",") {
|
||||
@@ -168,7 +166,7 @@ func (i CUDA) GetDriverCapabilities() DriverCapabilities {
|
||||
}
|
||||
|
||||
func (i CUDA) legacyVersion() (string, error) {
|
||||
cudaVersion := i.env[envCUDAVersion]
|
||||
cudaVersion := i.env[EnvVarCudaVersion]
|
||||
majorMinor, err := parseMajorMinorVersion(cudaVersion)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid CUDA version %v: %v", cudaVersion, err)
|
||||
@@ -202,7 +200,7 @@ func parseMajorMinorVersion(version string) (string, error) {
|
||||
// OnlyFullyQualifiedCDIDevices returns true if all devices requested in the image are requested as CDI devices/
|
||||
func (i CUDA) OnlyFullyQualifiedCDIDevices() bool {
|
||||
var hasCDIdevice bool
|
||||
for _, device := range i.DevicesFromEnvvars("NVIDIA_VISIBLE_DEVICES").List() {
|
||||
for _, device := range i.VisibleDevicesFromEnvVar() {
|
||||
if !parser.IsQualifiedName(device) {
|
||||
return false
|
||||
}
|
||||
@@ -218,14 +216,31 @@ func (i CUDA) OnlyFullyQualifiedCDIDevices() bool {
|
||||
return hasCDIdevice
|
||||
}
|
||||
|
||||
const (
|
||||
deviceListAsVolumeMountsRoot = "/var/run/nvidia-container-devices"
|
||||
)
|
||||
// VisibleDevicesFromEnvVar returns the set of visible devices requested through
|
||||
// the NVIDIA_VISIBLE_DEVICES environment variable.
|
||||
func (i CUDA) VisibleDevicesFromEnvVar() []string {
|
||||
return i.DevicesFromEnvvars(EnvVarNvidiaVisibleDevices).List()
|
||||
}
|
||||
|
||||
// VisibleDevicesFromMounts returns the set of visible devices requested as mounts.
|
||||
func (i CUDA) VisibleDevicesFromMounts() []string {
|
||||
var devices []string
|
||||
for _, device := range i.DevicesFromMounts() {
|
||||
switch {
|
||||
case strings.HasPrefix(device, volumeMountDevicePrefixCDI):
|
||||
continue
|
||||
case strings.HasPrefix(device, volumeMountDevicePrefixImex):
|
||||
continue
|
||||
}
|
||||
devices = append(devices, device)
|
||||
}
|
||||
return devices
|
||||
}
|
||||
|
||||
// DevicesFromMounts returns a list of device specified as mounts.
|
||||
// TODO: This should be merged with getDevicesFromMounts used in the NVIDIA Container Runtime
|
||||
func (i CUDA) DevicesFromMounts() []string {
|
||||
root := filepath.Clean(deviceListAsVolumeMountsRoot)
|
||||
root := filepath.Clean(DeviceListAsVolumeMountsRoot)
|
||||
seen := make(map[string]bool)
|
||||
var devices []string
|
||||
for _, m := range i.mounts {
|
||||
@@ -260,10 +275,10 @@ func (i CUDA) DevicesFromMounts() []string {
|
||||
func (i CUDA) CDIDevicesFromMounts() []string {
|
||||
var devices []string
|
||||
for _, mountDevice := range i.DevicesFromMounts() {
|
||||
if !strings.HasPrefix(mountDevice, "cdi/") {
|
||||
if !strings.HasPrefix(mountDevice, volumeMountDevicePrefixCDI) {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(strings.TrimPrefix(mountDevice, "cdi/"), "/", 3)
|
||||
parts := strings.SplitN(strings.TrimPrefix(mountDevice, volumeMountDevicePrefixCDI), "/", 3)
|
||||
if len(parts) != 3 {
|
||||
continue
|
||||
}
|
||||
@@ -274,3 +289,20 @@ func (i CUDA) CDIDevicesFromMounts() []string {
|
||||
}
|
||||
return devices
|
||||
}
|
||||
|
||||
// ImexChannelsFromEnvVar returns the list of IMEX channels requested for the image.
|
||||
func (i CUDA) ImexChannelsFromEnvVar() []string {
|
||||
return i.DevicesFromEnvvars(EnvVarNvidiaImexChannels).List()
|
||||
}
|
||||
|
||||
// ImexChannelsFromMounts returns the list of IMEX channels requested for the image.
|
||||
func (i CUDA) ImexChannelsFromMounts() []string {
|
||||
var channels []string
|
||||
for _, mountDevice := range i.DevicesFromMounts() {
|
||||
if !strings.HasPrefix(mountDevice, volumeMountDevicePrefixImex) {
|
||||
continue
|
||||
}
|
||||
channels = append(channels, strings.TrimPrefix(mountDevice, volumeMountDevicePrefixImex))
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -130,3 +132,85 @@ func TestGetRequirements(t *testing.T) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVisibleDevicesFromMounts(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
mounts []specs.Mount
|
||||
expectedDevices []string
|
||||
}{
|
||||
{
|
||||
description: "No mounts",
|
||||
mounts: nil,
|
||||
expectedDevices: nil,
|
||||
},
|
||||
{
|
||||
description: "Host path is not /dev/null",
|
||||
mounts: []specs.Mount{
|
||||
{
|
||||
Source: "/not/dev/null",
|
||||
Destination: filepath.Join(DeviceListAsVolumeMountsRoot, "GPU0"),
|
||||
},
|
||||
},
|
||||
expectedDevices: nil,
|
||||
},
|
||||
{
|
||||
description: "Container path is not prefixed by 'root'",
|
||||
mounts: []specs.Mount{
|
||||
{
|
||||
Source: "/dev/null",
|
||||
Destination: filepath.Join("/other/prefix", "GPU0"),
|
||||
},
|
||||
},
|
||||
expectedDevices: nil,
|
||||
},
|
||||
{
|
||||
description: "Container path is only 'root'",
|
||||
mounts: []specs.Mount{
|
||||
{
|
||||
Source: "/dev/null",
|
||||
Destination: DeviceListAsVolumeMountsRoot,
|
||||
},
|
||||
},
|
||||
expectedDevices: nil,
|
||||
},
|
||||
{
|
||||
description: "Discover 2 devices",
|
||||
mounts: makeTestMounts("GPU0", "GPU1"),
|
||||
expectedDevices: []string{"GPU0", "GPU1"},
|
||||
},
|
||||
{
|
||||
description: "Discover 2 devices with slashes in the name",
|
||||
mounts: makeTestMounts("GPU0-MIG0/0/1", "GPU1-MIG0/0/1"),
|
||||
expectedDevices: []string{"GPU0-MIG0/0/1", "GPU1-MIG0/0/1"},
|
||||
},
|
||||
{
|
||||
description: "cdi devices are ignored",
|
||||
mounts: makeTestMounts("GPU0", "cdi/nvidia.com/gpu=all", "GPU1"),
|
||||
expectedDevices: []string{"GPU0", "GPU1"},
|
||||
},
|
||||
{
|
||||
description: "imex devices are ignored",
|
||||
mounts: makeTestMounts("GPU0", "imex/0", "GPU1"),
|
||||
expectedDevices: []string{"GPU0", "GPU1"},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
image, _ := New(WithMounts(tc.mounts))
|
||||
require.Equal(t, tc.expectedDevices, image.VisibleDevicesFromMounts())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestMounts(paths ...string) []specs.Mount {
|
||||
var mounts []specs.Mount
|
||||
for _, path := range paths {
|
||||
mount := specs.Mount{
|
||||
Source: "/dev/null",
|
||||
Destination: filepath.Join(DeviceListAsVolumeMountsRoot, path),
|
||||
}
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
||||
31
internal/config/image/envvars.go
Normal file
31
internal/config/image/envvars.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package image
|
||||
|
||||
const (
|
||||
EnvVarCudaVersion = "CUDA_VERSION"
|
||||
EnvVarNvidiaDisableRequire = "NVIDIA_DISABLE_REQUIRE"
|
||||
EnvVarNvidiaDriverCapabilities = "NVIDIA_DRIVER_CAPABILITIES"
|
||||
EnvVarNvidiaImexChannels = "NVIDIA_IMEX_CHANNELS"
|
||||
EnvVarNvidiaMigConfigDevices = "NVIDIA_MIG_CONFIG_DEVICES"
|
||||
EnvVarNvidiaMigMonitorDevices = "NVIDIA_MIG_MONITOR_DEVICES"
|
||||
EnvVarNvidiaRequireCuda = NvidiaRequirePrefix + "CUDA"
|
||||
EnvVarNvidiaRequireJetpack = NvidiaRequirePrefix + "JETPACK"
|
||||
EnvVarNvidiaVisibleDevices = "NVIDIA_VISIBLE_DEVICES"
|
||||
|
||||
NvidiaRequirePrefix = "NVIDIA_REQUIRE_"
|
||||
)
|
||||
@@ -170,11 +170,22 @@ func (t *Toml) Get(key string) interface{} {
|
||||
return (*toml.Tree)(t).Get(key)
|
||||
}
|
||||
|
||||
// GetDefault returns the value for the specified key and falls back to the default value if the Get call fails
|
||||
func (t *Toml) GetDefault(key string, def interface{}) interface{} {
|
||||
return (*toml.Tree)(t).GetDefault(key, def)
|
||||
}
|
||||
|
||||
// Set sets the specified key to the specified value in the TOML config.
|
||||
func (t *Toml) Set(key string, value interface{}) {
|
||||
(*toml.Tree)(t).Set(key, value)
|
||||
}
|
||||
|
||||
// WriteTo encode the Tree as Toml and writes it to the writer w.
|
||||
// Returns the number of bytes written in case of success, or an error if anything happened.
|
||||
func (t *Toml) WriteTo(w io.Writer) (int64, error) {
|
||||
return (*toml.Tree)(t).WriteTo(w)
|
||||
}
|
||||
|
||||
// commentDefaults applies the required comments for default values to the Toml.
|
||||
func (t *Toml) commentDefaults() *Toml {
|
||||
asToml := (*toml.Tree)(t)
|
||||
|
||||
80
internal/discover/cache.go
Normal file
80
internal/discover/cache.go
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package discover
|
||||
|
||||
import "sync"
|
||||
|
||||
type cache struct {
|
||||
d Discover
|
||||
|
||||
sync.Mutex
|
||||
devices []Device
|
||||
hooks []Hook
|
||||
mounts []Mount
|
||||
}
|
||||
|
||||
var _ Discover = (*cache)(nil)
|
||||
|
||||
// WithCache decorates the specified disoverer with a cache.
|
||||
func WithCache(d Discover) Discover {
|
||||
if d == nil {
|
||||
return None{}
|
||||
}
|
||||
return &cache{d: d}
|
||||
}
|
||||
|
||||
func (c *cache) Devices() ([]Device, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.devices == nil {
|
||||
devices, err := c.d.Devices()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.devices = devices
|
||||
}
|
||||
return c.devices, nil
|
||||
}
|
||||
|
||||
func (c *cache) Hooks() ([]Hook, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.hooks == nil {
|
||||
hooks, err := c.d.Hooks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.hooks = hooks
|
||||
}
|
||||
return c.hooks, nil
|
||||
}
|
||||
|
||||
func (c *cache) Mounts() ([]Mount, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.mounts == nil {
|
||||
mounts, err := c.d.Mounts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.mounts = mounts
|
||||
}
|
||||
return c.mounts, nil
|
||||
}
|
||||
72
internal/discover/first-valid.go
Normal file
72
internal/discover/first-valid.go
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package discover
|
||||
|
||||
import "errors"
|
||||
|
||||
type firstOf []Discover
|
||||
|
||||
// FirstValid returns a discoverer that returns the first non-error result from a list of discoverers.
|
||||
func FirstValid(discoverers ...Discover) Discover {
|
||||
var f firstOf
|
||||
for _, d := range discoverers {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
f = append(f, d)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (f firstOf) Devices() ([]Device, error) {
|
||||
var errs error
|
||||
for _, d := range f {
|
||||
devices, err := d.Devices()
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
continue
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
func (f firstOf) Hooks() ([]Hook, error) {
|
||||
var errs error
|
||||
for _, d := range f {
|
||||
hooks, err := d.Hooks()
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
continue
|
||||
}
|
||||
return hooks, nil
|
||||
}
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
func (f firstOf) Mounts() ([]Mount, error) {
|
||||
var errs error
|
||||
for _, d := range f {
|
||||
mounts, err := d.Mounts()
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
continue
|
||||
}
|
||||
return mounts, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -146,6 +146,27 @@ func newGraphicsLibrariesDiscoverer(logger logger.Interface, driver *root.Driver
|
||||
}
|
||||
}
|
||||
|
||||
// Mounts discovers the required libraries and filters out libnvidia-allocator.so.
|
||||
// The library libnvidia-allocator.so is already handled by either the *.RM_VERSION
|
||||
// injection or by libnvidia-container. We therefore filter it out here as a
|
||||
// workaround for the case where libnvidia-container will re-mount this in the
|
||||
// container, which causes issues with shared mount propagation.
|
||||
func (d graphicsDriverLibraries) Mounts() ([]Mount, error) {
|
||||
mounts, err := d.Discover.Mounts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get library mounts: %v", err)
|
||||
}
|
||||
|
||||
var filtered []Mount
|
||||
for _, mount := range mounts {
|
||||
if d.isDriverLibrary(filepath.Base(mount.Path), "libnvidia-allocator.so") {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, mount)
|
||||
}
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
// Create necessary library symlinks for graphics drivers
|
||||
func (d graphicsDriverLibraries) Hooks() ([]Hook, error) {
|
||||
mounts, err := d.Discover.Mounts()
|
||||
@@ -159,10 +180,10 @@ func (d graphicsDriverLibraries) Hooks() ([]Hook, error) {
|
||||
switch {
|
||||
case d.isDriverLibrary(filename, "libnvidia-allocator.so"):
|
||||
// gbm/nvidia-drm_gbm.so is a symlink to ../libnvidia-allocator.so.1 which
|
||||
// in turn symlinks to libnvidia-allocator.so.RM_VERSION and is created
|
||||
// when ldconfig is run in the container.
|
||||
// create libnvidia-allocate.so.1 -> libnvidia-allocate.so.RM_VERSION symlink
|
||||
links = append(links, fmt.Sprintf("%s::%s", filename, filepath.Join(dir, "libnvidia-allocator.so.1")))
|
||||
// in turn symlinks to libnvidia-allocator.so.RM_VERSION.
|
||||
// The libnvidia-allocator.so.1 -> libnvidia-allocator.so.RM_VERSION symlink
|
||||
// is created when ldconfig is run against the container and there
|
||||
// is no explicit need to create it.
|
||||
// create gbm/nvidia-drm_gbm.so -> ../libnvidia-allocate.so.1 symlink
|
||||
linkPath := filepath.Join(dir, "gbm", "nvidia-drm_gbm.so")
|
||||
links = append(links, fmt.Sprintf("%s::%s", "../libnvidia-allocator.so.1", linkPath))
|
||||
|
||||
@@ -62,17 +62,12 @@ func TestGraphicsLibrariesDiscoverer(t *testing.T) {
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib64/libnvidia-allocator.so.123.45.67",
|
||||
},
|
||||
},
|
||||
expectedMounts: nil,
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks",
|
||||
"--link", "libnvidia-allocator.so.123.45.67::/usr/lib64/libnvidia-allocator.so.1",
|
||||
"--link", "../libnvidia-allocator.so.1::/usr/lib64/gbm/nvidia-drm_gbm.so",
|
||||
},
|
||||
},
|
||||
@@ -121,9 +116,6 @@ func TestGraphicsLibrariesDiscoverer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib64/libnvidia-allocator.so.123.45.67",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib64/libnvidia-vulkan-producer.so.123.45.67",
|
||||
},
|
||||
@@ -133,7 +125,6 @@ func TestGraphicsLibrariesDiscoverer(t *testing.T) {
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/usr/bin/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks",
|
||||
"--link", "libnvidia-allocator.so.123.45.67::/usr/lib64/libnvidia-allocator.so.1",
|
||||
"--link", "../libnvidia-allocator.so.1::/usr/lib64/gbm/nvidia-drm_gbm.so",
|
||||
"--link", "libnvidia-vulkan-producer.so.123.45.67::/usr/lib64/libnvidia-vulkan-producer.so",
|
||||
},
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestMounts(t *testing.T) {
|
||||
lookup: &lookup.LocatorMock{
|
||||
LocateFunc: func(s string) ([]string, error) {
|
||||
if s == "error" {
|
||||
return nil, fmt.Errorf(s)
|
||||
return nil, fmt.Errorf("error")
|
||||
}
|
||||
return []string{s}, nil
|
||||
},
|
||||
|
||||
@@ -24,15 +24,15 @@ var _ Discover = (*None)(nil)
|
||||
|
||||
// Devices returns an empty list of devices
|
||||
func (e None) Devices() ([]Device, error) {
|
||||
return []Device{}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Mounts returns an empty list of mounts
|
||||
func (e None) Mounts() ([]Mount, error) {
|
||||
return []Mount{}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Hooks returns an empty list of hooks
|
||||
func (e None) Hooks() ([]Hook, error) {
|
||||
return []Hook{}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
108
internal/discover/symlinks.go
Normal file
108
internal/discover/symlinks.go
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type additionalSymlinks struct {
|
||||
Discover
|
||||
version string
|
||||
nvidiaCDIHookPath string
|
||||
}
|
||||
|
||||
// WithDriverDotSoSymlinks decorates the provided discoverer.
|
||||
// A hook is added that checks for specific driver symlinks that need to be created.
|
||||
func WithDriverDotSoSymlinks(mounts Discover, version string, nvidiaCDIHookPath string) Discover {
|
||||
if version == "" {
|
||||
version = "*.*"
|
||||
}
|
||||
return &additionalSymlinks{
|
||||
Discover: mounts,
|
||||
nvidiaCDIHookPath: nvidiaCDIHookPath,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// Hooks returns a hook to create the additional symlinks based on the mounts.
|
||||
func (d *additionalSymlinks) Hooks() ([]Hook, error) {
|
||||
mounts, err := d.Discover.Mounts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get library mounts: %v", err)
|
||||
}
|
||||
hooks, err := d.Discover.Hooks()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get hooks: %v", err)
|
||||
}
|
||||
|
||||
var links []string
|
||||
processedPaths := make(map[string]bool)
|
||||
processedLinks := make(map[string]bool)
|
||||
for _, mount := range mounts {
|
||||
if processedPaths[mount.Path] {
|
||||
continue
|
||||
}
|
||||
processedPaths[mount.Path] = true
|
||||
|
||||
for _, link := range d.getLinksForMount(mount.Path) {
|
||||
if processedLinks[link] {
|
||||
continue
|
||||
}
|
||||
processedLinks[link] = true
|
||||
links = append(links, link)
|
||||
}
|
||||
}
|
||||
|
||||
if len(links) == 0 {
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
hook := CreateCreateSymlinkHook(d.nvidiaCDIHookPath, links).(Hook)
|
||||
return append(hooks, hook), nil
|
||||
}
|
||||
|
||||
// getLinksForMount maps the path to created links if any.
|
||||
func (d additionalSymlinks) getLinksForMount(path string) []string {
|
||||
dir, filename := filepath.Split(path)
|
||||
switch {
|
||||
case d.isDriverLibrary("libcuda.so", filename):
|
||||
// XXX Many applications wrongly assume that libcuda.so exists (e.g. with dlopen).
|
||||
// create libcuda.so -> libcuda.so.1 symlink
|
||||
link := fmt.Sprintf("%s::%s", "libcuda.so.1", filepath.Join(dir, "libcuda.so"))
|
||||
return []string{link}
|
||||
case d.isDriverLibrary("libGLX_nvidia.so", filename):
|
||||
// XXX GLVND requires this symlink for indirect GLX support.
|
||||
// create libGLX_indirect.so.0 -> libGLX_nvidia.so.VERSION symlink
|
||||
link := fmt.Sprintf("%s::%s", filename, filepath.Join(dir, "libGLX_indirect.so.0"))
|
||||
return []string{link}
|
||||
case d.isDriverLibrary("libnvidia-opticalflow.so", filename):
|
||||
// XXX Fix missing symlink for libnvidia-opticalflow.so.
|
||||
// create libnvidia-opticalflow.so -> libnvidia-opticalflow.so.1 symlink
|
||||
link := fmt.Sprintf("%s::%s", "libnvidia-opticalflow.so.1", filepath.Join(dir, "libnvidia-opticalflow.so"))
|
||||
return []string{link}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isDriverLibrary checks whether the specified filename is a specific driver library.
|
||||
func (d additionalSymlinks) isDriverLibrary(libraryName string, filename string) bool {
|
||||
pattern := libraryName + "." + d.version
|
||||
match, _ := filepath.Match(pattern, filename)
|
||||
return match
|
||||
}
|
||||
330
internal/discover/symlinks_test.go
Normal file
330
internal/discover/symlinks_test.go
Normal file
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWithWithDriverDotSoSymlinks(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
discover Discover
|
||||
version string
|
||||
expectedDevices []Device
|
||||
expectedDevicesError error
|
||||
expectedHooks []Hook
|
||||
expectedHooksError error
|
||||
expectedMounts []Mount
|
||||
expectedMountsError error
|
||||
}{
|
||||
{
|
||||
description: "empty discoverer remains empty",
|
||||
discover: None{},
|
||||
},
|
||||
{
|
||||
description: "non-matching discoverer remains unchanged",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
devices := []Device{
|
||||
{
|
||||
Path: "/dev/dev1",
|
||||
},
|
||||
}
|
||||
return devices, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
hooks := []Hook{
|
||||
{
|
||||
Lifecycle: "prestart",
|
||||
Path: "/path/to/a/hook",
|
||||
Args: []string{"hook", "arg1", "arg2"},
|
||||
},
|
||||
}
|
||||
return hooks, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libnotcuda.so.1.2.3",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
expectedDevices: []Device{
|
||||
{
|
||||
Path: "/dev/dev1",
|
||||
},
|
||||
},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "prestart",
|
||||
Path: "/path/to/a/hook",
|
||||
Args: []string{"hook", "arg1", "arg2"},
|
||||
},
|
||||
},
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libnotcuda.so.1.2.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "libcuda.so.RM_VERSION is matched",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
return nil, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
return nil, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
version: "1.2.3",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/path/to/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks", "--link", "libcuda.so.1::/usr/lib/libcuda.so"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "libcuda.so.RM_VERSION is matched by pattern",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
return nil, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
return nil, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
version: "",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/path/to/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks", "--link", "libcuda.so.1::/usr/lib/libcuda.so"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "beta libcuda.so.RM_VERSION is matched",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
return nil, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
return nil, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2",
|
||||
},
|
||||
},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/path/to/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks", "--link", "libcuda.so.1::/usr/lib/libcuda.so"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "non-matching libcuda.so.RM_VERSION is ignored",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
return nil, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
return nil, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
version: "4.5.6",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "hooks are extended",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
return nil, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
hooks := []Hook{
|
||||
{
|
||||
Lifecycle: "prestart",
|
||||
Path: "/path/to/a/hook",
|
||||
Args: []string{"hook", "arg1", "arg2"},
|
||||
},
|
||||
}
|
||||
return hooks, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
version: "1.2.3",
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "prestart",
|
||||
Path: "/path/to/a/hook",
|
||||
Args: []string{"hook", "arg1", "arg2"},
|
||||
},
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/path/to/nvidia-cdi-hook",
|
||||
Args: []string{"nvidia-cdi-hook", "create-symlinks", "--link", "libcuda.so.1::/usr/lib/libcuda.so"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "all driver so symlinks are matched",
|
||||
discover: &DiscoverMock{
|
||||
DevicesFunc: func() ([]Device, error) {
|
||||
return nil, nil
|
||||
},
|
||||
HooksFunc: func() ([]Hook, error) {
|
||||
return nil, nil
|
||||
},
|
||||
MountsFunc: func() ([]Mount, error) {
|
||||
mounts := []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib/libGLX_nvidia.so.1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib/libnvidia-opticalflow.so.1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib/libanother.so.1.2.3",
|
||||
},
|
||||
}
|
||||
return mounts, nil
|
||||
},
|
||||
},
|
||||
expectedMounts: []Mount{
|
||||
{
|
||||
Path: "/usr/lib/libcuda.so.1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib/libGLX_nvidia.so.1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib/libnvidia-opticalflow.so.1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "/usr/lib/libanother.so.1.2.3",
|
||||
},
|
||||
},
|
||||
expectedHooks: []Hook{
|
||||
{
|
||||
Lifecycle: "createContainer",
|
||||
Path: "/path/to/nvidia-cdi-hook",
|
||||
Args: []string{
|
||||
"nvidia-cdi-hook", "create-symlinks",
|
||||
"--link", "libcuda.so.1::/usr/lib/libcuda.so",
|
||||
"--link", "libGLX_nvidia.so.1.2.3::/usr/lib/libGLX_indirect.so.0",
|
||||
"--link", "libnvidia-opticalflow.so.1::/usr/lib/libnvidia-opticalflow.so",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
d := WithDriverDotSoSymlinks(
|
||||
tc.discover,
|
||||
tc.version,
|
||||
"/path/to/nvidia-cdi-hook",
|
||||
)
|
||||
|
||||
devices, err := d.Devices()
|
||||
require.ErrorIs(t, err, tc.expectedDevicesError)
|
||||
require.EqualValues(t, tc.expectedDevices, devices)
|
||||
|
||||
hooks, err := d.Hooks()
|
||||
require.ErrorIs(t, err, tc.expectedHooksError)
|
||||
require.EqualValues(t, tc.expectedHooks, hooks)
|
||||
|
||||
mounts, err := d.Mounts()
|
||||
require.ErrorIs(t, err, tc.expectedMountsError)
|
||||
require.EqualValues(t, tc.expectedMounts, mounts)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
|
||||
)
|
||||
|
||||
const ldcachePath = "/etc/ld.so.cache"
|
||||
@@ -82,10 +79,9 @@ type entry2 struct {
|
||||
|
||||
// LDCache represents the interface for performing lookups into the LDCache
|
||||
//
|
||||
//go:generate moq -out ldcache_mock.go . LDCache
|
||||
//go:generate moq -rm -out ldcache_mock.go . LDCache
|
||||
type LDCache interface {
|
||||
List() ([]string, []string)
|
||||
Lookup(...string) ([]string, []string)
|
||||
}
|
||||
|
||||
type ldcache struct {
|
||||
@@ -105,14 +101,7 @@ func New(logger logger.Interface, root string) (LDCache, error) {
|
||||
|
||||
logger.Debugf("Opening ld.conf at %v", path)
|
||||
f, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
logger.Warningf("Could not find ld.so.cache at %v; creating empty cache", path)
|
||||
e := &empty{
|
||||
logger: logger,
|
||||
path: path,
|
||||
}
|
||||
return e, nil
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
@@ -196,7 +185,7 @@ type entry struct {
|
||||
}
|
||||
|
||||
// getEntries returns the entires of the ldcache in a go-friendly struct.
|
||||
func (c *ldcache) getEntries(selected func(string) bool) []entry {
|
||||
func (c *ldcache) getEntries() []entry {
|
||||
var entries []entry
|
||||
for _, e := range c.entries {
|
||||
bits := 0
|
||||
@@ -223,9 +212,6 @@ func (c *ldcache) getEntries(selected func(string) bool) []entry {
|
||||
c.logger.Debugf("Skipping invalid lib")
|
||||
continue
|
||||
}
|
||||
if !selected(lib) {
|
||||
continue
|
||||
}
|
||||
value := bytesToString(c.libs[e.Value:])
|
||||
if value == "" {
|
||||
c.logger.Debugf("Skipping invalid value for lib %v", lib)
|
||||
@@ -236,51 +222,19 @@ func (c *ldcache) getEntries(selected func(string) bool) []entry {
|
||||
bits: bits,
|
||||
value: value,
|
||||
}
|
||||
|
||||
entries = append(entries, e)
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// List creates a list of libraries in the ldcache.
|
||||
// The 32-bit and 64-bit libraries are returned separately.
|
||||
func (c *ldcache) List() ([]string, []string) {
|
||||
all := func(s string) bool { return true }
|
||||
|
||||
return c.resolveSelected(all)
|
||||
}
|
||||
|
||||
// Lookup searches the ldcache for the specified prefixes.
|
||||
// The 32-bit and 64-bit libraries matching the prefixes are returned.
|
||||
func (c *ldcache) Lookup(libPrefixes ...string) ([]string, []string) {
|
||||
c.logger.Debugf("Looking up %v in cache", libPrefixes)
|
||||
|
||||
// We define a functor to check whether a given library name matches any of the prefixes
|
||||
matchesAnyPrefix := func(s string) bool {
|
||||
for _, p := range libPrefixes {
|
||||
if strings.HasPrefix(s, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return c.resolveSelected(matchesAnyPrefix)
|
||||
}
|
||||
|
||||
// resolveSelected process the entries in the LDCach based on the supplied filter and returns the resolved paths.
|
||||
// The paths are separated by bittage.
|
||||
func (c *ldcache) resolveSelected(selected func(string) bool) ([]string, []string) {
|
||||
paths := make(map[int][]string)
|
||||
processed := make(map[string]bool)
|
||||
|
||||
for _, e := range c.getEntries(selected) {
|
||||
path, err := c.resolve(e.value)
|
||||
if err != nil {
|
||||
c.logger.Debugf("Could not resolve entry: %v", err)
|
||||
continue
|
||||
}
|
||||
for _, e := range c.getEntries() {
|
||||
path := filepath.Join(c.root, e.value)
|
||||
if processed[path] {
|
||||
continue
|
||||
}
|
||||
@@ -291,29 +245,6 @@ func (c *ldcache) resolveSelected(selected func(string) bool) ([]string, []strin
|
||||
return paths[32], paths[64]
|
||||
}
|
||||
|
||||
// resolve resolves the specified ldcache entry based on the value being processed.
|
||||
// The input is the name of the entry in the cache.
|
||||
func (c *ldcache) resolve(target string) (string, error) {
|
||||
name := filepath.Join(c.root, target)
|
||||
|
||||
c.logger.Debugf("checking %v", name)
|
||||
|
||||
link, err := symlinks.Resolve(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to resolve symlink: %v", err)
|
||||
}
|
||||
if link == name {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// We return absolute paths for all targets
|
||||
if !filepath.IsAbs(link) || strings.HasPrefix(link, ".") {
|
||||
link = filepath.Join(filepath.Dir(target), link)
|
||||
}
|
||||
|
||||
return c.resolve(link)
|
||||
}
|
||||
|
||||
// bytesToString converts a byte slice to a string.
|
||||
// This assumes that the byte slice is null-terminated
|
||||
func bytesToString(value []byte) string {
|
||||
|
||||
@@ -20,9 +20,6 @@ var _ LDCache = &LDCacheMock{}
|
||||
// ListFunc: func() ([]string, []string) {
|
||||
// panic("mock out the List method")
|
||||
// },
|
||||
// LookupFunc: func(strings ...string) ([]string, []string) {
|
||||
// panic("mock out the Lookup method")
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// // use mockedLDCache in code that requires LDCache
|
||||
@@ -33,22 +30,13 @@ type LDCacheMock struct {
|
||||
// ListFunc mocks the List method.
|
||||
ListFunc func() ([]string, []string)
|
||||
|
||||
// LookupFunc mocks the Lookup method.
|
||||
LookupFunc func(strings ...string) ([]string, []string)
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
// List holds details about calls to the List method.
|
||||
List []struct {
|
||||
}
|
||||
// Lookup holds details about calls to the Lookup method.
|
||||
Lookup []struct {
|
||||
// Strings is the strings argument value.
|
||||
Strings []string
|
||||
}
|
||||
}
|
||||
lockList sync.RWMutex
|
||||
lockLookup sync.RWMutex
|
||||
lockList sync.RWMutex
|
||||
}
|
||||
|
||||
// List calls ListFunc.
|
||||
@@ -77,35 +65,3 @@ func (mock *LDCacheMock) ListCalls() []struct {
|
||||
mock.lockList.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Lookup calls LookupFunc.
|
||||
func (mock *LDCacheMock) Lookup(strings ...string) ([]string, []string) {
|
||||
if mock.LookupFunc == nil {
|
||||
panic("LDCacheMock.LookupFunc: method is nil but LDCache.Lookup was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Strings []string
|
||||
}{
|
||||
Strings: strings,
|
||||
}
|
||||
mock.lockLookup.Lock()
|
||||
mock.calls.Lookup = append(mock.calls.Lookup, callInfo)
|
||||
mock.lockLookup.Unlock()
|
||||
return mock.LookupFunc(strings...)
|
||||
}
|
||||
|
||||
// LookupCalls gets all the calls that were made to Lookup.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedLDCache.LookupCalls())
|
||||
func (mock *LDCacheMock) LookupCalls() []struct {
|
||||
Strings []string
|
||||
} {
|
||||
var calls []struct {
|
||||
Strings []string
|
||||
}
|
||||
mock.lockLookup.RLock()
|
||||
calls = mock.calls.Lookup
|
||||
mock.lockLookup.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
118
internal/lookup/ldcache.go
Normal file
118
internal/lookup/ldcache.go
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package lookup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
)
|
||||
|
||||
type ldcacheLocator struct {
|
||||
*builder
|
||||
resolvesTo map[string]string
|
||||
}
|
||||
|
||||
var _ Locator = (*ldcacheLocator)(nil)
|
||||
|
||||
func NewLdcacheLocator(opts ...Option) Locator {
|
||||
b := newBuilder(opts...)
|
||||
|
||||
cache, err := ldcache.New(b.logger, b.root)
|
||||
if err != nil {
|
||||
b.logger.Warningf("Failed to load ldcache: %v", err)
|
||||
if b.isOptional {
|
||||
return &null{}
|
||||
}
|
||||
return ¬Found{}
|
||||
}
|
||||
|
||||
chain := NewSymlinkChainLocator(WithOptional(true))
|
||||
|
||||
resolvesTo := make(map[string]string)
|
||||
_, libs64 := cache.List()
|
||||
for _, library := range libs64 {
|
||||
if _, processed := resolvesTo[library]; processed {
|
||||
continue
|
||||
}
|
||||
candidates, err := chain.Locate(library)
|
||||
if err != nil {
|
||||
b.logger.Errorf("error processing library %s from ldcache: %v", library, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(candidates) == 0 {
|
||||
resolvesTo[library] = library
|
||||
continue
|
||||
}
|
||||
|
||||
// candidates represents a symlink chain.
|
||||
// The first element represents the start of the chain and the last
|
||||
// element the final target.
|
||||
target := candidates[len(candidates)-1]
|
||||
for _, candidate := range candidates {
|
||||
resolvesTo[candidate] = target
|
||||
}
|
||||
}
|
||||
|
||||
return &ldcacheLocator{
|
||||
builder: b,
|
||||
resolvesTo: resolvesTo,
|
||||
}
|
||||
}
|
||||
|
||||
// Locate finds the specified libraryname.
|
||||
// If the input is a library name, the ldcache is searched otherwise the
|
||||
// provided path is resolved as a symlink.
|
||||
func (l ldcacheLocator) Locate(libname string) ([]string, error) {
|
||||
var matcher func(string, string) bool
|
||||
|
||||
if filepath.IsAbs(libname) {
|
||||
matcher = func(p string, c string) bool {
|
||||
m, _ := filepath.Match(filepath.Join(l.root, p), c)
|
||||
return m
|
||||
}
|
||||
} else {
|
||||
matcher = func(p string, c string) bool {
|
||||
m, _ := filepath.Match(p, filepath.Base(c))
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
var matches []string
|
||||
seen := make(map[string]bool)
|
||||
for name, target := range l.resolvesTo {
|
||||
if !matcher(libname, name) {
|
||||
continue
|
||||
}
|
||||
if seen[target] {
|
||||
continue
|
||||
}
|
||||
seen[target] = true
|
||||
matches = append(matches, target)
|
||||
}
|
||||
|
||||
slices.Sort(matches)
|
||||
|
||||
if len(matches) == 0 && !l.isOptional {
|
||||
return nil, fmt.Errorf("%s: %w", libname, ErrNotFound)
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
77
internal/lookup/ldcache_test.go
Normal file
77
internal/lookup/ldcache_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package lookup
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestLDCacheLookup(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
rootFs string
|
||||
inputs []string
|
||||
expected string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
rootFs: "rootfs-empty",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expectedError: ErrNotFound,
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-1",
|
||||
inputs: []string{
|
||||
"libcuda.so.1",
|
||||
"libcuda.so.*",
|
||||
"libcuda.so.*.*",
|
||||
"libcuda.so.999.88.77",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.1",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.*",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.*.*",
|
||||
"/lib/x86_64-linux-gnu/libcuda.so.999.88.77",
|
||||
},
|
||||
expected: "/lib/x86_64-linux-gnu/libcuda.so.999.88.77",
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-2",
|
||||
inputs: []string{
|
||||
"libcuda.so.1",
|
||||
"libcuda.so.*",
|
||||
"libcuda.so.*.*",
|
||||
"libcuda.so.999.88.77",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.1",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.*",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.*.*",
|
||||
"/var/lib/nvidia/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
expected: "/var/lib/nvidia/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for _, input := range tc.inputs {
|
||||
t.Run(tc.rootFs+" "+input, func(t *testing.T) {
|
||||
rootfs := filepath.Join(moduleRoot, "testdata", "lookup", tc.rootFs)
|
||||
l := NewLdcacheLocator(
|
||||
WithLogger(logger),
|
||||
WithRoot(rootfs),
|
||||
)
|
||||
|
||||
candidates, err := l.Locate(input)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
if tc.expectedError == nil {
|
||||
require.Equal(t, []string{filepath.Join(rootfs, tc.expected)}, candidates)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,6 @@
|
||||
|
||||
package lookup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
type ldcacheLocator struct {
|
||||
logger logger.Interface
|
||||
cache ldcache.LDCache
|
||||
}
|
||||
|
||||
var _ Locator = (*ldcacheLocator)(nil)
|
||||
|
||||
// NewLibraryLocator creates a library locator using the specified options.
|
||||
func NewLibraryLocator(opts ...Option) Locator {
|
||||
b := newBuilder(opts...)
|
||||
@@ -63,39 +49,7 @@ func NewLibraryLocator(opts ...Option) Locator {
|
||||
|
||||
l := First(
|
||||
symlinkLocator,
|
||||
newLdcacheLocator(opts...),
|
||||
NewLdcacheLocator(opts...),
|
||||
)
|
||||
return l
|
||||
}
|
||||
|
||||
func newLdcacheLocator(opts ...Option) Locator {
|
||||
b := newBuilder(opts...)
|
||||
|
||||
cache, err := ldcache.New(b.logger, b.root)
|
||||
if err != nil {
|
||||
// If we failed to open the LDCache, we default to a symlink locator.
|
||||
b.logger.Warningf("Failed to load ldcache: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ldcacheLocator{
|
||||
logger: b.logger,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// Locate finds the specified libraryname.
|
||||
// If the input is a library name, the ldcache is searched otherwise the
|
||||
// provided path is resolved as a symlink.
|
||||
func (l ldcacheLocator) Locate(libname string) ([]string, error) {
|
||||
paths32, paths64 := l.cache.Lookup(libname)
|
||||
if len(paths32) > 0 {
|
||||
l.logger.Warningf("Ignoring 32-bit libraries for %v: %v", libname, paths32)
|
||||
}
|
||||
|
||||
if len(paths64) == 0 {
|
||||
return nil, fmt.Errorf("64-bit library %v: %w", libname, ErrNotFound)
|
||||
}
|
||||
|
||||
return paths64, nil
|
||||
}
|
||||
|
||||
@@ -24,82 +24,8 @@ import (
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/ldcache"
|
||||
)
|
||||
|
||||
func TestLDCacheLocator(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testDir := t.TempDir()
|
||||
symlinkDir := filepath.Join(testDir, "/lib/symlink")
|
||||
require.NoError(t, os.MkdirAll(symlinkDir, 0755))
|
||||
|
||||
versionLib := filepath.Join(symlinkDir, "libcuda.so.1.2.3")
|
||||
soLink := filepath.Join(symlinkDir, "libcuda.so")
|
||||
sonameLink := filepath.Join(symlinkDir, "libcuda.so.1")
|
||||
|
||||
_, err := os.Create(versionLib)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.Symlink(versionLib, sonameLink))
|
||||
require.NoError(t, os.Symlink(sonameLink, soLink))
|
||||
|
||||
lut := newLdcacheLocator(
|
||||
WithLogger(logger),
|
||||
WithRoot(testDir),
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
libname string
|
||||
ldcacheMap map[string]string
|
||||
expected []string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "lib only resolves in LDCache",
|
||||
libname: "libcuda.so",
|
||||
ldcacheMap: map[string]string{
|
||||
"libcuda.so": "/lib/from/ldcache/libcuda.so.4.5.6",
|
||||
},
|
||||
expected: []string{"/lib/from/ldcache/libcuda.so.4.5.6"},
|
||||
},
|
||||
{
|
||||
description: "lib only not in LDCache returns error",
|
||||
libname: "libnotcuda.so",
|
||||
expectedError: ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
// We override the LDCache with a mock implementation
|
||||
l := lut.(*ldcacheLocator)
|
||||
l.cache = &ldcache.LDCacheMock{
|
||||
LookupFunc: func(strings ...string) ([]string, []string) {
|
||||
var result []string
|
||||
for _, s := range strings {
|
||||
if v, ok := tc.ldcacheMap[s]; ok {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return nil, result
|
||||
},
|
||||
}
|
||||
|
||||
candidates, err := lut.Locate(tc.libname)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
|
||||
var cleanedCandidates []string
|
||||
for _, c := range candidates {
|
||||
// On MacOS /var and /tmp symlink to /private/var and /private/tmp which is included in the resolved path.
|
||||
cleanedCandidates = append(cleanedCandidates, strings.TrimPrefix(c, "/private"))
|
||||
}
|
||||
require.EqualValues(t, tc.expected, cleanedCandidates)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLibraryLocator(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
# Copyright (c) NVIDIA CORPORATION. All rights reserved.
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -14,24 +14,23 @@
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package ldcache
|
||||
package lookup
|
||||
|
||||
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
import "fmt"
|
||||
|
||||
type empty struct {
|
||||
logger logger.Interface
|
||||
path string
|
||||
// A null locator always returns an empty response.
|
||||
type null struct {
|
||||
}
|
||||
|
||||
var _ LDCache = (*empty)(nil)
|
||||
|
||||
// List always returns nil for an empty ldcache
|
||||
func (e *empty) List() ([]string, []string) {
|
||||
// Locate always returns empty for a null locator.
|
||||
func (l *null) Locate(string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Lookup logs a debug message and returns nil for an empty ldcache
|
||||
func (e *empty) Lookup(prefixes ...string) ([]string, []string) {
|
||||
e.logger.Debugf("Calling Lookup(%v) on empty ldcache: %v", prefixes, e.path)
|
||||
return nil, nil
|
||||
// A notFound locator always returns an ErrNotFound error.
|
||||
type notFound struct {
|
||||
}
|
||||
|
||||
func (l *notFound) Locate(s string) ([]string, error) {
|
||||
return nil, fmt.Errorf("%s: %w", s, ErrNotFound)
|
||||
}
|
||||
81
internal/lookup/root/root_test.go
Normal file
81
internal/lookup/root/root_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
# Copyright 2023 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package root
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestDriverLibrariesLocate(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
rootFs string
|
||||
inputs []string
|
||||
expected string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
rootFs: "rootfs-empty",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expectedError: lookup.ErrNotFound,
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-no-cache-lib64",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expected: "/usr/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-1",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expected: "/lib/x86_64-linux-gnu/libcuda.so.999.88.77",
|
||||
},
|
||||
{
|
||||
rootFs: "rootfs-2",
|
||||
inputs: []string{"libcuda.so.1", "libcuda.so.*", "libcuda.so.*.*", "libcuda.so.999.88.77"},
|
||||
expected: "/var/lib/nvidia/lib64/libcuda.so.999.88.77",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for _, input := range tc.inputs {
|
||||
t.Run(tc.rootFs+input, func(t *testing.T) {
|
||||
rootfs := filepath.Join(moduleRoot, "testdata", "lookup", tc.rootFs)
|
||||
driver := New(
|
||||
WithLogger(logger),
|
||||
WithDriverRoot(rootfs),
|
||||
)
|
||||
|
||||
candidates, err := driver.Libraries().Locate(input)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
if tc.expectedError == nil {
|
||||
require.Equal(t, []string{filepath.Join(rootfs, tc.expected)}, candidates)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ func (p symlinkChain) Locate(pattern string) ([]string, error) {
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
var filenames []string
|
||||
found := make(map[string]bool)
|
||||
for len(candidates) > 0 {
|
||||
candidate := candidates[0]
|
||||
@@ -70,6 +71,7 @@ func (p symlinkChain) Locate(pattern string) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
found[candidate] = true
|
||||
filenames = append(filenames, candidate)
|
||||
|
||||
target, err := symlinks.Resolve(candidate)
|
||||
if err != nil {
|
||||
@@ -88,11 +90,6 @@ func (p symlinkChain) Locate(pattern string) ([]string, error) {
|
||||
candidates = append(candidates, target)
|
||||
}
|
||||
}
|
||||
|
||||
var filenames []string
|
||||
for f := range found {
|
||||
filenames = append(filenames, f)
|
||||
}
|
||||
return filenames, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
func Resolve(filename string) (string, error) {
|
||||
info, err := os.Lstat(filename)
|
||||
if err != nil {
|
||||
return filename, fmt.Errorf("failed to get file info: %v", info)
|
||||
return filename, fmt.Errorf("failed to get file info: %w", err)
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink == 0 {
|
||||
return filename, nil
|
||||
|
||||
@@ -90,11 +90,9 @@ func getDevicesFromSpec(logger logger.Interface, ociSpec oci.Spec, cfg *config.C
|
||||
}
|
||||
}
|
||||
|
||||
envDevices := container.DevicesFromEnvvars(visibleDevicesEnvvar)
|
||||
|
||||
var devices []string
|
||||
seen := make(map[string]bool)
|
||||
for _, name := range envDevices.List() {
|
||||
for _, name := range container.VisibleDevicesFromEnvVar() {
|
||||
if !parser.IsQualifiedName(name) {
|
||||
name = fmt.Sprintf("%s=%s", cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.DefaultKind, name)
|
||||
}
|
||||
|
||||
@@ -30,23 +30,16 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
|
||||
)
|
||||
|
||||
const (
|
||||
visibleDevicesEnvvar = "NVIDIA_VISIBLE_DEVICES"
|
||||
visibleDevicesVoid = "void"
|
||||
|
||||
nvidiaRequireJetpackEnvvar = "NVIDIA_REQUIRE_JETPACK"
|
||||
)
|
||||
|
||||
// NewCSVModifier creates a modifier that applies modications to an OCI spec if required by the runtime wrapper.
|
||||
// The modifications are defined by CSV MountSpecs.
|
||||
func NewCSVModifier(logger logger.Interface, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
||||
if devices := image.DevicesFromEnvvars(visibleDevicesEnvvar); len(devices.List()) == 0 {
|
||||
func NewCSVModifier(logger logger.Interface, cfg *config.Config, container image.CUDA) (oci.SpecModifier, error) {
|
||||
if devices := container.VisibleDevicesFromEnvVar(); len(devices) == 0 {
|
||||
logger.Infof("No modification required; no devices requested")
|
||||
return nil, nil
|
||||
}
|
||||
logger.Infof("Constructing modifier from config: %+v", *cfg)
|
||||
|
||||
if err := checkRequirements(logger, image); err != nil {
|
||||
if err := checkRequirements(logger, container); err != nil {
|
||||
return nil, fmt.Errorf("requirements not met: %v", err)
|
||||
}
|
||||
|
||||
@@ -55,7 +48,7 @@ func NewCSVModifier(logger logger.Interface, cfg *config.Config, image image.CUD
|
||||
return nil, fmt.Errorf("failed to get list of CSV files: %v", err)
|
||||
}
|
||||
|
||||
if image.Getenv(nvidiaRequireJetpackEnvvar) != "csv-mounts=all" {
|
||||
if container.Getenv(image.EnvVarNvidiaRequireJetpack) != "csv-mounts=all" {
|
||||
csvFiles = csv.BaseFilesOnly(csvFiles)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
//
|
||||
// If not devices are selected, no changes are made.
|
||||
func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
||||
if devices := image.DevicesFromEnvvars(visibleDevicesEnvvar); len(devices.List()) == 0 {
|
||||
if devices := image.VisibleDevicesFromEnvVar(); len(devices) == 0 {
|
||||
logger.Infof("No modification required; no devices requested")
|
||||
return nil, nil
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
driverRoot := cfg.NVIDIAContainerCLIConfig.Root
|
||||
devRoot := cfg.NVIDIAContainerCLIConfig.Root
|
||||
|
||||
if cfg.Features.IsEnabled(config.FeatureGDS, image) {
|
||||
if image.Getenv("NVIDIA_GDS") == "enabled" {
|
||||
d, err := discover.NewGDSDiscoverer(logger, driverRoot, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for GDS devices: %w", err)
|
||||
@@ -54,7 +54,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if cfg.Features.IsEnabled(config.FeatureMOFED, image) {
|
||||
if image.Getenv("NVIDIA_MOFED") == "enabled" {
|
||||
d, err := discover.NewMOFEDDiscoverer(logger, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for MOFED devices: %w", err)
|
||||
@@ -62,7 +62,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if cfg.Features.IsEnabled(config.FeatureNVSWITCH, image) {
|
||||
if image.Getenv("NVIDIA_NVSWITCH") == "enabled" {
|
||||
d, err := discover.NewNvSwitchDiscoverer(logger, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for NVSWITCH devices: %w", err)
|
||||
@@ -70,7 +70,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if cfg.Features.IsEnabled(config.FeatureGDRCopy, image) {
|
||||
if image.Getenv("NVIDIA_GDRCOPY") == "enabled" {
|
||||
d, err := discover.NewGDRCopyDiscoverer(logger, devRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct discoverer for GDRCopy devices: %w", err)
|
||||
|
||||
@@ -29,8 +29,8 @@ import (
|
||||
|
||||
// NewGraphicsModifier constructs a modifier that injects graphics-related modifications into an OCI runtime specification.
|
||||
// The value of the NVIDIA_DRIVER_CAPABILITIES environment variable is checked to determine if this modification should be made.
|
||||
func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image image.CUDA, driver *root.Driver) (oci.SpecModifier, error) {
|
||||
if required, reason := requiresGraphicsModifier(image); !required {
|
||||
func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, containerImage image.CUDA, driver *root.Driver) (oci.SpecModifier, error) {
|
||||
if required, reason := requiresGraphicsModifier(containerImage); !required {
|
||||
logger.Infof("No graphics modifier required: %v", reason)
|
||||
return nil, nil
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image imag
|
||||
devRoot := driver.Root
|
||||
drmNodes, err := discover.NewDRMNodesDiscoverer(
|
||||
logger,
|
||||
image.DevicesFromEnvvars(visibleDevicesEnvvar),
|
||||
containerImage.DevicesFromEnvvars(image.EnvVarNvidiaVisibleDevices),
|
||||
devRoot,
|
||||
nvidiaCDIHookPath,
|
||||
)
|
||||
@@ -67,7 +67,7 @@ func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image imag
|
||||
|
||||
// requiresGraphicsModifier determines whether a graphics modifier is required.
|
||||
func requiresGraphicsModifier(cudaImage image.CUDA) (bool, string) {
|
||||
if devices := cudaImage.DevicesFromEnvvars(visibleDevicesEnvvar); len(devices.List()) == 0 {
|
||||
if devices := cudaImage.VisibleDevicesFromEnvVar(); len(devices) == 0 {
|
||||
return false, "no devices requested"
|
||||
}
|
||||
|
||||
|
||||
45
internal/nvsandboxutils/api.go
Normal file
45
internal/nvsandboxutils/api.go
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
// libraryOptions hold the parameters than can be set by a LibraryOption
|
||||
type libraryOptions struct {
|
||||
path string
|
||||
flags int
|
||||
}
|
||||
|
||||
// LibraryOption represents a functional option to configure the underlying nvsandboxutils library
|
||||
type LibraryOption func(*libraryOptions)
|
||||
|
||||
// WithLibraryPath provides an option to set the library name to be used by the nvsandboxutils library.
|
||||
func WithLibraryPath(path string) LibraryOption {
|
||||
return func(o *libraryOptions) {
|
||||
o.path = path
|
||||
}
|
||||
}
|
||||
|
||||
// SetLibraryOptions applies the specified options to the nvsandboxutils library.
|
||||
// If this is called when a library is already loaded, an error is raised.
|
||||
func SetLibraryOptions(opts ...LibraryOption) error {
|
||||
libnvsandboxutils.Lock()
|
||||
defer libnvsandboxutils.Unlock()
|
||||
if libnvsandboxutils.refcount != 0 {
|
||||
return errLibraryAlreadyLoaded
|
||||
}
|
||||
libnvsandboxutils.init(opts...)
|
||||
return nil
|
||||
}
|
||||
25
internal/nvsandboxutils/cgo_helpers.h
Normal file
25
internal/nvsandboxutils/cgo_helpers.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
// WARNING: THIS FILE WAS AUTOMATICALLY GENERATED.
|
||||
// Code generated by https://git.io/c-for-go. DO NOT EDIT.
|
||||
|
||||
#include "nvsandboxutils.h"
|
||||
#include <stdlib.h>
|
||||
#pragma once
|
||||
|
||||
#define __CGOGEN 1
|
||||
|
||||
38
internal/nvsandboxutils/cgo_helpers_static.go
Normal file
38
internal/nvsandboxutils/cgo_helpers_static.go
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
var cgoAllocsUnknown = new(struct{})
|
||||
|
||||
func clen(n []byte) int {
|
||||
for i := 0; i < len(n); i++ {
|
||||
if n[i] == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(n)
|
||||
}
|
||||
|
||||
// Creates an int8 array of fixed input length to store the Go string.
|
||||
// TODO: Add error check if input string has a length greater than INPUT_LENGTH
|
||||
func convertStringToFixedArray(str string) [INPUT_LENGTH]int8 {
|
||||
var output [INPUT_LENGTH]int8
|
||||
for i, s := range str {
|
||||
output[i] = int8(s)
|
||||
}
|
||||
return output
|
||||
}
|
||||
156
internal/nvsandboxutils/const.go
Normal file
156
internal/nvsandboxutils/const.go
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
// WARNING: THIS FILE WAS AUTOMATICALLY GENERATED.
|
||||
// Code generated by https://git.io/c-for-go. DO NOT EDIT.
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
/*
|
||||
#cgo linux LDFLAGS: -Wl,--export-dynamic -Wl,--unresolved-symbols=ignore-in-object-files
|
||||
#cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup
|
||||
#include "nvsandboxutils.h"
|
||||
#include <stdlib.h>
|
||||
#include "cgo_helpers.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
// INPUT_LENGTH as defined in nvsandboxutils/nvsandboxutils.h
|
||||
INPUT_LENGTH = 256
|
||||
// MAX_FILE_PATH as defined in nvsandboxutils/nvsandboxutils.h
|
||||
MAX_FILE_PATH = 256
|
||||
// MAX_NAME_LENGTH as defined in nvsandboxutils/nvsandboxutils.h
|
||||
MAX_NAME_LENGTH = 256
|
||||
)
|
||||
|
||||
// Ret as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type Ret int32
|
||||
|
||||
// Ret enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
SUCCESS Ret = iota
|
||||
ERROR_UNINITIALIZED Ret = 1
|
||||
ERROR_NOT_SUPPORTED Ret = 2
|
||||
ERROR_INVALID_ARG Ret = 3
|
||||
ERROR_INSUFFICIENT_SIZE Ret = 4
|
||||
ERROR_VERSION_NOT_SUPPORTED Ret = 5
|
||||
ERROR_LIBRARY_LOAD Ret = 6
|
||||
ERROR_FUNCTION_NOT_FOUND Ret = 7
|
||||
ERROR_DEVICE_NOT_FOUND Ret = 8
|
||||
ERROR_NVML_LIB_CALL Ret = 9
|
||||
ERROR_OUT_OF_MEMORY Ret = 10
|
||||
ERROR_FILEPATH_NOT_FOUND Ret = 11
|
||||
ERROR_UNKNOWN Ret = 65535
|
||||
)
|
||||
|
||||
// LogLevel as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type LogLevel int32
|
||||
|
||||
// LogLevel enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
LOG_LEVEL_FATAL LogLevel = iota
|
||||
LOG_LEVEL_ERROR LogLevel = 1
|
||||
LOG_LEVEL_WARN LogLevel = 2
|
||||
LOG_LEVEL_DEBUG LogLevel = 3
|
||||
LOG_LEVEL_INFO LogLevel = 4
|
||||
LOG_LEVEL_NONE LogLevel = 65535
|
||||
)
|
||||
|
||||
// RootfsInputType as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type RootfsInputType int32
|
||||
|
||||
// RootfsInputType enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
NV_ROOTFS_DEFAULT RootfsInputType = iota
|
||||
NV_ROOTFS_PATH RootfsInputType = 1
|
||||
NV_ROOTFS_PID RootfsInputType = 2
|
||||
)
|
||||
|
||||
// FileType as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type FileType int32
|
||||
|
||||
// FileType enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
NV_DEV FileType = iota
|
||||
NV_PROC FileType = 1
|
||||
NV_SYS FileType = 2
|
||||
)
|
||||
|
||||
// FileSystemSubType as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type FileSystemSubType int32
|
||||
|
||||
// FileSystemSubType enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
NV_DEV_NVIDIA FileSystemSubType = iota
|
||||
NV_DEV_DRI_CARD FileSystemSubType = 1
|
||||
NV_DEV_DRI_RENDERD FileSystemSubType = 2
|
||||
NV_DEV_DRI_CARD_SYMLINK FileSystemSubType = 3
|
||||
NV_DEV_DRI_RENDERD_SYMLINK FileSystemSubType = 4
|
||||
NV_DEV_NVIDIA_UVM FileSystemSubType = 5
|
||||
NV_DEV_NVIDIA_UVM_TOOLS FileSystemSubType = 6
|
||||
NV_DEV_NVIDIA_MODESET FileSystemSubType = 7
|
||||
NV_DEV_NVIDIA_CTL FileSystemSubType = 8
|
||||
NV_DEV_GDRDRV FileSystemSubType = 9
|
||||
NV_DEV_NVIDIA_CAPS_NVIDIA_CAP FileSystemSubType = 10
|
||||
NV_PROC_DRIVER_NVIDIA_GPUS_PCIBUSID FileSystemSubType = 11
|
||||
NV_PROC_DRIVER_NVIDIA_GPUS FileSystemSubType = 12
|
||||
NV_PROC_NVIDIA_PARAMS FileSystemSubType = 13
|
||||
NV_PROC_NVIDIA_CAPS_MIG_MINORS FileSystemSubType = 14
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIES_GPU FileSystemSubType = 15
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIES FileSystemSubType = 16
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIIES_GPU_MIG_CI_ACCESS FileSystemSubType = 17
|
||||
NV_SYS_MODULE_NVIDIA_DRIVER_PCIBUSID FileSystemSubType = 18
|
||||
NV_SYS_MODULE_NVIDIA_DRIVER FileSystemSubType = 19
|
||||
NV_NUM_SUBTYPE FileSystemSubType = 20
|
||||
)
|
||||
|
||||
// FileModule as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type FileModule int32
|
||||
|
||||
// FileModule enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
NV_GPU FileModule = iota
|
||||
NV_MIG FileModule = 1
|
||||
NV_DRIVER_NVIDIA FileModule = 2
|
||||
NV_DRIVER_NVIDIA_UVM FileModule = 3
|
||||
NV_DRIVER_NVIDIA_MODESET FileModule = 4
|
||||
NV_DRIVER_GDRDRV FileModule = 5
|
||||
NV_SYSTEM FileModule = 6
|
||||
)
|
||||
|
||||
// FileFlag as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type FileFlag int32
|
||||
|
||||
// FileFlag enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
NV_FILE_FLAG_HINT FileFlag = 1
|
||||
NV_FILE_FLAG_MASKOUT FileFlag = 2
|
||||
NV_FILE_FLAG_CONTENT FileFlag = 4
|
||||
NV_FILE_FLAG_DEPRECTATED FileFlag = 8
|
||||
NV_FILE_FLAG_CANDIDATES FileFlag = 16
|
||||
)
|
||||
|
||||
// GpuInputType as declared in nvsandboxutils/nvsandboxutils.h
|
||||
type GpuInputType int32
|
||||
|
||||
// GpuInputType enumeration from nvsandboxutils/nvsandboxutils.h
|
||||
const (
|
||||
NV_GPU_INPUT_GPU_UUID GpuInputType = iota
|
||||
NV_GPU_INPUT_MIG_UUID GpuInputType = 1
|
||||
NV_GPU_INPUT_PCI_ID GpuInputType = 2
|
||||
NV_GPU_INPUT_PCI_INDEX GpuInputType = 3
|
||||
)
|
||||
23
internal/nvsandboxutils/doc.go
Normal file
23
internal/nvsandboxutils/doc.go
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
// WARNING: THIS FILE WAS AUTOMATICALLY GENERATED.
|
||||
// Code generated by https://git.io/c-for-go. DO NOT EDIT.
|
||||
|
||||
/*
|
||||
Package NVSANDBOXUTILS bindings
|
||||
*/
|
||||
package nvsandboxutils
|
||||
157
internal/nvsandboxutils/dynamicLibrary_mock.go
Normal file
157
internal/nvsandboxutils/dynamicLibrary_mock.go
Normal file
@@ -0,0 +1,157 @@
|
||||
// Code generated by moq; DO NOT EDIT.
|
||||
// github.com/matryer/moq
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ensure, that dynamicLibraryMock does implement dynamicLibrary.
|
||||
// If this is not the case, regenerate this file with moq.
|
||||
var _ dynamicLibrary = &dynamicLibraryMock{}
|
||||
|
||||
// dynamicLibraryMock is a mock implementation of dynamicLibrary.
|
||||
//
|
||||
// func TestSomethingThatUsesdynamicLibrary(t *testing.T) {
|
||||
//
|
||||
// // make and configure a mocked dynamicLibrary
|
||||
// mockeddynamicLibrary := &dynamicLibraryMock{
|
||||
// CloseFunc: func() error {
|
||||
// panic("mock out the Close method")
|
||||
// },
|
||||
// LookupFunc: func(s string) error {
|
||||
// panic("mock out the Lookup method")
|
||||
// },
|
||||
// OpenFunc: func() error {
|
||||
// panic("mock out the Open method")
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// // use mockeddynamicLibrary in code that requires dynamicLibrary
|
||||
// // and then make assertions.
|
||||
//
|
||||
// }
|
||||
type dynamicLibraryMock struct {
|
||||
// CloseFunc mocks the Close method.
|
||||
CloseFunc func() error
|
||||
|
||||
// LookupFunc mocks the Lookup method.
|
||||
LookupFunc func(s string) error
|
||||
|
||||
// OpenFunc mocks the Open method.
|
||||
OpenFunc func() error
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
// Close holds details about calls to the Close method.
|
||||
Close []struct {
|
||||
}
|
||||
// Lookup holds details about calls to the Lookup method.
|
||||
Lookup []struct {
|
||||
// S is the s argument value.
|
||||
S string
|
||||
}
|
||||
// Open holds details about calls to the Open method.
|
||||
Open []struct {
|
||||
}
|
||||
}
|
||||
lockClose sync.RWMutex
|
||||
lockLookup sync.RWMutex
|
||||
lockOpen sync.RWMutex
|
||||
}
|
||||
|
||||
// Close calls CloseFunc.
|
||||
func (mock *dynamicLibraryMock) Close() error {
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockClose.Lock()
|
||||
mock.calls.Close = append(mock.calls.Close, callInfo)
|
||||
mock.lockClose.Unlock()
|
||||
if mock.CloseFunc == nil {
|
||||
var (
|
||||
errOut error
|
||||
)
|
||||
return errOut
|
||||
}
|
||||
return mock.CloseFunc()
|
||||
}
|
||||
|
||||
// CloseCalls gets all the calls that were made to Close.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockeddynamicLibrary.CloseCalls())
|
||||
func (mock *dynamicLibraryMock) CloseCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockClose.RLock()
|
||||
calls = mock.calls.Close
|
||||
mock.lockClose.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Lookup calls LookupFunc.
|
||||
func (mock *dynamicLibraryMock) Lookup(s string) error {
|
||||
callInfo := struct {
|
||||
S string
|
||||
}{
|
||||
S: s,
|
||||
}
|
||||
mock.lockLookup.Lock()
|
||||
mock.calls.Lookup = append(mock.calls.Lookup, callInfo)
|
||||
mock.lockLookup.Unlock()
|
||||
if mock.LookupFunc == nil {
|
||||
var (
|
||||
errOut error
|
||||
)
|
||||
return errOut
|
||||
}
|
||||
return mock.LookupFunc(s)
|
||||
}
|
||||
|
||||
// LookupCalls gets all the calls that were made to Lookup.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockeddynamicLibrary.LookupCalls())
|
||||
func (mock *dynamicLibraryMock) LookupCalls() []struct {
|
||||
S string
|
||||
} {
|
||||
var calls []struct {
|
||||
S string
|
||||
}
|
||||
mock.lockLookup.RLock()
|
||||
calls = mock.calls.Lookup
|
||||
mock.lockLookup.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Open calls OpenFunc.
|
||||
func (mock *dynamicLibraryMock) Open() error {
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockOpen.Lock()
|
||||
mock.calls.Open = append(mock.calls.Open, callInfo)
|
||||
mock.lockOpen.Unlock()
|
||||
if mock.OpenFunc == nil {
|
||||
var (
|
||||
errOut error
|
||||
)
|
||||
return errOut
|
||||
}
|
||||
return mock.OpenFunc()
|
||||
}
|
||||
|
||||
// OpenCalls gets all the calls that were made to Open.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockeddynamicLibrary.OpenCalls())
|
||||
func (mock *dynamicLibraryMock) OpenCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockOpen.RLock()
|
||||
calls = mock.calls.Open
|
||||
mock.lockOpen.RUnlock()
|
||||
return calls
|
||||
}
|
||||
50
internal/nvsandboxutils/gen/generate-bindings.sh
Executable file
50
internal/nvsandboxutils/gen/generate-bindings.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file generates bindings for nvsandboxutils by calling c-for-go.
|
||||
|
||||
set -x -e
|
||||
|
||||
PWD=$(pwd)
|
||||
GEN_DIR="$PWD/gen"
|
||||
PKG_DIR="$PWD"
|
||||
GEN_BINDINGS_DIR="$GEN_DIR/nvsandboxutils"
|
||||
PKG_BINDINGS_DIR="$PKG_DIR"
|
||||
|
||||
SOURCES=$(find "$GEN_BINDINGS_DIR" -type f)
|
||||
|
||||
mkdir -p "$PKG_BINDINGS_DIR"
|
||||
|
||||
cp "$GEN_BINDINGS_DIR/nvsandboxutils.h" "$PKG_BINDINGS_DIR/nvsandboxutils.h"
|
||||
spatch --in-place --very-quiet --sp-file "$GEN_BINDINGS_DIR/anonymous_structs.cocci" "$PKG_BINDINGS_DIR/nvsandboxutils.h" > /dev/null
|
||||
|
||||
echo "Generating the bindings..."
|
||||
c-for-go -out "$PKG_DIR/.." "$GEN_BINDINGS_DIR/nvsandboxutils.yml"
|
||||
cd "$PKG_BINDINGS_DIR"
|
||||
go tool cgo -godefs types.go > types_gen.go
|
||||
go fmt types_gen.go
|
||||
cd - > /dev/null
|
||||
rm -rf "$PKG_BINDINGS_DIR/cgo_helpers.go" "$PKG_BINDINGS_DIR/types.go" "$PKG_BINDINGS_DIR/_obj"
|
||||
go run "$GEN_BINDINGS_DIR/generateapi.go" --sourceDir "$PKG_BINDINGS_DIR" --output "$PKG_BINDINGS_DIR/zz_generated.api.go"
|
||||
# go fmt "$PKG_BINDINGS_DIR"
|
||||
|
||||
SED_SEARCH_STRING='// WARNING: This file has automatically been generated on'
|
||||
SED_REPLACE_STRING='// WARNING: THIS FILE WAS AUTOMATICALLY GENERATED.'
|
||||
grep -l -R "$SED_SEARCH_STRING" "$PKG_DIR" | grep -v "/gen/" | xargs sed -i -E "s#$SED_SEARCH_STRING.*\$#$SED_REPLACE_STRING#g"
|
||||
|
||||
SED_SEARCH_STRING='// (.*) nvsandboxutils/nvsandboxutils.h:[0-9]+'
|
||||
SED_REPLACE_STRING='// \1 nvsandboxutils/nvsandboxutils.h'
|
||||
grep -l -RE "$SED_SEARCH_STRING" "$PKG_DIR" | grep -v "/gen/" | xargs sed -i -E "s#$SED_SEARCH_STRING\$#$SED_REPLACE_STRING#g"
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
@patch@
|
||||
type WRAPPER_TYPE;
|
||||
field list FIELDS;
|
||||
identifier V;
|
||||
expression E;
|
||||
fresh identifier ST = "nvSandboxUtilsGenerated_struct___";
|
||||
fresh identifier TEMP_VAR = "nvSandboxUtilsGenerated_variable___" ## V;
|
||||
@@
|
||||
|
||||
++ struct ST {
|
||||
++ WRAPPER_TYPE TEMP_VAR;
|
||||
++ FIELDS
|
||||
++ };
|
||||
+
|
||||
|
||||
WRAPPER_TYPE
|
||||
{
|
||||
...
|
||||
(
|
||||
- struct {
|
||||
- FIELDS
|
||||
- } V[E];
|
||||
+ struct ST V[E];
|
||||
|
||||
|
|
||||
|
||||
- struct {
|
||||
- FIELDS
|
||||
- } V;
|
||||
+ struct ST V;
|
||||
)
|
||||
...
|
||||
};
|
||||
|
||||
@capture@
|
||||
type WRAPPER_TYPE;
|
||||
identifier TEMP_VAR;
|
||||
identifier ST =~ "^nvSandboxUtilsGenerated_struct___";
|
||||
@@
|
||||
|
||||
struct ST {
|
||||
WRAPPER_TYPE TEMP_VAR;
|
||||
...
|
||||
};
|
||||
|
||||
@script:python concat@
|
||||
WRAPPER_TYPE << capture.WRAPPER_TYPE;
|
||||
TEMP_VAR << capture.TEMP_VAR;
|
||||
ST << capture.ST;
|
||||
T;
|
||||
@@
|
||||
|
||||
def removePrefix(string, prefix):
|
||||
if string.startswith(prefix):
|
||||
return string[len(prefix):]
|
||||
return string
|
||||
|
||||
def removeSuffix(string, suffix):
|
||||
if string.endswith(suffix):
|
||||
return string[:-len(suffix)]
|
||||
return string
|
||||
|
||||
WRAPPER_TYPE = removeSuffix(WRAPPER_TYPE, "_t")
|
||||
TEMP_VAR = removePrefix(TEMP_VAR, "nvSandboxUtilsGenerated_variable___")
|
||||
coccinelle.T = cocci.make_type(WRAPPER_TYPE + TEMP_VAR[0].upper() + TEMP_VAR[1:] + "_t")
|
||||
|
||||
@add_typedef@
|
||||
identifier capture.ST;
|
||||
type concat.T;
|
||||
type WRAPPER_TYPE;
|
||||
identifier TEMP_VAR;
|
||||
@@
|
||||
|
||||
- struct ST {
|
||||
+ typedef struct {
|
||||
- WRAPPER_TYPE TEMP_VAR;
|
||||
...
|
||||
- };
|
||||
+ } T;
|
||||
|
||||
@update@
|
||||
identifier capture.ST;
|
||||
type concat.T;
|
||||
identifier V;
|
||||
expression E;
|
||||
type WRAPPER_TYPE;
|
||||
@@
|
||||
|
||||
WRAPPER_TYPE
|
||||
{
|
||||
...
|
||||
(
|
||||
- struct ST V[E];
|
||||
+ T V[E];
|
||||
|
|
||||
- struct ST V;
|
||||
+ T V;
|
||||
)
|
||||
...
|
||||
};
|
||||
389
internal/nvsandboxutils/gen/nvsandboxutils/generateapi.go
Normal file
389
internal/nvsandboxutils/gen/nvsandboxutils/generateapi.go
Normal file
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type GeneratableInterfacePoperties struct {
|
||||
Type string
|
||||
Interface string
|
||||
Exclude []string
|
||||
PackageMethodsAliasedFrom string
|
||||
}
|
||||
|
||||
var GeneratableInterfaces = []GeneratableInterfacePoperties{
|
||||
{
|
||||
Type: "library",
|
||||
Interface: "Interface",
|
||||
PackageMethodsAliasedFrom: "libnvsandboxutils",
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
sourceDir := flag.String("sourceDir", "", "Path to the source directory for all go files")
|
||||
output := flag.String("output", "", "Path to the output file (default: stdout)")
|
||||
flag.Parse()
|
||||
|
||||
// Check if required flags are provided
|
||||
if *sourceDir == "" {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
writer, closer, err := getWriter(*output)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = closer()
|
||||
}()
|
||||
|
||||
header, err := generateHeader()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(writer, header)
|
||||
|
||||
for i, p := range GeneratableInterfaces {
|
||||
if p.PackageMethodsAliasedFrom != "" {
|
||||
comment, err := generatePackageMethodsComment(p)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(writer, comment)
|
||||
|
||||
output, err := generatePackageMethods(*sourceDir, p)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer, "%s\n", output)
|
||||
}
|
||||
|
||||
comment, err := generateInterfaceComment(p)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(writer, comment)
|
||||
|
||||
output, err := generateInterface(*sourceDir, p)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(writer, output)
|
||||
|
||||
if i < (len(GeneratableInterfaces) - 1) {
|
||||
fmt.Fprint(writer, "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getWriter(outputFile string) (io.Writer, func() error, error) {
|
||||
if outputFile == "" {
|
||||
return os.Stdout, func() error { return nil }, nil
|
||||
}
|
||||
|
||||
file, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return file, file.Close, nil
|
||||
}
|
||||
|
||||
func generateHeader() (string, error) {
|
||||
lines := []string{
|
||||
"/**",
|
||||
"# Copyright 2024 NVIDIA CORPORATION",
|
||||
"#",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");",
|
||||
"# you may not use this file except in compliance with the License.",
|
||||
"# You may obtain a copy of the License at",
|
||||
"#",
|
||||
"# http://www.apache.org/licenses/LICENSE-2.0",
|
||||
"#",
|
||||
"# Unless required by applicable law or agreed to in writing, software",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.",
|
||||
"# See the License for the specific language governing permissions and",
|
||||
"# limitations under the License.",
|
||||
"**/",
|
||||
"",
|
||||
"// Generated Code; DO NOT EDIT.",
|
||||
"",
|
||||
"package nvsandboxutils",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
return strings.Join(lines, "\n"), nil
|
||||
}
|
||||
|
||||
func generatePackageMethodsComment(input GeneratableInterfacePoperties) (string, error) {
|
||||
commentFmt := []string{
|
||||
"// The variables below represent package level methods from the %s type.",
|
||||
}
|
||||
|
||||
var signature strings.Builder
|
||||
comment := strings.Join(commentFmt, "\n")
|
||||
comment = fmt.Sprintf(comment, input.Type)
|
||||
signature.WriteString(fmt.Sprintf("%s\n", comment))
|
||||
return signature.String(), nil
|
||||
}
|
||||
|
||||
func generateInterfaceComment(input GeneratableInterfacePoperties) (string, error) {
|
||||
commentFmt := []string{
|
||||
"// %s represents the interface for the %s type.",
|
||||
"//",
|
||||
"//go:generate moq -out mock/%s.go -pkg mock . %s:%s",
|
||||
}
|
||||
|
||||
var signature strings.Builder
|
||||
comment := strings.Join(commentFmt, "\n")
|
||||
comment = fmt.Sprintf(comment, input.Interface, input.Type, strings.ToLower(input.Interface), input.Interface, input.Interface)
|
||||
signature.WriteString(fmt.Sprintf("%s\n", comment))
|
||||
return signature.String(), nil
|
||||
}
|
||||
|
||||
func generatePackageMethods(sourceDir string, input GeneratableInterfacePoperties) (string, error) {
|
||||
var signature strings.Builder
|
||||
|
||||
signature.WriteString("var (\n")
|
||||
|
||||
methods, err := extractMethodsFromPackage(sourceDir, input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
name := method.Name.Name
|
||||
formatted := fmt.Sprintf("\t%s = %s.%s\n", name, input.PackageMethodsAliasedFrom, name)
|
||||
signature.WriteString(formatted)
|
||||
}
|
||||
|
||||
signature.WriteString(")\n")
|
||||
|
||||
return signature.String(), nil
|
||||
}
|
||||
|
||||
func generateInterface(sourceDir string, input GeneratableInterfacePoperties) (string, error) {
|
||||
var signature strings.Builder
|
||||
|
||||
signature.WriteString(fmt.Sprintf("type %s interface {\n", input.Interface))
|
||||
|
||||
methods, err := extractMethodsFromPackage(sourceDir, input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
formatted := fmt.Sprintf("\t%s\n", formatMethodSignature(method))
|
||||
signature.WriteString(formatted)
|
||||
}
|
||||
|
||||
signature.WriteString("}\n")
|
||||
|
||||
return signature.String(), nil
|
||||
}
|
||||
|
||||
func getGoFiles(sourceDir string) (map[string][]byte, error) {
|
||||
gofiles := make(map[string][]byte)
|
||||
|
||||
err := filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() || filepath.Ext(path) != ".go" {
|
||||
return nil
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gofiles[path] = content
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("walking %s: %w", sourceDir, err)
|
||||
}
|
||||
|
||||
return gofiles, nil
|
||||
}
|
||||
|
||||
func extractMethodsFromPackage(sourceDir string, input GeneratableInterfacePoperties) ([]*ast.FuncDecl, error) {
|
||||
gofiles, err := getGoFiles(sourceDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var methods []*ast.FuncDecl
|
||||
for file, content := range gofiles {
|
||||
m, err := extractMethods(file, content, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
methods = append(methods, m...)
|
||||
}
|
||||
|
||||
sort.Slice(methods, func(i, j int) bool {
|
||||
return methods[i].Name.Name < methods[j].Name.Name
|
||||
})
|
||||
|
||||
return methods, nil
|
||||
}
|
||||
|
||||
func extractMethods(sourceFile string, sourceContent []byte, input GeneratableInterfacePoperties) ([]*ast.FuncDecl, error) {
|
||||
// Parse source file
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, sourceFile, sourceContent, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Traverse AST to find type declarations and associated methods
|
||||
var methods []*ast.FuncDecl
|
||||
for _, decl := range node.Decls {
|
||||
funcDecl, ok := decl.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the function is a method associated with the specified type
|
||||
if receiverType := funcDecl.Recv; receiverType != nil {
|
||||
var ident *ast.Ident
|
||||
|
||||
for _, field := range receiverType.List {
|
||||
switch fieldType := field.Type.(type) {
|
||||
case *ast.Ident:
|
||||
ident = fieldType
|
||||
case *ast.StarExpr:
|
||||
// Update ident if it's a *ast.StarExpr
|
||||
if newIdent, ok := fieldType.X.(*ast.Ident); ok {
|
||||
// If the inner type is an *ast.Ident, update ident
|
||||
ident = newIdent
|
||||
}
|
||||
}
|
||||
|
||||
// No identifier found
|
||||
if ident == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Identifier is not the one we are looking for
|
||||
if ident.Name != input.Type {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore non-public methods
|
||||
if !isPublic(funcDecl.Name.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore method in the exclude list
|
||||
if slices.Contains(input.Exclude, funcDecl.Name.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
methods = append(methods, funcDecl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return methods, nil
|
||||
}
|
||||
|
||||
func formatMethodSignature(decl *ast.FuncDecl) string {
|
||||
var signature strings.Builder
|
||||
|
||||
// Write method name
|
||||
signature.WriteString(decl.Name.Name)
|
||||
signature.WriteString("(")
|
||||
|
||||
// Write parameters
|
||||
if decl.Type.Params != nil {
|
||||
for i, param := range decl.Type.Params.List {
|
||||
if i > 0 {
|
||||
signature.WriteString(", ")
|
||||
}
|
||||
signature.WriteString(formatFieldList(param))
|
||||
}
|
||||
}
|
||||
|
||||
signature.WriteString(")")
|
||||
|
||||
// Write return types
|
||||
if decl.Type.Results != nil {
|
||||
signature.WriteString(" ")
|
||||
if len(decl.Type.Results.List) > 1 {
|
||||
signature.WriteString("(")
|
||||
}
|
||||
for i, result := range decl.Type.Results.List {
|
||||
if i > 0 {
|
||||
signature.WriteString(", ")
|
||||
}
|
||||
signature.WriteString(formatFieldList(result))
|
||||
}
|
||||
if len(decl.Type.Results.List) > 1 {
|
||||
signature.WriteString(")")
|
||||
}
|
||||
}
|
||||
|
||||
return signature.String()
|
||||
}
|
||||
|
||||
func formatFieldList(field *ast.Field) string {
|
||||
var builder strings.Builder
|
||||
switch fieldType := field.Type.(type) {
|
||||
case *ast.Ident:
|
||||
builder.WriteString(fieldType.Name)
|
||||
case *ast.ArrayType:
|
||||
builder.WriteString("[]")
|
||||
builder.WriteString(formatFieldList(&ast.Field{Type: fieldType.Elt}))
|
||||
case *ast.StarExpr:
|
||||
builder.WriteString("*")
|
||||
builder.WriteString(formatFieldList(&ast.Field{Type: fieldType.X}))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func isPublic(name string) bool {
|
||||
if len(name) == 0 {
|
||||
return false
|
||||
}
|
||||
return unicode.IsUpper([]rune(name)[0])
|
||||
}
|
||||
298
internal/nvsandboxutils/gen/nvsandboxutils/nvsandboxutils.h
Normal file
298
internal/nvsandboxutils/gen/nvsandboxutils/nvsandboxutils.h
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 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.
|
||||
*/
|
||||
|
||||
#ifndef __NVSANDBOXUTILS_H__
|
||||
#define __NVSANDBOXUTILS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INPUT_LENGTH 256
|
||||
#define MAX_FILE_PATH 256
|
||||
#define MAX_NAME_LENGTH 256
|
||||
|
||||
/***************************************************************************************************/
|
||||
/** @defgroup enums Enumerations
|
||||
* @{
|
||||
*/
|
||||
/***************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Return types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NVSANDBOXUTILS_SUCCESS = 0, //!< The operation was successful
|
||||
NVSANDBOXUTILS_ERROR_UNINITIALIZED = 1, //!< The library wasn't successfully initialized
|
||||
NVSANDBOXUTILS_ERROR_NOT_SUPPORTED = 2, //!< The requested operation is not supported on target device
|
||||
NVSANDBOXUTILS_ERROR_INVALID_ARG = 3, //!< A supplied argument is invalid
|
||||
NVSANDBOXUTILS_ERROR_INSUFFICIENT_SIZE = 4, //!< A supplied argument is not large enough
|
||||
NVSANDBOXUTILS_ERROR_VERSION_NOT_SUPPORTED = 5, //!< Requested library version is not supported
|
||||
NVSANDBOXUTILS_ERROR_LIBRARY_LOAD = 6, //!< The library load failed
|
||||
NVSANDBOXUTILS_ERROR_FUNCTION_NOT_FOUND = 7, //!< Called function was not found
|
||||
NVSANDBOXUTILS_ERROR_DEVICE_NOT_FOUND = 8, //!< Target device was not found
|
||||
NVSANDBOXUTILS_ERROR_NVML_LIB_CALL = 9, //!< NVML library call failed
|
||||
NVSANDBOXUTILS_ERROR_OUT_OF_MEMORY = 10, //!< There is insufficient memory
|
||||
NVSANDBOXUTILS_ERROR_FILEPATH_NOT_FOUND = 11, //!< A supplied file path was not found
|
||||
NVSANDBOXUTILS_ERROR_UNKNOWN = 0xFFFF, //!< Unknown error occurred
|
||||
} nvSandboxUtilsRet_t;
|
||||
|
||||
/**
|
||||
* Return if there is an error
|
||||
*/
|
||||
#define RETURN_ON_SANDBOX_ERROR(result) \
|
||||
if ((result) != NVSANDBOXUTILS_SUCCESS) { \
|
||||
NVSANDBOXUTILS_ERROR_MSG("%s %d result=%d", __func__, __LINE__, result); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NVSANDBOXUTILS_LOG_LEVEL_FATAL = 0, //!< Log fatal errors
|
||||
NVSANDBOXUTILS_LOG_LEVEL_ERROR = 1, //!< Log all errors
|
||||
NVSANDBOXUTILS_LOG_LEVEL_WARN = 2, //!< Log all warnings
|
||||
NVSANDBOXUTILS_LOG_LEVEL_DEBUG = 3, //!< Log all debug messages
|
||||
NVSANDBOXUTILS_LOG_LEVEL_INFO = 4, //!< Log all info messages
|
||||
NVSANDBOXUTILS_LOG_LEVEL_NONE = 0xFFFF, //!< Log none
|
||||
} nvSandboxUtilsLogLevel_t;
|
||||
|
||||
/**
|
||||
* Input rootfs to help access files inside the driver container
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_ROOTFS_DEFAULT, //!< Default no rootfs
|
||||
NV_ROOTFS_PATH, //!< /run/nvidia/driver
|
||||
NV_ROOTFS_PID, //!< /proc/PID/mountinfo
|
||||
} nvSandboxUtilsRootfsInputType_t;
|
||||
|
||||
/**
|
||||
* File type
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_DEV, //!< /dev file system
|
||||
NV_PROC, //!< /proc file system
|
||||
NV_SYS, //!< /sys file system
|
||||
} nvSandboxUtilsFileType_t;
|
||||
|
||||
/**
|
||||
* File subtype
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_DEV_NVIDIA, //!< /dev/nvidia0
|
||||
NV_DEV_DRI_CARD, //!< /dev/dri/card1
|
||||
NV_DEV_DRI_RENDERD, //!< /dev/dri/renderD128
|
||||
NV_DEV_DRI_CARD_SYMLINK, //!< /dev/dri/by-path/pci-0000:41:00.0-card
|
||||
NV_DEV_DRI_RENDERD_SYMLINK, //!< /dev/dri/by-path/pci-0000:41:00.0-render
|
||||
NV_DEV_NVIDIA_UVM, //!< /dev/nvidia-uvm
|
||||
NV_DEV_NVIDIA_UVM_TOOLS, //!< /dev/nvidia-uvm-tools
|
||||
NV_DEV_NVIDIA_MODESET, //!< /dev/nvidia-uvm-modeset
|
||||
NV_DEV_NVIDIA_CTL, //!< /dev/nvidiactl
|
||||
NV_DEV_GDRDRV, //!< /dev/gdrdrv
|
||||
NV_DEV_NVIDIA_CAPS_NVIDIA_CAP, //!< /dev/nvidia-caps/nvidia-cap22
|
||||
NV_PROC_DRIVER_NVIDIA_GPUS_PCIBUSID, //!< /proc/driver/nvidia/gpus/0000:2d:00.0
|
||||
NV_PROC_DRIVER_NVIDIA_GPUS, //!< /proc/driver/nvidia/gpus (for mask out)
|
||||
NV_PROC_NVIDIA_PARAMS, //!< /proc/driver/nvidia/params
|
||||
NV_PROC_NVIDIA_CAPS_MIG_MINORS, //!< /proc/driver/nvidia-caps/mig-minors
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIES_GPU, //!< /proc/driver/nvidia/capabilities/gpu0
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIES, //!< /proc/driver/nvidia/capabilities (for mask out)
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIIES_GPU_MIG_CI_ACCESS, //!< proc/driver/nvidia/capabilities/gpu0/mig/gi2/ci0/access
|
||||
NV_SYS_MODULE_NVIDIA_DRIVER_PCIBUSID, //!< /sys/module/nvidia/drivers/pci:nvidia/0000:2d:00.0
|
||||
NV_SYS_MODULE_NVIDIA_DRIVER, //!< /sys/module/nvidia/drivers/pci:nvidia (for mask out)
|
||||
NV_NUM_SUBTYPE, // always at the end.
|
||||
} nvSandboxUtilsFileSystemSubType_t;
|
||||
|
||||
/**
|
||||
* File module
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_GPU, //!< Target device
|
||||
NV_MIG, //!< Target device- MIG
|
||||
NV_DRIVER_NVIDIA, //!< NVIDIA kernel driver
|
||||
NV_DRIVER_NVIDIA_UVM, //!< NVIDIA kernel driver-UVM
|
||||
NV_DRIVER_NVIDIA_MODESET, //!< NVIDIA kernel driver-modeset
|
||||
NV_DRIVER_GDRDRV, //!< GDRDRV driver
|
||||
NV_SYSTEM, //!< System module
|
||||
} nvSandboxUtilsFileModule_t;
|
||||
|
||||
/**
|
||||
* Flag to provide additional details about the file
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_FILE_FLAG_HINT = (1 << 0), //!< Default no hint
|
||||
NV_FILE_FLAG_MASKOUT = (1 << 1), //!< For /proc/driver/nvidia/gpus
|
||||
NV_FILE_FLAG_CONTENT = (1 << 2), //!< For /proc/driver/nvidia/params
|
||||
//!< For SYMLINK
|
||||
//!< Use \p nvSandboxUtilsGetFileContent to get name of the linked file
|
||||
NV_FILE_FLAG_DEPRECTATED = (1 << 3), //!< For all the FIRMWARE GSP file
|
||||
NV_FILE_FLAG_CANDIDATES = (1 << 4), //!< For libcuda.so
|
||||
} nvSandboxUtilsFileFlag_t;
|
||||
|
||||
/**
|
||||
* Input type of the target device
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_GPU_INPUT_GPU_UUID, //!< GPU UUID
|
||||
NV_GPU_INPUT_MIG_UUID, //!< MIG UUID
|
||||
NV_GPU_INPUT_PCI_ID, //!< PCIe DBDF ID
|
||||
NV_GPU_INPUT_PCI_INDEX, //!< PCIe bus order (0 points to the GPU that has lowest PCIe BDF)
|
||||
} nvSandboxUtilsGpuInputType_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
/***************************************************************************************************/
|
||||
/** @defgroup dataTypes Structures and Unions
|
||||
* @{
|
||||
*/
|
||||
/***************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Initalization input v1
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version; //!< Version for the structure
|
||||
nvSandboxUtilsRootfsInputType_t type; //!< One of \p nvSandboxUtilsRootfsInputType_t
|
||||
char value[INPUT_LENGTH]; //!< String representation of input
|
||||
} nvSandboxUtilsInitInput_v1_t;
|
||||
|
||||
typedef nvSandboxUtilsInitInput_v1_t nvSandboxUtilsInitInput_t;
|
||||
|
||||
/**
|
||||
* File system information
|
||||
*/
|
||||
typedef struct nvSandboxUtilsGpuFileInfo_v1_t
|
||||
{
|
||||
struct nvSandboxUtilsGpuFileInfo_v1_t *next; //!< Pointer to the next node in the linked list
|
||||
nvSandboxUtilsFileType_t fileType; //!< One of \p nvSandboxUtilsFileType_t
|
||||
nvSandboxUtilsFileSystemSubType_t fileSubType; //!< One of \p nvSandboxUtilsFileSystemSubType_t
|
||||
nvSandboxUtilsFileModule_t module; //!< One of \p nvSandboxUtilsFileModule_t
|
||||
nvSandboxUtilsFileFlag_t flags; //!< One of \p nvSandboxUtilsFileFlag_t
|
||||
char *filePath; //!< Relative file path to rootfs
|
||||
}nvSandboxUtilsGpuFileInfo_v1_t;
|
||||
|
||||
/**
|
||||
* GPU resource request v1
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version; //!< Version for the structure
|
||||
nvSandboxUtilsGpuInputType_t inputType; //!< One of \p nvSandboxUtilsGpuInputType_t
|
||||
char input[INPUT_LENGTH]; //!< String representation of input
|
||||
nvSandboxUtilsGpuFileInfo_v1_t *files; //!< Linked list of \ref nvSandboxUtilsGpuFileInfo_v1_t
|
||||
} nvSandboxUtilsGpuRes_v1_t;
|
||||
|
||||
typedef nvSandboxUtilsGpuRes_v1_t nvSandboxUtilsGpuRes_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
/***************************************************************************************************/
|
||||
/** @defgroup funcs Functions
|
||||
* @{
|
||||
*/
|
||||
/***************************************************************************************************/
|
||||
|
||||
/* *************************************************
|
||||
* Initialize library
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Prepare library resources before library API can be used.
|
||||
* This initialization will not fail if one of the initialization prerequisites fails.
|
||||
* @param input Reference to the called-supplied input struct that has initialization fields
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p input->value isn't a valid rootfs path
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_VERSION_NOT_SUPPORTED if \p input->version isn't supported by the library
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_FILEPATH_NOT_FOUND if any of the required file paths are not found during initialization
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_OUT_OF_MEMORY if there is insufficient system memory during initialization
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_LIBRARY_LOAD on any error during loading the library
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsInit(nvSandboxUtilsInitInput_t *input);
|
||||
|
||||
/* *************************************************
|
||||
* Shutdown library
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Clean up library resources created by init call
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsShutdown(void);
|
||||
|
||||
/* *************************************************
|
||||
* Get NVIDIA RM driver version
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Get NVIDIA RM driver version
|
||||
* @param version Reference to caller-supplied buffer to return driver version string
|
||||
* @param length The maximum allowed length of the string returned in \p version
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p version is NULL
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_NVML_LIB_CALL on any error during driver version query from NVML
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsGetDriverVersion(char *version, unsigned int length);
|
||||
|
||||
/* *************************************************
|
||||
* Get /dev, /proc, /sys file system information
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Get /dev, /proc, /sys file system information
|
||||
* @param request Reference to caller-supplied request struct to return the file system information
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p request->input doesn't match any device
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_VERSION_NOT_SUPPORTED if \p request->version isn't supported by the library
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsGetGpuResource(nvSandboxUtilsGpuRes_t *request);
|
||||
|
||||
/* *************************************************
|
||||
* Get content of given file path
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Get file content of input file path
|
||||
* @param filePath Reference to the file path
|
||||
* @param content Reference to the caller-supplied buffer to return the file content
|
||||
* @param contentSize Reference to the maximum allowed size of content. It is updated to the actual size of the content on return
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p filePath or \p content is NULL
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INSUFFICIENT_SIZE if \p contentSize is too small
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_FILEPATH_NOT_FOUND on an error while obtaining the content for the file path
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsGetFileContent(char *filePath, char *content, unsigned int *contentSize);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __NVSANDBOXUTILS_H__
|
||||
@@ -0,0 +1,66 @@
|
||||
# Copyright (c) 2024, NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
---
|
||||
GENERATOR:
|
||||
PackageName: nvsandboxutils
|
||||
PackageDescription: "Package NVSANDBOXUTILS bindings"
|
||||
PackageLicense: |-
|
||||
Copyright (c) 2024, NVIDIA CORPORATION
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Includes: ["nvsandboxutils.h"]
|
||||
FlagGroups:
|
||||
- {name: "LDFLAGS", traits: ["linux"], flags: ["-Wl,--export-dynamic","-Wl,--unresolved-symbols=ignore-in-object-files"]}
|
||||
- {name: "LDFLAGS", traits: ["darwin"], flags: ["-Wl,-undefined,dynamic_lookup"]}
|
||||
PARSER:
|
||||
SourcesPaths: ["nvsandboxutils.h"]
|
||||
TRANSLATOR:
|
||||
ConstRules:
|
||||
defines: eval
|
||||
enum: eval
|
||||
PtrTips:
|
||||
function:
|
||||
- {target: "^nvSandboxUtils", default: "sref"}
|
||||
MemTips:
|
||||
- {target: "^nvSandboxUtils", default: "raw"}
|
||||
Rules:
|
||||
const:
|
||||
- {action: accept, from: "^NVSANDBOXUTILS_"}
|
||||
- {action: accept, from: "^nvSandboxUtils"}
|
||||
- {action: replace, from: "^NVSANDBOXUTILS_"}
|
||||
- {action: replace, from: "^nvSandboxUtils"}
|
||||
- {action: accept, from: "^NV"}
|
||||
- {action: accept, from: "^MAX"}
|
||||
- {action: accept, from: "^INPUT"}
|
||||
- {action: replace, from: "_t$"}
|
||||
- {transform: export}
|
||||
type:
|
||||
- {action: accept, from: "^nvSandboxUtils"}
|
||||
- {action: replace, from: "^nvSandboxUtils"}
|
||||
- {action: replace, from: "_t$"}
|
||||
- {transform: export}
|
||||
function:
|
||||
- {action: accept, from: "^nvSandboxUtils"}
|
||||
- {transform: unexport}
|
||||
41
internal/nvsandboxutils/gen/update-bindings.sh
Executable file
41
internal/nvsandboxutils/gen/update-bindings.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file allows for the nvsandboxutils bindings to be updated using the tooling
|
||||
# implemented in https://github.com/NVIDIA/go-nvml.
|
||||
# To run this:
|
||||
# cd internal/nvsandboxutils
|
||||
# ./update-bindings.sh
|
||||
|
||||
set -e
|
||||
|
||||
BUILDIMAGE=bindings
|
||||
|
||||
docker build \
|
||||
--build-arg GOLANG_VERSION=1.22.1 \
|
||||
--build-arg C_FOR_GO_TAG=8eeee8c3b71f9c3c90c4a73db54ed08b0bba971d \
|
||||
-t ${BUILDIMAGE} \
|
||||
-f docker/Dockerfile.devel \
|
||||
https://github.com/NVIDIA/go-nvml.git
|
||||
|
||||
|
||||
docker run --rm -ti \
|
||||
-e GOCACHE=/tmp/.cache/go \
|
||||
-e GOMODCACHE=/tmp/.cache/gomod \
|
||||
-v $(pwd):/nvsandboxutils \
|
||||
-w /nvsandboxutils \
|
||||
-u $(id -u):$(id -g) \
|
||||
${BUILDIMAGE} \
|
||||
./gen/generate-bindings.sh
|
||||
67
internal/nvsandboxutils/gpu-resources.go
Normal file
67
internal/nvsandboxutils/gpu-resources.go
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
import "C"
|
||||
|
||||
type GpuResource struct {
|
||||
Version uint32
|
||||
}
|
||||
|
||||
type GpuFileInfo struct {
|
||||
Path string
|
||||
Type FileType
|
||||
SubType FileSystemSubType
|
||||
Module FileModule
|
||||
Flags FileFlag
|
||||
}
|
||||
|
||||
func (l *library) GetGpuResource(uuid string) ([]GpuFileInfo, Ret) {
|
||||
deviceType := NV_GPU_INPUT_GPU_UUID
|
||||
if strings.HasPrefix(uuid, "MIG-") {
|
||||
deviceType = NV_GPU_INPUT_MIG_UUID
|
||||
}
|
||||
|
||||
request := GpuRes{
|
||||
Version: 1,
|
||||
InputType: uint32(deviceType),
|
||||
Input: convertStringToFixedArray(uuid),
|
||||
}
|
||||
|
||||
ret := nvSandboxUtilsGetGpuResource(&request)
|
||||
if ret != SUCCESS {
|
||||
return nil, ret
|
||||
}
|
||||
|
||||
var fileInfos []GpuFileInfo
|
||||
for fileInfo := request.Files; fileInfo != nil; fileInfo = fileInfo.Next {
|
||||
fi := GpuFileInfo{
|
||||
Path: C.GoString((*C.char)(unsafe.Pointer(fileInfo.FilePath))),
|
||||
Type: FileType(fileInfo.FileType),
|
||||
SubType: FileSystemSubType(fileInfo.FileSubType),
|
||||
Module: FileModule(fileInfo.Module),
|
||||
Flags: FileFlag(fileInfo.Flags),
|
||||
}
|
||||
fileInfos = append(fileInfos, fi)
|
||||
}
|
||||
return fileInfos, SUCCESS
|
||||
}
|
||||
64
internal/nvsandboxutils/impl.go
Normal file
64
internal/nvsandboxutils/impl.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import "C"
|
||||
|
||||
func (l *library) Init(path string) Ret {
|
||||
if err := l.load(); err != nil {
|
||||
return ERROR_LIBRARY_LOAD
|
||||
}
|
||||
|
||||
input := InitInput{
|
||||
Version: 1,
|
||||
Type: uint32(NV_ROOTFS_PATH),
|
||||
Value: convertStringToFixedArray(path),
|
||||
}
|
||||
|
||||
return nvSandboxUtilsInit(&input)
|
||||
}
|
||||
|
||||
func (l *library) Shutdown() Ret {
|
||||
ret := nvSandboxUtilsShutdown()
|
||||
if ret != SUCCESS {
|
||||
return ret
|
||||
}
|
||||
|
||||
err := l.close()
|
||||
if err != nil {
|
||||
return ERROR_UNKNOWN
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// TODO: Is this length specified in the header file?
|
||||
const VERSION_LENGTH = 100
|
||||
|
||||
func (l *library) GetDriverVersion() (string, Ret) {
|
||||
Version := make([]byte, VERSION_LENGTH)
|
||||
ret := nvSandboxUtilsGetDriverVersion(&Version[0], VERSION_LENGTH)
|
||||
return string(Version[:clen(Version)]), ret
|
||||
}
|
||||
|
||||
func (l *library) GetFileContent(path string) (string, Ret) {
|
||||
Content := make([]byte, MAX_FILE_PATH)
|
||||
FilePath := []byte(path + string(byte(0)))
|
||||
Size := uint32(MAX_FILE_PATH)
|
||||
ret := nvSandboxUtilsGetFileContent(&FilePath[0], &Content[0], &Size)
|
||||
return string(Content[:clen(Content)]), ret
|
||||
}
|
||||
156
internal/nvsandboxutils/lib.go
Normal file
156
internal/nvsandboxutils/lib.go
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/NVIDIA/go-nvml/pkg/dl"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultNvSandboxUtilsLibraryName = "libnvidia-sandboxutils.so.1"
|
||||
defaultNvSandboxUtilsLibraryLoadFlags = dl.RTLD_LAZY | dl.RTLD_GLOBAL
|
||||
)
|
||||
|
||||
var errLibraryNotLoaded = errors.New("library not loaded")
|
||||
var errLibraryAlreadyLoaded = errors.New("library already loaded")
|
||||
|
||||
// dynamicLibrary is an interface for abstacting the underlying library.
|
||||
// This also allows for mocking and testing.
|
||||
|
||||
//go:generate moq -rm -stub -out dynamicLibrary_mock.go . dynamicLibrary
|
||||
type dynamicLibrary interface {
|
||||
Lookup(string) error
|
||||
Open() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// library represents an nvsandboxutils library.
|
||||
// This includes a reference to the underlying DynamicLibrary
|
||||
type library struct {
|
||||
sync.Mutex
|
||||
path string
|
||||
refcount refcount
|
||||
dl dynamicLibrary
|
||||
}
|
||||
|
||||
// libnvsandboxutils is a global instance of the nvsandboxutils library.
|
||||
var libnvsandboxutils = newLibrary()
|
||||
|
||||
func New(opts ...LibraryOption) Interface {
|
||||
return newLibrary(opts...)
|
||||
}
|
||||
|
||||
func newLibrary(opts ...LibraryOption) *library {
|
||||
l := &library{}
|
||||
l.init(opts...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *library) init(opts ...LibraryOption) {
|
||||
o := libraryOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(&o)
|
||||
}
|
||||
|
||||
if o.path == "" {
|
||||
o.path = defaultNvSandboxUtilsLibraryName
|
||||
}
|
||||
if o.flags == 0 {
|
||||
o.flags = defaultNvSandboxUtilsLibraryLoadFlags
|
||||
}
|
||||
|
||||
l.path = o.path
|
||||
l.dl = dl.New(o.path, o.flags)
|
||||
}
|
||||
|
||||
// LookupSymbol checks whether the specified library symbol exists in the library.
|
||||
// Note that this requires that the library be loaded.
|
||||
func (l *library) LookupSymbol(name string) error {
|
||||
if l == nil || l.refcount == 0 {
|
||||
return fmt.Errorf("error looking up %s: %w", name, errLibraryNotLoaded)
|
||||
}
|
||||
return l.dl.Lookup(name)
|
||||
}
|
||||
|
||||
// load initializes the library and updates the versioned symbols.
|
||||
// Multiple calls to an already loaded library will return without error.
|
||||
func (l *library) load() (rerr error) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
defer func() { l.refcount.IncOnNoError(rerr) }()
|
||||
if l.refcount > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := l.dl.Open(); err != nil {
|
||||
return fmt.Errorf("error opening %s: %w", l.path, err)
|
||||
}
|
||||
|
||||
// Update the errorStringFunc to point to nvsandboxutils.ErrorString
|
||||
errorStringFunc = nvsanboxutilsErrorString
|
||||
|
||||
// Update all versioned symbols
|
||||
l.updateVersionedSymbols()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// close the underlying library and ensure that the global pointer to the
|
||||
// library is set to nil to ensure that subsequent calls to open will reinitialize it.
|
||||
// Multiple calls to an already closed nvsandboxutils library will return without error.
|
||||
func (l *library) close() (rerr error) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
defer func() { l.refcount.DecOnNoError(rerr) }()
|
||||
if l.refcount != 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := l.dl.Close(); err != nil {
|
||||
return fmt.Errorf("error closing %s: %w", l.path, err)
|
||||
}
|
||||
|
||||
// Update the errorStringFunc to point to defaultErrorStringFunc
|
||||
errorStringFunc = defaultErrorStringFunc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Default all versioned APIs to v1 (to infer the types)
|
||||
var (
|
||||
// Insert default versions for APIs here.
|
||||
// Example:
|
||||
// nvsandboxUtilsFunction = nvsandboxUtilsFunction_v1
|
||||
)
|
||||
|
||||
// updateVersionedSymbols checks for versioned symbols in the loaded dynamic library.
|
||||
// If newer versioned symbols exist, these replace the default `v1` symbols initialized above.
|
||||
// When new versioned symbols are added, these would have to be initialized above and have
|
||||
// corresponding checks and subsequent assignments added below.
|
||||
func (l *library) updateVersionedSymbols() {
|
||||
// Example:
|
||||
// err := l.dl.Lookup("nvsandboxUtilsFunction_v2")
|
||||
// if err == nil {
|
||||
// nvsandboxUtilsFunction = nvsandboxUtilsFunction_v2
|
||||
// }
|
||||
}
|
||||
245
internal/nvsandboxutils/lib_test.go
Normal file
245
internal/nvsandboxutils/lib_test.go
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newTestLibrary(dl dynamicLibrary) *library {
|
||||
return &library{dl: dl}
|
||||
}
|
||||
|
||||
func TestLookupFromDefault(t *testing.T) {
|
||||
errClose := errors.New("close error")
|
||||
errOpen := errors.New("open error")
|
||||
errLookup := errors.New("lookup error")
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
dl dynamicLibrary
|
||||
skipLoadLibrary bool
|
||||
expectedLoadError error
|
||||
expectedLookupErrror error
|
||||
expectedCloseError error
|
||||
}{
|
||||
{
|
||||
description: "library not loaded yields error",
|
||||
dl: &dynamicLibraryMock{},
|
||||
skipLoadLibrary: true,
|
||||
expectedLookupErrror: errLibraryNotLoaded,
|
||||
},
|
||||
{
|
||||
description: "open error is returned",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return errOpen
|
||||
},
|
||||
},
|
||||
|
||||
expectedLoadError: errOpen,
|
||||
expectedLookupErrror: errLibraryNotLoaded,
|
||||
},
|
||||
{
|
||||
description: "lookup error is returned",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
LookupFunc: func(s string) error {
|
||||
return fmt.Errorf("%w: %s", errLookup, s)
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
expectedLookupErrror: errLookup,
|
||||
},
|
||||
{
|
||||
description: "lookup succeeds",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
LookupFunc: func(s string) error {
|
||||
return nil
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "lookup succeeds",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
LookupFunc: func(s string) error {
|
||||
return nil
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "close error is returned",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
LookupFunc: func(s string) error {
|
||||
return nil
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return errClose
|
||||
},
|
||||
},
|
||||
expectedCloseError: errClose,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
l := newTestLibrary(tc.dl)
|
||||
if !tc.skipLoadLibrary {
|
||||
require.ErrorIs(t, l.load(), tc.expectedLoadError)
|
||||
}
|
||||
require.ErrorIs(t, l.LookupSymbol("symbol"), tc.expectedLookupErrror)
|
||||
require.ErrorIs(t, l.close(), tc.expectedCloseError)
|
||||
if tc.expectedCloseError == nil {
|
||||
require.Equal(t, 0, int(l.refcount))
|
||||
} else {
|
||||
require.Equal(t, 1, int(l.refcount))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAndCloseNesting(t *testing.T) {
|
||||
dl := &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
l := newTestLibrary(dl)
|
||||
|
||||
// When calling close before opening the library nothing happens.
|
||||
require.Equal(t, 0, len(dl.calls.Close))
|
||||
require.Nil(t, l.close())
|
||||
require.Equal(t, 0, len(dl.calls.Close))
|
||||
|
||||
// When calling load twice, the library was only opened once
|
||||
require.Equal(t, 0, len(dl.calls.Open))
|
||||
require.Nil(t, l.load())
|
||||
require.Equal(t, 1, len(dl.calls.Open))
|
||||
require.Nil(t, l.load())
|
||||
require.Equal(t, 1, len(dl.calls.Open))
|
||||
|
||||
// Only after calling close twice, was the library closed
|
||||
require.Equal(t, 0, len(dl.calls.Close))
|
||||
require.Nil(t, l.close())
|
||||
require.Equal(t, 0, len(dl.calls.Close))
|
||||
require.Nil(t, l.close())
|
||||
require.Equal(t, 1, len(dl.calls.Close))
|
||||
|
||||
// Calling close again doesn't attempt to close the library again
|
||||
require.Nil(t, l.close())
|
||||
require.Equal(t, 1, len(dl.calls.Close))
|
||||
}
|
||||
|
||||
func TestLoadAndCloseWithErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
dl dynamicLibrary
|
||||
expectedLoadRefcount refcount
|
||||
expectedCloseRefcount refcount
|
||||
}{
|
||||
{
|
||||
description: "regular flow",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
expectedLoadRefcount: 1,
|
||||
expectedCloseRefcount: 0,
|
||||
},
|
||||
{
|
||||
description: "open error",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return errors.New("")
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
expectedLoadRefcount: 0,
|
||||
expectedCloseRefcount: 0,
|
||||
},
|
||||
{
|
||||
description: "close error",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return nil
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return errors.New("")
|
||||
},
|
||||
},
|
||||
expectedLoadRefcount: 1,
|
||||
expectedCloseRefcount: 1,
|
||||
},
|
||||
{
|
||||
description: "open and close error",
|
||||
dl: &dynamicLibraryMock{
|
||||
OpenFunc: func() error {
|
||||
return errors.New("")
|
||||
},
|
||||
CloseFunc: func() error {
|
||||
return errors.New("")
|
||||
},
|
||||
},
|
||||
expectedLoadRefcount: 0,
|
||||
expectedCloseRefcount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
l := newTestLibrary(tc.dl)
|
||||
_ = l.load()
|
||||
require.Equal(t, tc.expectedLoadRefcount, l.refcount)
|
||||
_ = l.close()
|
||||
require.Equal(t, tc.expectedCloseRefcount, l.refcount)
|
||||
})
|
||||
}
|
||||
}
|
||||
325
internal/nvsandboxutils/mock/interface.go
Normal file
325
internal/nvsandboxutils/mock/interface.go
Normal file
@@ -0,0 +1,325 @@
|
||||
// Code generated by moq; DO NOT EDIT.
|
||||
// github.com/matryer/moq
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ensure, that Interface does implement nvsandboxutils.Interface.
|
||||
// If this is not the case, regenerate this file with moq.
|
||||
var _ nvsandboxutils.Interface = &Interface{}
|
||||
|
||||
// Interface is a mock implementation of nvsandboxutils.Interface.
|
||||
//
|
||||
// func TestSomethingThatUsesInterface(t *testing.T) {
|
||||
//
|
||||
// // make and configure a mocked nvsandboxutils.Interface
|
||||
// mockedInterface := &Interface{
|
||||
// ErrorStringFunc: func(ret nvsandboxutils.Ret) string {
|
||||
// panic("mock out the ErrorString method")
|
||||
// },
|
||||
// GetDriverVersionFunc: func() (string, nvsandboxutils.Ret) {
|
||||
// panic("mock out the GetDriverVersion method")
|
||||
// },
|
||||
// GetFileContentFunc: func(s string) (string, nvsandboxutils.Ret) {
|
||||
// panic("mock out the GetFileContent method")
|
||||
// },
|
||||
// GetGpuResourceFunc: func(s string) ([]nvsandboxutils.GpuFileInfo, nvsandboxutils.Ret) {
|
||||
// panic("mock out the GetGpuResource method")
|
||||
// },
|
||||
// InitFunc: func(s string) nvsandboxutils.Ret {
|
||||
// panic("mock out the Init method")
|
||||
// },
|
||||
// LookupSymbolFunc: func(s string) error {
|
||||
// panic("mock out the LookupSymbol method")
|
||||
// },
|
||||
// ShutdownFunc: func() nvsandboxutils.Ret {
|
||||
// panic("mock out the Shutdown method")
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// // use mockedInterface in code that requires nvsandboxutils.Interface
|
||||
// // and then make assertions.
|
||||
//
|
||||
// }
|
||||
type Interface struct {
|
||||
// ErrorStringFunc mocks the ErrorString method.
|
||||
ErrorStringFunc func(ret nvsandboxutils.Ret) string
|
||||
|
||||
// GetDriverVersionFunc mocks the GetDriverVersion method.
|
||||
GetDriverVersionFunc func() (string, nvsandboxutils.Ret)
|
||||
|
||||
// GetFileContentFunc mocks the GetFileContent method.
|
||||
GetFileContentFunc func(s string) (string, nvsandboxutils.Ret)
|
||||
|
||||
// GetGpuResourceFunc mocks the GetGpuResource method.
|
||||
GetGpuResourceFunc func(s string) ([]nvsandboxutils.GpuFileInfo, nvsandboxutils.Ret)
|
||||
|
||||
// InitFunc mocks the Init method.
|
||||
InitFunc func(s string) nvsandboxutils.Ret
|
||||
|
||||
// LookupSymbolFunc mocks the LookupSymbol method.
|
||||
LookupSymbolFunc func(s string) error
|
||||
|
||||
// ShutdownFunc mocks the Shutdown method.
|
||||
ShutdownFunc func() nvsandboxutils.Ret
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
// ErrorString holds details about calls to the ErrorString method.
|
||||
ErrorString []struct {
|
||||
// Ret is the ret argument value.
|
||||
Ret nvsandboxutils.Ret
|
||||
}
|
||||
// GetDriverVersion holds details about calls to the GetDriverVersion method.
|
||||
GetDriverVersion []struct {
|
||||
}
|
||||
// GetFileContent holds details about calls to the GetFileContent method.
|
||||
GetFileContent []struct {
|
||||
// S is the s argument value.
|
||||
S string
|
||||
}
|
||||
// GetGpuResource holds details about calls to the GetGpuResource method.
|
||||
GetGpuResource []struct {
|
||||
// S is the s argument value.
|
||||
S string
|
||||
}
|
||||
// Init holds details about calls to the Init method.
|
||||
Init []struct {
|
||||
// S is the s argument value.
|
||||
S string
|
||||
}
|
||||
// LookupSymbol holds details about calls to the LookupSymbol method.
|
||||
LookupSymbol []struct {
|
||||
// S is the s argument value.
|
||||
S string
|
||||
}
|
||||
// Shutdown holds details about calls to the Shutdown method.
|
||||
Shutdown []struct {
|
||||
}
|
||||
}
|
||||
lockErrorString sync.RWMutex
|
||||
lockGetDriverVersion sync.RWMutex
|
||||
lockGetFileContent sync.RWMutex
|
||||
lockGetGpuResource sync.RWMutex
|
||||
lockInit sync.RWMutex
|
||||
lockLookupSymbol sync.RWMutex
|
||||
lockShutdown sync.RWMutex
|
||||
}
|
||||
|
||||
// ErrorString calls ErrorStringFunc.
|
||||
func (mock *Interface) ErrorString(ret nvsandboxutils.Ret) string {
|
||||
if mock.ErrorStringFunc == nil {
|
||||
panic("Interface.ErrorStringFunc: method is nil but Interface.ErrorString was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Ret nvsandboxutils.Ret
|
||||
}{
|
||||
Ret: ret,
|
||||
}
|
||||
mock.lockErrorString.Lock()
|
||||
mock.calls.ErrorString = append(mock.calls.ErrorString, callInfo)
|
||||
mock.lockErrorString.Unlock()
|
||||
return mock.ErrorStringFunc(ret)
|
||||
}
|
||||
|
||||
// ErrorStringCalls gets all the calls that were made to ErrorString.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.ErrorStringCalls())
|
||||
func (mock *Interface) ErrorStringCalls() []struct {
|
||||
Ret nvsandboxutils.Ret
|
||||
} {
|
||||
var calls []struct {
|
||||
Ret nvsandboxutils.Ret
|
||||
}
|
||||
mock.lockErrorString.RLock()
|
||||
calls = mock.calls.ErrorString
|
||||
mock.lockErrorString.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// GetDriverVersion calls GetDriverVersionFunc.
|
||||
func (mock *Interface) GetDriverVersion() (string, nvsandboxutils.Ret) {
|
||||
if mock.GetDriverVersionFunc == nil {
|
||||
panic("Interface.GetDriverVersionFunc: method is nil but Interface.GetDriverVersion was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockGetDriverVersion.Lock()
|
||||
mock.calls.GetDriverVersion = append(mock.calls.GetDriverVersion, callInfo)
|
||||
mock.lockGetDriverVersion.Unlock()
|
||||
return mock.GetDriverVersionFunc()
|
||||
}
|
||||
|
||||
// GetDriverVersionCalls gets all the calls that were made to GetDriverVersion.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.GetDriverVersionCalls())
|
||||
func (mock *Interface) GetDriverVersionCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockGetDriverVersion.RLock()
|
||||
calls = mock.calls.GetDriverVersion
|
||||
mock.lockGetDriverVersion.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// GetFileContent calls GetFileContentFunc.
|
||||
func (mock *Interface) GetFileContent(s string) (string, nvsandboxutils.Ret) {
|
||||
if mock.GetFileContentFunc == nil {
|
||||
panic("Interface.GetFileContentFunc: method is nil but Interface.GetFileContent was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
S string
|
||||
}{
|
||||
S: s,
|
||||
}
|
||||
mock.lockGetFileContent.Lock()
|
||||
mock.calls.GetFileContent = append(mock.calls.GetFileContent, callInfo)
|
||||
mock.lockGetFileContent.Unlock()
|
||||
return mock.GetFileContentFunc(s)
|
||||
}
|
||||
|
||||
// GetFileContentCalls gets all the calls that were made to GetFileContent.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.GetFileContentCalls())
|
||||
func (mock *Interface) GetFileContentCalls() []struct {
|
||||
S string
|
||||
} {
|
||||
var calls []struct {
|
||||
S string
|
||||
}
|
||||
mock.lockGetFileContent.RLock()
|
||||
calls = mock.calls.GetFileContent
|
||||
mock.lockGetFileContent.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// GetGpuResource calls GetGpuResourceFunc.
|
||||
func (mock *Interface) GetGpuResource(s string) ([]nvsandboxutils.GpuFileInfo, nvsandboxutils.Ret) {
|
||||
if mock.GetGpuResourceFunc == nil {
|
||||
panic("Interface.GetGpuResourceFunc: method is nil but Interface.GetGpuResource was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
S string
|
||||
}{
|
||||
S: s,
|
||||
}
|
||||
mock.lockGetGpuResource.Lock()
|
||||
mock.calls.GetGpuResource = append(mock.calls.GetGpuResource, callInfo)
|
||||
mock.lockGetGpuResource.Unlock()
|
||||
return mock.GetGpuResourceFunc(s)
|
||||
}
|
||||
|
||||
// GetGpuResourceCalls gets all the calls that were made to GetGpuResource.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.GetGpuResourceCalls())
|
||||
func (mock *Interface) GetGpuResourceCalls() []struct {
|
||||
S string
|
||||
} {
|
||||
var calls []struct {
|
||||
S string
|
||||
}
|
||||
mock.lockGetGpuResource.RLock()
|
||||
calls = mock.calls.GetGpuResource
|
||||
mock.lockGetGpuResource.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Init calls InitFunc.
|
||||
func (mock *Interface) Init(s string) nvsandboxutils.Ret {
|
||||
if mock.InitFunc == nil {
|
||||
panic("Interface.InitFunc: method is nil but Interface.Init was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
S string
|
||||
}{
|
||||
S: s,
|
||||
}
|
||||
mock.lockInit.Lock()
|
||||
mock.calls.Init = append(mock.calls.Init, callInfo)
|
||||
mock.lockInit.Unlock()
|
||||
return mock.InitFunc(s)
|
||||
}
|
||||
|
||||
// InitCalls gets all the calls that were made to Init.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.InitCalls())
|
||||
func (mock *Interface) InitCalls() []struct {
|
||||
S string
|
||||
} {
|
||||
var calls []struct {
|
||||
S string
|
||||
}
|
||||
mock.lockInit.RLock()
|
||||
calls = mock.calls.Init
|
||||
mock.lockInit.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// LookupSymbol calls LookupSymbolFunc.
|
||||
func (mock *Interface) LookupSymbol(s string) error {
|
||||
if mock.LookupSymbolFunc == nil {
|
||||
panic("Interface.LookupSymbolFunc: method is nil but Interface.LookupSymbol was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
S string
|
||||
}{
|
||||
S: s,
|
||||
}
|
||||
mock.lockLookupSymbol.Lock()
|
||||
mock.calls.LookupSymbol = append(mock.calls.LookupSymbol, callInfo)
|
||||
mock.lockLookupSymbol.Unlock()
|
||||
return mock.LookupSymbolFunc(s)
|
||||
}
|
||||
|
||||
// LookupSymbolCalls gets all the calls that were made to LookupSymbol.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.LookupSymbolCalls())
|
||||
func (mock *Interface) LookupSymbolCalls() []struct {
|
||||
S string
|
||||
} {
|
||||
var calls []struct {
|
||||
S string
|
||||
}
|
||||
mock.lockLookupSymbol.RLock()
|
||||
calls = mock.calls.LookupSymbol
|
||||
mock.lockLookupSymbol.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Shutdown calls ShutdownFunc.
|
||||
func (mock *Interface) Shutdown() nvsandboxutils.Ret {
|
||||
if mock.ShutdownFunc == nil {
|
||||
panic("Interface.ShutdownFunc: method is nil but Interface.Shutdown was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockShutdown.Lock()
|
||||
mock.calls.Shutdown = append(mock.calls.Shutdown, callInfo)
|
||||
mock.lockShutdown.Unlock()
|
||||
return mock.ShutdownFunc()
|
||||
}
|
||||
|
||||
// ShutdownCalls gets all the calls that were made to Shutdown.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedInterface.ShutdownCalls())
|
||||
func (mock *Interface) ShutdownCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockShutdown.RLock()
|
||||
calls = mock.calls.Shutdown
|
||||
mock.lockShutdown.RUnlock()
|
||||
return calls
|
||||
}
|
||||
72
internal/nvsandboxutils/nvsandboxutils.go
Normal file
72
internal/nvsandboxutils/nvsandboxutils.go
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
// WARNING: THIS FILE WAS AUTOMATICALLY GENERATED.
|
||||
// Code generated by https://git.io/c-for-go. DO NOT EDIT.
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
/*
|
||||
#cgo linux LDFLAGS: -Wl,--export-dynamic -Wl,--unresolved-symbols=ignore-in-object-files
|
||||
#cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup
|
||||
#include "nvsandboxutils.h"
|
||||
#include <stdlib.h>
|
||||
#include "cgo_helpers.h"
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// nvSandboxUtilsInit function as declared in nvsandboxutils/nvsandboxutils.h
|
||||
func nvSandboxUtilsInit(Input *InitInput) Ret {
|
||||
cInput, _ := (*C.nvSandboxUtilsInitInput_t)(unsafe.Pointer(Input)), cgoAllocsUnknown
|
||||
__ret := C.nvSandboxUtilsInit(cInput)
|
||||
__v := (Ret)(__ret)
|
||||
return __v
|
||||
}
|
||||
|
||||
// nvSandboxUtilsShutdown function as declared in nvsandboxutils/nvsandboxutils.h
|
||||
func nvSandboxUtilsShutdown() Ret {
|
||||
__ret := C.nvSandboxUtilsShutdown()
|
||||
__v := (Ret)(__ret)
|
||||
return __v
|
||||
}
|
||||
|
||||
// nvSandboxUtilsGetDriverVersion function as declared in nvsandboxutils/nvsandboxutils.h
|
||||
func nvSandboxUtilsGetDriverVersion(Version *byte, Length uint32) Ret {
|
||||
cVersion, _ := (*C.char)(unsafe.Pointer(Version)), cgoAllocsUnknown
|
||||
cLength, _ := (C.uint)(Length), cgoAllocsUnknown
|
||||
__ret := C.nvSandboxUtilsGetDriverVersion(cVersion, cLength)
|
||||
__v := (Ret)(__ret)
|
||||
return __v
|
||||
}
|
||||
|
||||
// nvSandboxUtilsGetGpuResource function as declared in nvsandboxutils/nvsandboxutils.h
|
||||
func nvSandboxUtilsGetGpuResource(Request *GpuRes) Ret {
|
||||
cRequest, _ := (*C.nvSandboxUtilsGpuRes_t)(unsafe.Pointer(Request)), cgoAllocsUnknown
|
||||
__ret := C.nvSandboxUtilsGetGpuResource(cRequest)
|
||||
__v := (Ret)(__ret)
|
||||
return __v
|
||||
}
|
||||
|
||||
// nvSandboxUtilsGetFileContent function as declared in nvsandboxutils/nvsandboxutils.h
|
||||
func nvSandboxUtilsGetFileContent(FilePath *byte, Content *byte, ContentSize *uint32) Ret {
|
||||
cFilePath, _ := (*C.char)(unsafe.Pointer(FilePath)), cgoAllocsUnknown
|
||||
cContent, _ := (*C.char)(unsafe.Pointer(Content)), cgoAllocsUnknown
|
||||
cContentSize, _ := (*C.uint)(unsafe.Pointer(ContentSize)), cgoAllocsUnknown
|
||||
__ret := C.nvSandboxUtilsGetFileContent(cFilePath, cContent, cContentSize)
|
||||
__v := (Ret)(__ret)
|
||||
return __v
|
||||
}
|
||||
298
internal/nvsandboxutils/nvsandboxutils.h
Normal file
298
internal/nvsandboxutils/nvsandboxutils.h
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 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.
|
||||
*/
|
||||
|
||||
#ifndef __NVSANDBOXUTILS_H__
|
||||
#define __NVSANDBOXUTILS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INPUT_LENGTH 256
|
||||
#define MAX_FILE_PATH 256
|
||||
#define MAX_NAME_LENGTH 256
|
||||
|
||||
/***************************************************************************************************/
|
||||
/** @defgroup enums Enumerations
|
||||
* @{
|
||||
*/
|
||||
/***************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Return types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NVSANDBOXUTILS_SUCCESS = 0, //!< The operation was successful
|
||||
NVSANDBOXUTILS_ERROR_UNINITIALIZED = 1, //!< The library wasn't successfully initialized
|
||||
NVSANDBOXUTILS_ERROR_NOT_SUPPORTED = 2, //!< The requested operation is not supported on target device
|
||||
NVSANDBOXUTILS_ERROR_INVALID_ARG = 3, //!< A supplied argument is invalid
|
||||
NVSANDBOXUTILS_ERROR_INSUFFICIENT_SIZE = 4, //!< A supplied argument is not large enough
|
||||
NVSANDBOXUTILS_ERROR_VERSION_NOT_SUPPORTED = 5, //!< Requested library version is not supported
|
||||
NVSANDBOXUTILS_ERROR_LIBRARY_LOAD = 6, //!< The library load failed
|
||||
NVSANDBOXUTILS_ERROR_FUNCTION_NOT_FOUND = 7, //!< Called function was not found
|
||||
NVSANDBOXUTILS_ERROR_DEVICE_NOT_FOUND = 8, //!< Target device was not found
|
||||
NVSANDBOXUTILS_ERROR_NVML_LIB_CALL = 9, //!< NVML library call failed
|
||||
NVSANDBOXUTILS_ERROR_OUT_OF_MEMORY = 10, //!< There is insufficient memory
|
||||
NVSANDBOXUTILS_ERROR_FILEPATH_NOT_FOUND = 11, //!< A supplied file path was not found
|
||||
NVSANDBOXUTILS_ERROR_UNKNOWN = 0xFFFF, //!< Unknown error occurred
|
||||
} nvSandboxUtilsRet_t;
|
||||
|
||||
/**
|
||||
* Return if there is an error
|
||||
*/
|
||||
#define RETURN_ON_SANDBOX_ERROR(result) \
|
||||
if ((result) != NVSANDBOXUTILS_SUCCESS) { \
|
||||
NVSANDBOXUTILS_ERROR_MSG("%s %d result=%d", __func__, __LINE__, result); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NVSANDBOXUTILS_LOG_LEVEL_FATAL = 0, //!< Log fatal errors
|
||||
NVSANDBOXUTILS_LOG_LEVEL_ERROR = 1, //!< Log all errors
|
||||
NVSANDBOXUTILS_LOG_LEVEL_WARN = 2, //!< Log all warnings
|
||||
NVSANDBOXUTILS_LOG_LEVEL_DEBUG = 3, //!< Log all debug messages
|
||||
NVSANDBOXUTILS_LOG_LEVEL_INFO = 4, //!< Log all info messages
|
||||
NVSANDBOXUTILS_LOG_LEVEL_NONE = 0xFFFF, //!< Log none
|
||||
} nvSandboxUtilsLogLevel_t;
|
||||
|
||||
/**
|
||||
* Input rootfs to help access files inside the driver container
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_ROOTFS_DEFAULT, //!< Default no rootfs
|
||||
NV_ROOTFS_PATH, //!< /run/nvidia/driver
|
||||
NV_ROOTFS_PID, //!< /proc/PID/mountinfo
|
||||
} nvSandboxUtilsRootfsInputType_t;
|
||||
|
||||
/**
|
||||
* File type
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_DEV, //!< /dev file system
|
||||
NV_PROC, //!< /proc file system
|
||||
NV_SYS, //!< /sys file system
|
||||
} nvSandboxUtilsFileType_t;
|
||||
|
||||
/**
|
||||
* File subtype
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_DEV_NVIDIA, //!< /dev/nvidia0
|
||||
NV_DEV_DRI_CARD, //!< /dev/dri/card1
|
||||
NV_DEV_DRI_RENDERD, //!< /dev/dri/renderD128
|
||||
NV_DEV_DRI_CARD_SYMLINK, //!< /dev/dri/by-path/pci-0000:41:00.0-card
|
||||
NV_DEV_DRI_RENDERD_SYMLINK, //!< /dev/dri/by-path/pci-0000:41:00.0-render
|
||||
NV_DEV_NVIDIA_UVM, //!< /dev/nvidia-uvm
|
||||
NV_DEV_NVIDIA_UVM_TOOLS, //!< /dev/nvidia-uvm-tools
|
||||
NV_DEV_NVIDIA_MODESET, //!< /dev/nvidia-uvm-modeset
|
||||
NV_DEV_NVIDIA_CTL, //!< /dev/nvidiactl
|
||||
NV_DEV_GDRDRV, //!< /dev/gdrdrv
|
||||
NV_DEV_NVIDIA_CAPS_NVIDIA_CAP, //!< /dev/nvidia-caps/nvidia-cap22
|
||||
NV_PROC_DRIVER_NVIDIA_GPUS_PCIBUSID, //!< /proc/driver/nvidia/gpus/0000:2d:00.0
|
||||
NV_PROC_DRIVER_NVIDIA_GPUS, //!< /proc/driver/nvidia/gpus (for mask out)
|
||||
NV_PROC_NVIDIA_PARAMS, //!< /proc/driver/nvidia/params
|
||||
NV_PROC_NVIDIA_CAPS_MIG_MINORS, //!< /proc/driver/nvidia-caps/mig-minors
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIES_GPU, //!< /proc/driver/nvidia/capabilities/gpu0
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIES, //!< /proc/driver/nvidia/capabilities (for mask out)
|
||||
NV_PROC_DRIVER_NVIDIA_CAPABILITIIES_GPU_MIG_CI_ACCESS, //!< proc/driver/nvidia/capabilities/gpu0/mig/gi2/ci0/access
|
||||
NV_SYS_MODULE_NVIDIA_DRIVER_PCIBUSID, //!< /sys/module/nvidia/drivers/pci:nvidia/0000:2d:00.0
|
||||
NV_SYS_MODULE_NVIDIA_DRIVER, //!< /sys/module/nvidia/drivers/pci:nvidia (for mask out)
|
||||
NV_NUM_SUBTYPE, // always at the end.
|
||||
} nvSandboxUtilsFileSystemSubType_t;
|
||||
|
||||
/**
|
||||
* File module
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_GPU, //!< Target device
|
||||
NV_MIG, //!< Target device- MIG
|
||||
NV_DRIVER_NVIDIA, //!< NVIDIA kernel driver
|
||||
NV_DRIVER_NVIDIA_UVM, //!< NVIDIA kernel driver-UVM
|
||||
NV_DRIVER_NVIDIA_MODESET, //!< NVIDIA kernel driver-modeset
|
||||
NV_DRIVER_GDRDRV, //!< GDRDRV driver
|
||||
NV_SYSTEM, //!< System module
|
||||
} nvSandboxUtilsFileModule_t;
|
||||
|
||||
/**
|
||||
* Flag to provide additional details about the file
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_FILE_FLAG_HINT = (1 << 0), //!< Default no hint
|
||||
NV_FILE_FLAG_MASKOUT = (1 << 1), //!< For /proc/driver/nvidia/gpus
|
||||
NV_FILE_FLAG_CONTENT = (1 << 2), //!< For /proc/driver/nvidia/params
|
||||
//!< For SYMLINK
|
||||
//!< Use \p nvSandboxUtilsGetFileContent to get name of the linked file
|
||||
NV_FILE_FLAG_DEPRECTATED = (1 << 3), //!< For all the FIRMWARE GSP file
|
||||
NV_FILE_FLAG_CANDIDATES = (1 << 4), //!< For libcuda.so
|
||||
} nvSandboxUtilsFileFlag_t;
|
||||
|
||||
/**
|
||||
* Input type of the target device
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
NV_GPU_INPUT_GPU_UUID, //!< GPU UUID
|
||||
NV_GPU_INPUT_MIG_UUID, //!< MIG UUID
|
||||
NV_GPU_INPUT_PCI_ID, //!< PCIe DBDF ID
|
||||
NV_GPU_INPUT_PCI_INDEX, //!< PCIe bus order (0 points to the GPU that has lowest PCIe BDF)
|
||||
} nvSandboxUtilsGpuInputType_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
/***************************************************************************************************/
|
||||
/** @defgroup dataTypes Structures and Unions
|
||||
* @{
|
||||
*/
|
||||
/***************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Initalization input v1
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version; //!< Version for the structure
|
||||
nvSandboxUtilsRootfsInputType_t type; //!< One of \p nvSandboxUtilsRootfsInputType_t
|
||||
char value[INPUT_LENGTH]; //!< String representation of input
|
||||
} nvSandboxUtilsInitInput_v1_t;
|
||||
|
||||
typedef nvSandboxUtilsInitInput_v1_t nvSandboxUtilsInitInput_t;
|
||||
|
||||
/**
|
||||
* File system information
|
||||
*/
|
||||
typedef struct nvSandboxUtilsGpuFileInfo_v1_t
|
||||
{
|
||||
struct nvSandboxUtilsGpuFileInfo_v1_t *next; //!< Pointer to the next node in the linked list
|
||||
nvSandboxUtilsFileType_t fileType; //!< One of \p nvSandboxUtilsFileType_t
|
||||
nvSandboxUtilsFileSystemSubType_t fileSubType; //!< One of \p nvSandboxUtilsFileSystemSubType_t
|
||||
nvSandboxUtilsFileModule_t module; //!< One of \p nvSandboxUtilsFileModule_t
|
||||
nvSandboxUtilsFileFlag_t flags; //!< One of \p nvSandboxUtilsFileFlag_t
|
||||
char *filePath; //!< Relative file path to rootfs
|
||||
}nvSandboxUtilsGpuFileInfo_v1_t;
|
||||
|
||||
/**
|
||||
* GPU resource request v1
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version; //!< Version for the structure
|
||||
nvSandboxUtilsGpuInputType_t inputType; //!< One of \p nvSandboxUtilsGpuInputType_t
|
||||
char input[INPUT_LENGTH]; //!< String representation of input
|
||||
nvSandboxUtilsGpuFileInfo_v1_t *files; //!< Linked list of \ref nvSandboxUtilsGpuFileInfo_v1_t
|
||||
} nvSandboxUtilsGpuRes_v1_t;
|
||||
|
||||
typedef nvSandboxUtilsGpuRes_v1_t nvSandboxUtilsGpuRes_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
/***************************************************************************************************/
|
||||
/** @defgroup funcs Functions
|
||||
* @{
|
||||
*/
|
||||
/***************************************************************************************************/
|
||||
|
||||
/* *************************************************
|
||||
* Initialize library
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Prepare library resources before library API can be used.
|
||||
* This initialization will not fail if one of the initialization prerequisites fails.
|
||||
* @param input Reference to the called-supplied input struct that has initialization fields
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p input->value isn't a valid rootfs path
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_VERSION_NOT_SUPPORTED if \p input->version isn't supported by the library
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_FILEPATH_NOT_FOUND if any of the required file paths are not found during initialization
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_OUT_OF_MEMORY if there is insufficient system memory during initialization
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_LIBRARY_LOAD on any error during loading the library
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsInit(nvSandboxUtilsInitInput_t *input);
|
||||
|
||||
/* *************************************************
|
||||
* Shutdown library
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Clean up library resources created by init call
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsShutdown(void);
|
||||
|
||||
/* *************************************************
|
||||
* Get NVIDIA RM driver version
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Get NVIDIA RM driver version
|
||||
* @param version Reference to caller-supplied buffer to return driver version string
|
||||
* @param length The maximum allowed length of the string returned in \p version
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p version is NULL
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_NVML_LIB_CALL on any error during driver version query from NVML
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsGetDriverVersion(char *version, unsigned int length);
|
||||
|
||||
/* *************************************************
|
||||
* Get /dev, /proc, /sys file system information
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Get /dev, /proc, /sys file system information
|
||||
* @param request Reference to caller-supplied request struct to return the file system information
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p request->input doesn't match any device
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_VERSION_NOT_SUPPORTED if \p request->version isn't supported by the library
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsGetGpuResource(nvSandboxUtilsGpuRes_t *request);
|
||||
|
||||
/* *************************************************
|
||||
* Get content of given file path
|
||||
* *************************************************
|
||||
*/
|
||||
/**
|
||||
* Get file content of input file path
|
||||
* @param filePath Reference to the file path
|
||||
* @param content Reference to the caller-supplied buffer to return the file content
|
||||
* @param contentSize Reference to the maximum allowed size of content. It is updated to the actual size of the content on return
|
||||
*
|
||||
* @returns @ref NVSANDBOXUTILS_SUCCESS on success
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INVALID_ARG if \p filePath or \p content is NULL
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_INSUFFICIENT_SIZE if \p contentSize is too small
|
||||
* @returns @ref NVSANDBOXUTILS_ERROR_FILEPATH_NOT_FOUND on an error while obtaining the content for the file path
|
||||
*/
|
||||
nvSandboxUtilsRet_t nvSandboxUtilsGetFileContent(char *filePath, char *content, unsigned int *contentSize);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __NVSANDBOXUTILS_H__
|
||||
31
internal/nvsandboxutils/refcount.go
Normal file
31
internal/nvsandboxutils/refcount.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
type refcount int
|
||||
|
||||
func (r *refcount) IncOnNoError(err error) {
|
||||
if err == nil {
|
||||
(*r)++
|
||||
}
|
||||
}
|
||||
|
||||
func (r *refcount) DecOnNoError(err error) {
|
||||
if err == nil && (*r) > 0 {
|
||||
(*r)--
|
||||
}
|
||||
}
|
||||
139
internal/nvsandboxutils/refcount_test.go
Normal file
139
internal/nvsandboxutils/refcount_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRefcount(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
workload func(r *refcount)
|
||||
expectedRefcount refcount
|
||||
}{
|
||||
{
|
||||
description: "No inc or dec",
|
||||
workload: func(r *refcount) {},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Single inc, no error",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(1),
|
||||
},
|
||||
{
|
||||
description: "Single inc, with error",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(errors.New(""))
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Double inc, no error",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.IncOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(2),
|
||||
},
|
||||
{
|
||||
description: "Double inc, one with error",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.IncOnNoError(errors.New(""))
|
||||
},
|
||||
expectedRefcount: refcount(1),
|
||||
},
|
||||
{
|
||||
description: "Single dec, no error",
|
||||
workload: func(r *refcount) {
|
||||
r.DecOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Single dec, with error",
|
||||
workload: func(r *refcount) {
|
||||
r.DecOnNoError(errors.New(""))
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Single inc, single dec, no errors",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.DecOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Double inc, Double dec, no errors",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.IncOnNoError(nil)
|
||||
r.DecOnNoError(nil)
|
||||
r.DecOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Double inc, Double dec, one inc error",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.IncOnNoError(errors.New(""))
|
||||
r.DecOnNoError(nil)
|
||||
r.DecOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
{
|
||||
description: "Double inc, Double dec, one dec error",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.IncOnNoError(nil)
|
||||
r.DecOnNoError(nil)
|
||||
r.DecOnNoError(errors.New(""))
|
||||
},
|
||||
expectedRefcount: refcount(1),
|
||||
},
|
||||
{
|
||||
description: "Double inc, Tripple dec, one dec error early on",
|
||||
workload: func(r *refcount) {
|
||||
r.IncOnNoError(nil)
|
||||
r.IncOnNoError(nil)
|
||||
r.DecOnNoError(errors.New(""))
|
||||
r.DecOnNoError(nil)
|
||||
r.DecOnNoError(nil)
|
||||
},
|
||||
expectedRefcount: refcount(0),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
var r refcount
|
||||
tc.workload(&r)
|
||||
require.Equal(t, tc.expectedRefcount, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
74
internal/nvsandboxutils/return.go
Normal file
74
internal/nvsandboxutils/return.go
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// nvsandboxutils.ErrorString()
|
||||
func (l *library) ErrorString(r Ret) string {
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// String returns the string representation of a Ret.
|
||||
func (r Ret) String() string {
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Error returns the string representation of a Ret.
|
||||
func (r Ret) Error() string {
|
||||
return errorStringFunc(r)
|
||||
}
|
||||
|
||||
// Assigned to nvsandboxutils.ErrorString if the system nvsandboxutils library is in use.
|
||||
var errorStringFunc = defaultErrorStringFunc
|
||||
|
||||
// nvsanboxutilsErrorString is an alias for the default error string function.
|
||||
var nvsanboxutilsErrorString = defaultErrorStringFunc
|
||||
|
||||
// defaultErrorStringFunc provides a basic nvsandboxutils.ErrorString implementation.
|
||||
// This allows the nvsandboxutils.ErrorString function to be used even if the nvsandboxutils library
|
||||
// is not loaded.
|
||||
var defaultErrorStringFunc = func(r Ret) string {
|
||||
switch r {
|
||||
case SUCCESS:
|
||||
return "SUCCESS"
|
||||
case ERROR_UNINITIALIZED:
|
||||
return "ERROR_UNINITIALIZED"
|
||||
case ERROR_NOT_SUPPORTED:
|
||||
return "ERROR_NOT_SUPPORTED"
|
||||
case ERROR_INVALID_ARG:
|
||||
return "ERROR_INVALID_ARG"
|
||||
case ERROR_INSUFFICIENT_SIZE:
|
||||
return "ERROR_INSUFFICIENT_SIZE"
|
||||
case ERROR_VERSION_NOT_SUPPORTED:
|
||||
return "ERROR_VERSION_NOT_SUPPORTED"
|
||||
case ERROR_LIBRARY_LOAD:
|
||||
return "ERROR_LIBRARY_LOAD"
|
||||
case ERROR_FUNCTION_NOT_FOUND:
|
||||
return "ERROR_FUNCTION_NOT_FOUND"
|
||||
case ERROR_DEVICE_NOT_FOUND:
|
||||
return "ERROR_DEVICE_NOT_FOUND"
|
||||
case ERROR_NVML_LIB_CALL:
|
||||
return "ERROR_NVML_LIB_CALL"
|
||||
case ERROR_UNKNOWN:
|
||||
return "ERROR_UNKNOWN"
|
||||
default:
|
||||
return fmt.Sprintf("unknown return value: %d", r)
|
||||
}
|
||||
}
|
||||
39
internal/nvsandboxutils/types_gen.go
Normal file
39
internal/nvsandboxutils/types_gen.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs types.go
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
type InitInput_v1 struct {
|
||||
Version uint32
|
||||
Type uint32
|
||||
Value [256]int8
|
||||
}
|
||||
|
||||
type InitInput struct {
|
||||
Version uint32
|
||||
Type uint32
|
||||
Value [256]int8
|
||||
}
|
||||
|
||||
type GpuFileInfo_v1 struct {
|
||||
Next *GpuFileInfo_v1
|
||||
FileType uint32
|
||||
FileSubType uint32
|
||||
Module uint32
|
||||
Flags uint32
|
||||
FilePath *int8
|
||||
}
|
||||
|
||||
type GpuRes_v1 struct {
|
||||
Version uint32
|
||||
InputType uint32
|
||||
Input [256]int8
|
||||
Files *GpuFileInfo_v1
|
||||
}
|
||||
|
||||
type GpuRes struct {
|
||||
Version uint32
|
||||
InputType uint32
|
||||
Input [256]int8
|
||||
Files *GpuFileInfo_v1
|
||||
}
|
||||
43
internal/nvsandboxutils/zz_generated.api.go
Normal file
43
internal/nvsandboxutils/zz_generated.api.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
// Generated Code; DO NOT EDIT.
|
||||
|
||||
package nvsandboxutils
|
||||
|
||||
// The variables below represent package level methods from the library type.
|
||||
var (
|
||||
ErrorString = libnvsandboxutils.ErrorString
|
||||
GetDriverVersion = libnvsandboxutils.GetDriverVersion
|
||||
GetFileContent = libnvsandboxutils.GetFileContent
|
||||
GetGpuResource = libnvsandboxutils.GetGpuResource
|
||||
Init = libnvsandboxutils.Init
|
||||
LookupSymbol = libnvsandboxutils.LookupSymbol
|
||||
Shutdown = libnvsandboxutils.Shutdown
|
||||
)
|
||||
|
||||
// Interface represents the interface for the library type.
|
||||
//
|
||||
//go:generate moq -out mock/interface.go -pkg mock . Interface:Interface
|
||||
type Interface interface {
|
||||
ErrorString(Ret) string
|
||||
GetDriverVersion() (string, Ret)
|
||||
GetFileContent(string) (string, Ret)
|
||||
GetGpuResource(string) ([]GpuFileInfo, Ret)
|
||||
Init(string) Ret
|
||||
LookupSymbol(string) error
|
||||
Shutdown() Ret
|
||||
}
|
||||
@@ -17,41 +17,109 @@
|
||||
package dgpu
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
|
||||
)
|
||||
|
||||
// NewForDevice creates a discoverer for the specified Device.
|
||||
// nvsandboxutils is used for discovery if specified, otherwise NVML is used.
|
||||
func NewForDevice(d device.Device, opts ...Option) (discover.Discover, error) {
|
||||
o := &options{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
o := new(opts...)
|
||||
|
||||
var discoverers []discover.Discover
|
||||
var errs error
|
||||
nvsandboxutilsDiscoverer, err := o.newNvsandboxutilsDGPUDiscoverer(d)
|
||||
if err != nil {
|
||||
// TODO: Log a warning
|
||||
errs = errors.Join(errs, err)
|
||||
} else if nvsandboxutilsDiscoverer != nil {
|
||||
discoverers = append(discoverers, nvsandboxutilsDiscoverer)
|
||||
}
|
||||
|
||||
if o.logger == nil {
|
||||
o.logger = logger.New()
|
||||
nvmlDiscoverer, err := o.newNvmlDGPUDiscoverer(&toRequiredInfo{d})
|
||||
if err != nil {
|
||||
// TODO: Log a warning
|
||||
errs = errors.Join(errs, err)
|
||||
} else if nvmlDiscoverer != nil {
|
||||
discoverers = append(discoverers, nvmlDiscoverer)
|
||||
}
|
||||
|
||||
return o.newNvmlDGPUDiscoverer(&toRequiredInfo{d})
|
||||
if len(discoverers) == 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
return discover.WithCache(
|
||||
discover.FirstValid(
|
||||
discoverers...,
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
// NewForDevice creates a discoverer for the specified device and its associated MIG device.
|
||||
// NewForMigDevice creates a discoverer for the specified device and its associated MIG device.
|
||||
// nvsandboxutils is used for discovery if specified, otherwise NVML is used.
|
||||
func NewForMigDevice(d device.Device, mig device.MigDevice, opts ...Option) (discover.Discover, error) {
|
||||
o := &options{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
o := new(opts...)
|
||||
o.isMigDevice = true
|
||||
|
||||
var discoverers []discover.Discover
|
||||
var errs error
|
||||
nvsandboxutilsDiscoverer, err := o.newNvsandboxutilsDGPUDiscoverer(mig)
|
||||
if err != nil {
|
||||
// TODO: Log a warning
|
||||
errs = errors.Join(errs, err)
|
||||
} else if nvsandboxutilsDiscoverer != nil {
|
||||
discoverers = append(discoverers, nvsandboxutilsDiscoverer)
|
||||
}
|
||||
|
||||
if o.logger == nil {
|
||||
o.logger = logger.New()
|
||||
}
|
||||
|
||||
return o.newNvmlMigDiscoverer(
|
||||
nvmlDiscoverer, err := o.newNvmlMigDiscoverer(
|
||||
&toRequiredMigInfo{
|
||||
MigDevice: mig,
|
||||
parent: &toRequiredInfo{d},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// TODO: Log a warning
|
||||
errs = errors.Join(errs, err)
|
||||
} else if nvmlDiscoverer != nil {
|
||||
discoverers = append(discoverers, nvmlDiscoverer)
|
||||
}
|
||||
|
||||
if len(discoverers) == 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
return discover.WithCache(
|
||||
discover.FirstValid(
|
||||
discoverers...,
|
||||
),
|
||||
), nil
|
||||
|
||||
}
|
||||
|
||||
func new(opts ...Option) *options {
|
||||
o := &options{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
}
|
||||
|
||||
if o.logger == nil {
|
||||
o.logger = logger.New()
|
||||
}
|
||||
|
||||
if o.migCaps == nil {
|
||||
migCaps, err := nvcaps.NewMigCaps()
|
||||
if err != nil {
|
||||
o.logger.Debugf("ignoring error getting MIG capability device paths: %v", err)
|
||||
o.migCapsError = err
|
||||
} else {
|
||||
o.migCaps = migCaps
|
||||
}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
@@ -78,24 +78,23 @@ type requiredMigInfo interface {
|
||||
}
|
||||
|
||||
func (o *options) newNvmlMigDiscoverer(d requiredMigInfo) (discover.Discover, error) {
|
||||
if o.migCaps == nil || o.migCapsError != nil {
|
||||
return nil, fmt.Errorf("error getting MIG capability device paths: %v", o.migCapsError)
|
||||
}
|
||||
|
||||
gpu, gi, ci, err := d.getPlacementInfo()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting placement info: %w", err)
|
||||
}
|
||||
|
||||
migCaps, err := nvcaps.NewMigCaps()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting MIG capability device paths: %v", err)
|
||||
}
|
||||
|
||||
giCap := nvcaps.NewGPUInstanceCap(gpu, gi)
|
||||
giCapDevicePath, err := migCaps.GetCapDevicePath(giCap)
|
||||
giCapDevicePath, err := o.migCaps.GetCapDevicePath(giCap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get GI cap device path: %v", err)
|
||||
}
|
||||
|
||||
ciCap := nvcaps.NewComputeInstanceCap(gpu, gi, ci)
|
||||
ciCapDevicePath, err := migCaps.GetCapDevicePath(ciCap)
|
||||
ciCapDevicePath, err := o.migCaps.GetCapDevicePath(ciCap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get CI cap device path: %v", err)
|
||||
}
|
||||
@@ -145,9 +144,9 @@ type toRequiredMigInfo struct {
|
||||
}
|
||||
|
||||
func (d *toRequiredMigInfo) getPlacementInfo() (int, int, int, error) {
|
||||
gpu, ret := d.parent.GetMinorNumber()
|
||||
if ret != nvml.SUCCESS {
|
||||
return 0, 0, 0, fmt.Errorf("error getting GPU minor: %v", ret)
|
||||
gpu, err := d.parent.GetMinorNumber()
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("error getting GPU minor: %w", err)
|
||||
}
|
||||
|
||||
gi, ret := d.GetGpuInstanceId()
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
|
||||
)
|
||||
|
||||
// TODO: In order to properly test this, we need a mechanism to inject /
|
||||
@@ -85,3 +86,86 @@ func TestNewNvmlDGPUDiscoverer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewNvmlMIGDiscoverer(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
nvmllib := &mock.Interface{}
|
||||
devicelib := device.New(
|
||||
nvmllib,
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
mig *mock.Device
|
||||
parent nvml.Device
|
||||
migCaps nvcaps.MigCaps
|
||||
expectedError error
|
||||
expectedDevices []discover.Device
|
||||
expectedHooks []discover.Hook
|
||||
expectedMounts []discover.Mount
|
||||
}{
|
||||
{
|
||||
description: "",
|
||||
mig: &mock.Device{
|
||||
IsMigDeviceHandleFunc: func() (bool, nvml.Return) {
|
||||
return true, nvml.SUCCESS
|
||||
},
|
||||
GetGpuInstanceIdFunc: func() (int, nvml.Return) {
|
||||
return 1, nvml.SUCCESS
|
||||
},
|
||||
GetComputeInstanceIdFunc: func() (int, nvml.Return) {
|
||||
return 2, nvml.SUCCESS
|
||||
},
|
||||
},
|
||||
parent: &mock.Device{
|
||||
GetMinorNumberFunc: func() (int, nvml.Return) {
|
||||
return 3, nvml.SUCCESS
|
||||
},
|
||||
GetPciInfoFunc: func() (nvml.PciInfo, nvml.Return) {
|
||||
var busID [32]int8
|
||||
for i, b := range []byte("00000000:45:00:00") {
|
||||
busID[i] = int8(b)
|
||||
}
|
||||
info := nvml.PciInfo{
|
||||
BusId: busID,
|
||||
}
|
||||
return info, nvml.SUCCESS
|
||||
},
|
||||
},
|
||||
migCaps: nvcaps.MigCaps{
|
||||
"gpu3/gi1/access": 31,
|
||||
"gpu3/gi1/ci2/access": 312,
|
||||
},
|
||||
expectedDevices: nil,
|
||||
expectedMounts: nil,
|
||||
expectedHooks: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
|
||||
tc.mig.GetDeviceHandleFromMigDeviceHandleFunc = func() (nvml.Device, nvml.Return) {
|
||||
return tc.parent, nvml.SUCCESS
|
||||
}
|
||||
parent, err := devicelib.NewDevice(tc.parent)
|
||||
require.NoError(t, err)
|
||||
|
||||
mig, err := devicelib.NewMigDevice(tc.mig)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := NewForMigDevice(parent, mig,
|
||||
WithLogger(logger),
|
||||
WithMIGCaps(tc.migCaps),
|
||||
)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
|
||||
devices, _ := d.Devices()
|
||||
require.EqualValues(t, tc.expectedDevices, devices)
|
||||
hooks, _ := d.Hooks()
|
||||
require.EqualValues(t, tc.expectedHooks, hooks)
|
||||
mounts, _ := d.Mounts()
|
||||
require.EqualValues(t, tc.expectedMounts, mounts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
131
internal/platform-support/dgpu/nvsandboxutils.go
Normal file
131
internal/platform-support/dgpu/nvsandboxutils.go
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package dgpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
|
||||
)
|
||||
|
||||
type nvsandboxutilsDGPU struct {
|
||||
lib nvsandboxutils.Interface
|
||||
uuid string
|
||||
devRoot string
|
||||
isMig bool
|
||||
nvidiaCDIHookPath string
|
||||
deviceLinks []string
|
||||
}
|
||||
|
||||
var _ discover.Discover = (*nvsandboxutilsDGPU)(nil)
|
||||
|
||||
type UUIDer interface {
|
||||
GetUUID() (string, nvml.Return)
|
||||
}
|
||||
|
||||
func (o *options) newNvsandboxutilsDGPUDiscoverer(d UUIDer) (discover.Discover, error) {
|
||||
if o.nvsandboxutilslib == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
uuid, nvmlRet := d.GetUUID()
|
||||
if nvmlRet != nvml.SUCCESS {
|
||||
return nil, fmt.Errorf("failed to get device UUID: %w", nvmlRet)
|
||||
}
|
||||
|
||||
nvd := nvsandboxutilsDGPU{
|
||||
lib: o.nvsandboxutilslib,
|
||||
uuid: uuid,
|
||||
devRoot: strings.TrimSuffix(filepath.Clean(o.devRoot), "/dev"),
|
||||
isMig: o.isMigDevice,
|
||||
nvidiaCDIHookPath: o.nvidiaCDIHookPath,
|
||||
}
|
||||
|
||||
return &nvd, nil
|
||||
}
|
||||
|
||||
func (d *nvsandboxutilsDGPU) Devices() ([]discover.Device, error) {
|
||||
gpuFileInfos, ret := d.lib.GetGpuResource(d.uuid)
|
||||
if ret != nvsandboxutils.SUCCESS {
|
||||
return nil, fmt.Errorf("failed to get GPU resource: %w", ret)
|
||||
}
|
||||
|
||||
var devices []discover.Device
|
||||
for _, info := range gpuFileInfos {
|
||||
switch {
|
||||
case info.SubType == nvsandboxutils.NV_DEV_DRI_CARD, info.SubType == nvsandboxutils.NV_DEV_DRI_RENDERD:
|
||||
if d.isMig {
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
case info.SubType == nvsandboxutils.NV_DEV_NVIDIA, info.SubType == nvsandboxutils.NV_DEV_NVIDIA_CAPS_NVIDIA_CAP:
|
||||
containerPath := info.Path
|
||||
if d.devRoot != "/" {
|
||||
containerPath = strings.TrimPrefix(containerPath, d.devRoot)
|
||||
}
|
||||
|
||||
// TODO: Extend discover.Device with additional information.
|
||||
device := discover.Device{
|
||||
HostPath: info.Path,
|
||||
Path: containerPath,
|
||||
}
|
||||
devices = append(devices, device)
|
||||
case info.SubType == nvsandboxutils.NV_DEV_DRI_CARD_SYMLINK, info.SubType == nvsandboxutils.NV_DEV_DRI_RENDERD_SYMLINK:
|
||||
if d.isMig {
|
||||
continue
|
||||
}
|
||||
if info.Flags == nvsandboxutils.NV_FILE_FLAG_CONTENT {
|
||||
targetPath, ret := d.lib.GetFileContent(info.Path)
|
||||
if ret != nvsandboxutils.SUCCESS {
|
||||
return nil, fmt.Errorf("failed to get symlink: %w", ret)
|
||||
}
|
||||
d.deviceLinks = append(d.deviceLinks, fmt.Sprintf("%v::%v", targetPath, info.Path))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// Hooks returns a hook to create the by-path symlinks for the discovered devices.
|
||||
func (d *nvsandboxutilsDGPU) Hooks() ([]discover.Hook, error) {
|
||||
if len(d.deviceLinks) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var args []string
|
||||
for _, l := range d.deviceLinks {
|
||||
args = append(args, "--link", l)
|
||||
}
|
||||
|
||||
hook := discover.CreateNvidiaCDIHook(
|
||||
d.nvidiaCDIHookPath,
|
||||
"create-symlinks",
|
||||
args...,
|
||||
)
|
||||
|
||||
return []discover.Hook{hook}, nil
|
||||
}
|
||||
|
||||
func (d *nvsandboxutilsDGPU) Mounts() ([]discover.Mount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
174
internal/platform-support/dgpu/nvsandboxutils_test.go
Normal file
174
internal/platform-support/dgpu/nvsandboxutils_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package dgpu
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
mocknvml "github.com/NVIDIA/go-nvml/pkg/nvml/mock"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
|
||||
mocknvsandboxutils "github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils/mock"
|
||||
)
|
||||
|
||||
func TestNewNvsandboxutilsDGPUDiscoverer(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
nvmllib := &mocknvml.Interface{}
|
||||
devicelib := device.New(
|
||||
nvmllib,
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
devRoot string
|
||||
device nvml.Device
|
||||
nvsandboxutils nvsandboxutils.Interface
|
||||
expectedError error
|
||||
expectedDevices []discover.Device
|
||||
expectedHooks []discover.Hook
|
||||
expectedMounts []discover.Mount
|
||||
}{
|
||||
{
|
||||
description: "detects host devices",
|
||||
device: &mocknvml.Device{
|
||||
GetUUIDFunc: func() (string, nvml.Return) {
|
||||
return "GPU-1234", nvml.SUCCESS
|
||||
},
|
||||
},
|
||||
nvsandboxutils: &mocknvsandboxutils.Interface{
|
||||
GetGpuResourceFunc: func(s string) ([]nvsandboxutils.GpuFileInfo, nvsandboxutils.Ret) {
|
||||
infos := []nvsandboxutils.GpuFileInfo{
|
||||
{
|
||||
Path: "/dev/nvidia0",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidiactl",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidia-uvm",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidia-uvm-tools",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
}
|
||||
return infos, nvsandboxutils.SUCCESS
|
||||
},
|
||||
},
|
||||
expectedDevices: []discover.Device{
|
||||
{
|
||||
Path: "/dev/nvidia0",
|
||||
HostPath: "/dev/nvidia0",
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidiactl",
|
||||
HostPath: "/dev/nvidiactl",
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidia-uvm",
|
||||
HostPath: "/dev/nvidia-uvm",
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidia-uvm-tools",
|
||||
HostPath: "/dev/nvidia-uvm-tools",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "detects container devices",
|
||||
devRoot: "/some/root",
|
||||
device: &mocknvml.Device{
|
||||
GetUUIDFunc: func() (string, nvml.Return) {
|
||||
return "GPU-1234", nvml.SUCCESS
|
||||
},
|
||||
},
|
||||
nvsandboxutils: &mocknvsandboxutils.Interface{
|
||||
GetGpuResourceFunc: func(s string) ([]nvsandboxutils.GpuFileInfo, nvsandboxutils.Ret) {
|
||||
infos := []nvsandboxutils.GpuFileInfo{
|
||||
{
|
||||
Path: "/some/root/dev/nvidia0",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
{
|
||||
Path: "/some/root/dev/nvidiactl",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
{
|
||||
Path: "/some/root/dev/nvidia-uvm",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
{
|
||||
Path: "/some/root/dev/nvidia-uvm-tools",
|
||||
Type: nvsandboxutils.NV_DEV,
|
||||
},
|
||||
}
|
||||
return infos, nvsandboxutils.SUCCESS
|
||||
},
|
||||
},
|
||||
expectedDevices: []discover.Device{
|
||||
{
|
||||
Path: "/dev/nvidia0",
|
||||
HostPath: "/some/root/dev/nvidia0",
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidiactl",
|
||||
HostPath: "/some/root/dev/nvidiactl",
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidia-uvm",
|
||||
HostPath: "/some/root/dev/nvidia-uvm",
|
||||
},
|
||||
{
|
||||
Path: "/dev/nvidia-uvm-tools",
|
||||
HostPath: "/some/root/dev/nvidia-uvm-tools",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
o := &options{
|
||||
logger: logger,
|
||||
devRoot: tc.devRoot,
|
||||
nvsandboxutilslib: tc.nvsandboxutils,
|
||||
}
|
||||
|
||||
device, err := devicelib.NewDevice(tc.device)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := o.newNvsandboxutilsDGPUDiscoverer(device)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
|
||||
devices, _ := d.Devices()
|
||||
require.EqualValues(t, tc.expectedDevices, devices)
|
||||
hooks, _ := d.Hooks()
|
||||
require.EqualValues(t, tc.expectedHooks, hooks)
|
||||
mounts, _ := d.Mounts()
|
||||
require.EqualValues(t, tc.expectedMounts, mounts)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,22 @@ package dgpu
|
||||
|
||||
import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvcaps"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/nvsandboxutils"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
logger logger.Interface
|
||||
devRoot string
|
||||
nvidiaCDIHookPath string
|
||||
|
||||
isMigDevice bool
|
||||
// migCaps stores the MIG capabilities for the system.
|
||||
// If MIG is not available, this is nil.
|
||||
migCaps nvcaps.MigCaps
|
||||
migCapsError error
|
||||
|
||||
nvsandboxutilslib nvsandboxutils.Interface
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
@@ -48,3 +58,17 @@ func WithNVIDIACDIHookPath(path string) Option {
|
||||
l.nvidiaCDIHookPath = path
|
||||
}
|
||||
}
|
||||
|
||||
// WithMIGCaps sets the MIG capabilities.
|
||||
func WithMIGCaps(migCaps nvcaps.MigCaps) Option {
|
||||
return func(l *options) {
|
||||
l.migCaps = migCaps
|
||||
}
|
||||
}
|
||||
|
||||
// WithNvsandboxuitilsLib sets the nvsandboxutils library implementation.
|
||||
func WithNvsandboxuitilsLib(nvsandboxutilslib nvsandboxutils.Interface) Option {
|
||||
return func(l *options) {
|
||||
l.nvsandboxutilslib = nvsandboxutilslib
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,20 @@ func (o tegraOptions) newDiscovererFromCSVFiles() (discover.Discover, error) {
|
||||
targetsByType[csv.MountSpecDir],
|
||||
)
|
||||
|
||||
// Libraries and symlinks use the same locator.
|
||||
libraries := discover.NewMounts(
|
||||
o.logger,
|
||||
o.symlinkLocator,
|
||||
o.driverRoot,
|
||||
targetsByType[csv.MountSpecLib],
|
||||
// We create a discoverer for mounted libraries and add additional .so
|
||||
// symlinks for the driver.
|
||||
libraries := discover.WithDriverDotSoSymlinks(
|
||||
discover.NewMounts(
|
||||
o.logger,
|
||||
o.symlinkLocator,
|
||||
o.driverRoot,
|
||||
targetsByType[csv.MountSpecLib],
|
||||
),
|
||||
"",
|
||||
o.nvidiaCDIHookPath,
|
||||
)
|
||||
|
||||
// We process the explicitly requested symlinks.
|
||||
symlinkTargets := o.ignorePatterns.Apply(targetsByType[csv.MountSpecSym]...)
|
||||
o.logger.Debugf("Filtered symlink targets: %v", symlinkTargets)
|
||||
symlinks := discover.NewMounts(
|
||||
@@ -65,7 +71,7 @@ func (o tegraOptions) newDiscovererFromCSVFiles() (discover.Discover, error) {
|
||||
o.driverRoot,
|
||||
symlinkTargets,
|
||||
)
|
||||
createSymlinks := o.createCSVSymlinkHooks(symlinkTargets, libraries)
|
||||
createSymlinks := o.createCSVSymlinkHooks(symlinkTargets)
|
||||
|
||||
d := discover.Merge(
|
||||
devices,
|
||||
|
||||
@@ -18,8 +18,6 @@ package tegra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
@@ -31,7 +29,6 @@ type symlinkHook struct {
|
||||
logger logger.Interface
|
||||
nvidiaCDIHookPath string
|
||||
targets []string
|
||||
mountsFrom discover.Discover
|
||||
|
||||
// The following can be overridden for testing
|
||||
symlinkChainLocator lookup.Locator
|
||||
@@ -39,12 +36,11 @@ type symlinkHook struct {
|
||||
}
|
||||
|
||||
// createCSVSymlinkHooks creates a discoverer for a hook that creates required symlinks in the container
|
||||
func (o tegraOptions) createCSVSymlinkHooks(targets []string, mounts discover.Discover) discover.Discover {
|
||||
func (o tegraOptions) createCSVSymlinkHooks(targets []string) discover.Discover {
|
||||
return symlinkHook{
|
||||
logger: o.logger,
|
||||
nvidiaCDIHookPath: o.nvidiaCDIHookPath,
|
||||
targets: targets,
|
||||
mountsFrom: mounts,
|
||||
symlinkChainLocator: o.symlinkChainLocator,
|
||||
resolveSymlink: o.resolveSymlink,
|
||||
}
|
||||
@@ -52,62 +48,12 @@ func (o tegraOptions) createCSVSymlinkHooks(targets []string, mounts discover.Di
|
||||
|
||||
// Hooks returns a hook to create the symlinks from the required CSV files
|
||||
func (d symlinkHook) Hooks() ([]discover.Hook, error) {
|
||||
specificLinks, err := d.getSpecificLinks()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to determine specific links: %v", err)
|
||||
}
|
||||
|
||||
csvSymlinks := d.getCSVFileSymlinks()
|
||||
|
||||
return discover.CreateCreateSymlinkHook(
|
||||
d.nvidiaCDIHookPath,
|
||||
append(csvSymlinks, specificLinks...),
|
||||
d.getCSVFileSymlinks(),
|
||||
).Hooks()
|
||||
}
|
||||
|
||||
// getSpecificLinks returns the required specic links that need to be created
|
||||
func (d symlinkHook) getSpecificLinks() ([]string, error) {
|
||||
mounts, err := d.mountsFrom.Mounts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to discover mounts for ldcache update: %v", err)
|
||||
}
|
||||
|
||||
linkProcessed := make(map[string]bool)
|
||||
var links []string
|
||||
for _, m := range mounts {
|
||||
var target string
|
||||
var link string
|
||||
|
||||
lib := filepath.Base(m.Path)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(lib, "libcuda.so"):
|
||||
// XXX Many applications wrongly assume that libcuda.so exists (e.g. with dlopen).
|
||||
target = "libcuda.so.1"
|
||||
link = "libcuda.so"
|
||||
case strings.HasPrefix(lib, "libGLX_nvidia.so"):
|
||||
// XXX GLVND requires this symlink for indirect GLX support.
|
||||
target = lib
|
||||
link = "libGLX_indirect.so.0"
|
||||
case strings.HasPrefix(lib, "libnvidia-opticalflow.so"):
|
||||
// XXX Fix missing symlink for libnvidia-opticalflow.so.
|
||||
target = "libnvidia-opticalflow.so.1"
|
||||
link = "libnvidia-opticalflow.so"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if linkProcessed[link] {
|
||||
continue
|
||||
}
|
||||
linkProcessed[link] = true
|
||||
|
||||
linkPath := filepath.Join(filepath.Dir(m.Path), link)
|
||||
links = append(links, fmt.Sprintf("%v::%v", target, linkPath))
|
||||
}
|
||||
|
||||
return links, nil
|
||||
}
|
||||
|
||||
// getSymlinkCandidates returns a list of symlinks that are candidates for being created.
|
||||
func (d symlinkHook) getSymlinkCandidates() []string {
|
||||
var candidates []string
|
||||
|
||||
@@ -19,8 +19,14 @@ package engine
|
||||
// Interface defines the API for a runtime config updater.
|
||||
type Interface interface {
|
||||
DefaultRuntime() string
|
||||
AddRuntime(string, string, bool, ...map[string]interface{}) error
|
||||
AddRuntime(string, string, bool) error
|
||||
Set(string, interface{})
|
||||
RemoveRuntime(string) error
|
||||
Save(string) (int64, error)
|
||||
GetRuntimeConfig(string) (RuntimeConfig, error)
|
||||
}
|
||||
|
||||
// RuntimeConfig defines the interface to query container runtime handler configuration
|
||||
type RuntimeConfig interface {
|
||||
GetBinaryPath() string
|
||||
}
|
||||
|
||||
@@ -19,9 +19,8 @@ package containerd
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
// ConfigV1 represents a version 1 containerd config
|
||||
@@ -30,7 +29,7 @@ type ConfigV1 Config
|
||||
var _ engine.Interface = (*ConfigV1)(nil)
|
||||
|
||||
// AddRuntime adds a runtime to the containerd config
|
||||
func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool, configOverrides ...map[string]interface{}) error {
|
||||
func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
if c == nil || c.Tree == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
@@ -39,18 +38,16 @@ func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool, confi
|
||||
|
||||
config.Set("version", int64(1))
|
||||
|
||||
// By default we extract the runtime options from the runc settings; if this does not exist we get the options from the default runtime specified in the config.
|
||||
runtimeNamesForConfig := []string{"runc"}
|
||||
if name, ok := config.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"}).(string); ok && name != "" {
|
||||
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
|
||||
}
|
||||
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
|
||||
for _, r := range runtimeNamesForConfig {
|
||||
if options, ok := config.GetPath([]string{"plugins", "cri", "containerd", "runtimes", r}).(*toml.Tree); ok {
|
||||
c.Logger.Debugf("using options from runtime %v: %v", r, options.String())
|
||||
options, _ = toml.Load(options.String())
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "runtimes", name}, options)
|
||||
break
|
||||
options := config.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", r})
|
||||
if options == nil {
|
||||
continue
|
||||
}
|
||||
c.Logger.Debugf("using options from runtime %v: %v", r, options)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "runtimes", name}, options.Copy())
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
if config.GetPath([]string{"plugins", "cri", "containerd", "runtimes", name}) == nil {
|
||||
@@ -85,16 +82,6 @@ func (c *ConfigV1) AddRuntime(name string, path string, setAsDefault bool, confi
|
||||
}
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", "default_runtime", "options", "Runtime"}, path)
|
||||
|
||||
defaultRuntimeSubtree := subtreeAtPath(config, "plugins", "cri", "containerd", "default_runtime")
|
||||
if err := defaultRuntimeSubtree.applyOverrides(configOverrides...); err != nil {
|
||||
return fmt.Errorf("failed to apply config overrides to default_runtime: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
runtimeSubtree := subtreeAtPath(config, "plugins", "cri", "containerd", "runtimes", name)
|
||||
if err := runtimeSubtree.applyOverrides(configOverrides...); err != nil {
|
||||
return fmt.Errorf("failed to apply config overrides: %w", err)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
@@ -154,14 +141,25 @@ func (c *ConfigV1) RemoveRuntime(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetOption sets the specified containerd option.
|
||||
// Set sets the specified containerd option.
|
||||
func (c *ConfigV1) Set(key string, value interface{}) {
|
||||
config := *c.Tree
|
||||
config.SetPath([]string{"plugins", "cri", "containerd", key}, value)
|
||||
*c.Tree = config
|
||||
}
|
||||
|
||||
// Save wrotes the config to a file
|
||||
// Save writes the config to a file
|
||||
func (c ConfigV1) Save(path string) (int64, error) {
|
||||
return (Config)(c).Save(path)
|
||||
}
|
||||
|
||||
func (c *ConfigV1) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
|
||||
if c == nil || c.Tree == nil {
|
||||
return nil, fmt.Errorf("config is nil")
|
||||
}
|
||||
runtimeData := c.GetSubtreeByPath([]string{"plugins", "cri", "containerd", "runtimes", name})
|
||||
|
||||
return &containerdCfgRuntime{
|
||||
tree: runtimeData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -19,20 +19,20 @@ package containerd
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
func TestAddRuntimeV1(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
configOverrides []map[string]interface{}
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "empty config not default runtime",
|
||||
@@ -53,32 +53,6 @@ func TestAddRuntimeV1(t *testing.T) {
|
||||
`,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "empty config not default runtime with overrides",
|
||||
configOverrides: []map[string]interface{}{
|
||||
{
|
||||
"options": map[string]interface{}{
|
||||
"SystemdCgroup": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfig: `
|
||||
version = 1
|
||||
[plugins]
|
||||
[plugins.cri]
|
||||
[plugins.cri.containerd]
|
||||
[plugins.cri.containerd.runtimes]
|
||||
[plugins.cri.containerd.runtimes.test]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
[plugins.cri.containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
Runtime = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "options from runc are imported",
|
||||
config: `
|
||||
@@ -164,7 +138,7 @@ func TestAddRuntimeV1(t *testing.T) {
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "options from runc take precedence over default runtime",
|
||||
description: "options from the default runtime take precedence over runc",
|
||||
config: `
|
||||
[plugins]
|
||||
[plugins.cri]
|
||||
@@ -212,34 +186,34 @@ func TestAddRuntimeV1(t *testing.T) {
|
||||
BinaryName = "/usr/bin/default"
|
||||
SystemdCgroup = false
|
||||
[plugins.cri.containerd.runtimes.test]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = "defaultengine"
|
||||
runtime_root = "defaultroot"
|
||||
runtime_type = "defaulttype"
|
||||
[plugins.cri.containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
Runtime = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
SystemdCgroup = false
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
config, err := toml.Load(tc.config)
|
||||
cfg, err := toml.Load(tc.config)
|
||||
require.NoError(t, err)
|
||||
expectedConfig, err := toml.Load(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: config,
|
||||
Tree: cfg,
|
||||
}
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault, tc.configOverrides...)
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), config.String())
|
||||
require.EqualValues(t, expectedConfig.String(), cfg.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@ package containerd
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
// AddRuntime adds a runtime to the containerd config
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool, configOverrides ...map[string]interface{}) error {
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
if c == nil || c.Tree == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
@@ -33,18 +32,15 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool, configO
|
||||
|
||||
config.Set("version", int64(2))
|
||||
|
||||
// By default we extract the runtime options from the runc settings; if this does not exist we get the options from the default runtime specified in the config.
|
||||
runtimeNamesForConfig := []string{"runc"}
|
||||
if name, ok := config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}).(string); ok && name != "" {
|
||||
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
|
||||
}
|
||||
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
|
||||
for _, r := range runtimeNamesForConfig {
|
||||
if options, ok := config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", r}).(*toml.Tree); ok {
|
||||
c.Logger.Debugf("using options from runtime %v: %v", r, options.String())
|
||||
options, _ = toml.Load(options.String())
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}, options)
|
||||
break
|
||||
options := config.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", r})
|
||||
if options == nil {
|
||||
continue
|
||||
}
|
||||
c.Logger.Debugf("using options from runtime %v: %v", r, options)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}, options.Copy())
|
||||
break
|
||||
}
|
||||
|
||||
if config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}) == nil {
|
||||
@@ -70,11 +66,6 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool, configO
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}, name)
|
||||
}
|
||||
|
||||
runtimeSubtree := subtreeAtPath(config, "plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name)
|
||||
if err := runtimeSubtree.applyOverrides(configOverrides...); err != nil {
|
||||
return fmt.Errorf("failed to apply config overrides: %w", err)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
return nil
|
||||
}
|
||||
@@ -151,15 +142,3 @@ func (c *Config) RemoveRuntime(name string) error {
|
||||
*c.Tree = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save writes the config to the specified path
|
||||
func (c Config) Save(path string) (int64, error) {
|
||||
config := c.Tree
|
||||
output, err := config.Marshal()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to convert to TOML: %v", err)
|
||||
}
|
||||
|
||||
n, err := engine.Config(path).Write(output)
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
@@ -19,20 +19,20 @@ package containerd
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
func TestAddRuntime(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
configOverrides []map[string]interface{}
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "empty config not default runtime",
|
||||
@@ -52,31 +52,6 @@ func TestAddRuntime(t *testing.T) {
|
||||
`,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "empty config not default runtime with overrides",
|
||||
configOverrides: []map[string]interface{}{
|
||||
{
|
||||
"options": map[string]interface{}{
|
||||
"SystemdCgroup": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfig: `
|
||||
version = 2
|
||||
[plugins]
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "options from runc are imported",
|
||||
config: `
|
||||
@@ -162,7 +137,7 @@ func TestAddRuntime(t *testing.T) {
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "options from runc take precedence over default runtime",
|
||||
description: "options from the default runtime take precedence over runc",
|
||||
config: `
|
||||
version = 2
|
||||
[plugins]
|
||||
@@ -211,33 +186,129 @@ func TestAddRuntime(t *testing.T) {
|
||||
BinaryName = "/usr/bin/default"
|
||||
SystemdCgroup = false
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = "defaultengine"
|
||||
runtime_root = "defaultroot"
|
||||
runtime_type = "defaulttype"
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
SystemdCgroup = false
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
config, err := toml.Load(tc.config)
|
||||
cfg, err := toml.Load(tc.config)
|
||||
require.NoError(t, err)
|
||||
expectedConfig, err := toml.Load(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &Config{
|
||||
Logger: logger,
|
||||
Tree: config,
|
||||
Tree: cfg,
|
||||
}
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault, tc.configOverrides...)
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), config.String())
|
||||
require.EqualValues(t, expectedConfig.String(), cfg.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRuntimeConfig(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
config := `
|
||||
version = 2
|
||||
[plugins]
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
default_runtime_name = "nvidia"
|
||||
disable_snapshot_annotations = true
|
||||
discard_unpacked_layers = false
|
||||
ignore_blockio_not_enabled_errors = false
|
||||
ignore_rdt_not_enabled_errors = false
|
||||
no_pivot = false
|
||||
snapshotter = "overlayfs"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
|
||||
base_runtime_spec = ""
|
||||
cni_conf_dir = ""
|
||||
cni_max_conf_num = 0
|
||||
container_annotations = []
|
||||
pod_annotations = []
|
||||
privileged_without_host_devices = false
|
||||
privileged_without_host_devices_all_devices_allowed = false
|
||||
runtime_engine = ""
|
||||
runtime_path = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
sandbox_mode = ""
|
||||
snapshotter = ""
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
|
||||
base_runtime_spec = ""
|
||||
cni_conf_dir = ""
|
||||
cni_max_conf_num = 0
|
||||
container_annotations = []
|
||||
pod_annotations = []
|
||||
privileged_without_host_devices = false
|
||||
privileged_without_host_devices_all_devices_allowed = false
|
||||
runtime_engine = ""
|
||||
runtime_path = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
sandbox_mode = "podsandbox"
|
||||
snapshotter = ""
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
|
||||
BinaryName = "/usr/bin/runc"
|
||||
CriuImagePath = ""
|
||||
CriuPath = ""
|
||||
CriuWorkPath = ""
|
||||
IoGid = 0
|
||||
IoUid = 0
|
||||
NoNewKeyring = false
|
||||
NoPivotRoot = false
|
||||
Root = ""
|
||||
ShimCgroup = ""
|
||||
SystemdCgroup = false
|
||||
`
|
||||
testCases := []struct {
|
||||
description string
|
||||
runtime string
|
||||
expected string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "valid runtime config, existing runtime",
|
||||
runtime: "runc",
|
||||
expected: "/usr/bin/runc",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "valid runtime config, non-existing runtime",
|
||||
runtime: "some-other-runtime",
|
||||
expected: "",
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg, err := toml.Load(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
}
|
||||
rc, err := c.GetRuntimeConfig(tc.runtime)
|
||||
require.Equal(t, tc.expectedError, err)
|
||||
require.Equal(t, tc.expected, rc.GetBinaryPath())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"github.com/pelletier/go-toml"
|
||||
"fmt"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
// Config represents the containerd config
|
||||
@@ -34,16 +35,106 @@ type Config struct {
|
||||
|
||||
var _ engine.Interface = (*Config)(nil)
|
||||
|
||||
type containerdCfgRuntime struct {
|
||||
tree *toml.Tree
|
||||
}
|
||||
|
||||
var _ engine.RuntimeConfig = (*containerdCfgRuntime)(nil)
|
||||
|
||||
// GetBinaryPath retrieves the path to the low-level runtime binary for a runtime.
|
||||
// If no path is available, the empty string is returned.
|
||||
func (c *containerdCfgRuntime) GetBinaryPath() string {
|
||||
if c == nil || c.tree == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
binPath, _ := c.tree.GetPath([]string{"options", "BinaryName"}).(string)
|
||||
return binPath
|
||||
}
|
||||
|
||||
// New creates a containerd config with the specified options
|
||||
func New(opts ...Option) (engine.Interface, error) {
|
||||
b := &builder{}
|
||||
b := &builder{
|
||||
runtimeType: defaultRuntimeType,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
|
||||
if b.logger == nil {
|
||||
b.logger = logger.New()
|
||||
}
|
||||
if b.configSource == nil {
|
||||
b.configSource = toml.FromFile(b.path)
|
||||
}
|
||||
|
||||
return b.build()
|
||||
tomlConfig, err := b.configSource.Load()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load config: %v", err)
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Tree: tomlConfig,
|
||||
Logger: b.logger,
|
||||
RuntimeType: b.runtimeType,
|
||||
UseDefaultRuntimeName: b.useLegacyConfig,
|
||||
ContainerAnnotations: b.containerAnnotations,
|
||||
}
|
||||
|
||||
version, err := cfg.parseVersion(b.useLegacyConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config version: %v", err)
|
||||
}
|
||||
switch version {
|
||||
case 1:
|
||||
return (*ConfigV1)(cfg), nil
|
||||
case 2:
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported config version: %v", version)
|
||||
}
|
||||
|
||||
// parseVersion returns the version of the config
|
||||
func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
|
||||
defaultVersion := 2
|
||||
if useLegacyConfig {
|
||||
defaultVersion = 1
|
||||
}
|
||||
|
||||
switch v := c.Get("version").(type) {
|
||||
case nil:
|
||||
switch len(c.Keys()) {
|
||||
case 0: // No config exists, or the config file is empty, use version inferred from containerd
|
||||
return defaultVersion, nil
|
||||
default: // A config file exists, has content, and no version is set
|
||||
return 1, nil
|
||||
}
|
||||
case int64:
|
||||
return int(v), nil
|
||||
default:
|
||||
return -1, fmt.Errorf("unsupported type for version field: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
|
||||
if c == nil || c.Tree == nil {
|
||||
return nil, fmt.Errorf("config is nil")
|
||||
}
|
||||
runtimeData := c.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
|
||||
return &containerdCfgRuntime{
|
||||
tree: runtimeData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CommandLineSource returns the CLI-based containerd config loader
|
||||
func CommandLineSource(hostRoot string) toml.Loader {
|
||||
return toml.FromCommandLine(chrootIfRequired(hostRoot, "containerd", "config", "dump")...)
|
||||
}
|
||||
|
||||
func chrootIfRequired(hostRoot string, commandLine ...string) []string {
|
||||
if hostRoot == "" || hostRoot == "/" {
|
||||
return commandLine
|
||||
}
|
||||
|
||||
return append([]string{"chroot", hostRoot}, commandLine...)
|
||||
}
|
||||
|
||||
@@ -17,13 +17,8 @@
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,6 +27,7 @@ const (
|
||||
|
||||
type builder struct {
|
||||
logger logger.Interface
|
||||
configSource toml.Loader
|
||||
path string
|
||||
runtimeType string
|
||||
useLegacyConfig bool
|
||||
@@ -55,6 +51,13 @@ func WithPath(path string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigSource sets the source for the config.
|
||||
func WithConfigSource(configSource toml.Loader) Option {
|
||||
return func(b *builder) {
|
||||
b.configSource = configSource
|
||||
}
|
||||
}
|
||||
|
||||
// WithRuntimeType sets the runtime type for the config builder
|
||||
func WithRuntimeType(runtimeType string) Option {
|
||||
return func(b *builder) {
|
||||
@@ -75,82 +78,3 @@ func WithContainerAnnotations(containerAnnotations ...string) Option {
|
||||
b.containerAnnotations = containerAnnotations
|
||||
}
|
||||
}
|
||||
|
||||
func (b *builder) build() (engine.Interface, error) {
|
||||
if b.path == "" {
|
||||
return nil, fmt.Errorf("config path is empty")
|
||||
}
|
||||
|
||||
if b.runtimeType == "" {
|
||||
b.runtimeType = defaultRuntimeType
|
||||
}
|
||||
|
||||
config, err := b.loadConfig(b.path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load config: %v", err)
|
||||
}
|
||||
config.Logger = b.logger
|
||||
config.RuntimeType = b.runtimeType
|
||||
config.UseDefaultRuntimeName = !b.useLegacyConfig
|
||||
config.ContainerAnnotations = b.containerAnnotations
|
||||
|
||||
version, err := config.parseVersion(b.useLegacyConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config version: %v", err)
|
||||
}
|
||||
switch version {
|
||||
case 1:
|
||||
return (*ConfigV1)(config), nil
|
||||
case 2:
|
||||
return config, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported config version: %v", version)
|
||||
}
|
||||
|
||||
// loadConfig loads the containerd config from disk
|
||||
func (b *builder) loadConfig(config string) (*Config, error) {
|
||||
info, err := os.Stat(config)
|
||||
if os.IsExist(err) && info.IsDir() {
|
||||
return nil, fmt.Errorf("config file is a directory")
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
b.logger.Infof("Config file does not exist; using empty config")
|
||||
config = "/dev/null"
|
||||
} else {
|
||||
b.logger.Infof("Loading config from %v", config)
|
||||
}
|
||||
|
||||
tomlConfig, err := toml.LoadFile(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := Config{
|
||||
Tree: tomlConfig,
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// parseVersion returns the version of the config
|
||||
func (c *Config) parseVersion(useLegacyConfig bool) (int, error) {
|
||||
defaultVersion := 2
|
||||
if useLegacyConfig {
|
||||
defaultVersion = 1
|
||||
}
|
||||
|
||||
switch v := c.Get("version").(type) {
|
||||
case nil:
|
||||
switch len(c.Keys()) {
|
||||
case 0: // No config exists, or the config file is empty, use version inferred from containerd
|
||||
return defaultVersion, nil
|
||||
default: // A config file exists, has content, and no version is set
|
||||
return 1, nil
|
||||
}
|
||||
case int64:
|
||||
return int(v), nil
|
||||
default:
|
||||
return -1, fmt.Errorf("unsupported type for version field: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
# Copyright 2024 NVIDIA CORPORATION
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
// tomlTree is an alias for toml.Tree that allows for extensions.
|
||||
type tomlTree toml.Tree
|
||||
|
||||
func subtreeAtPath(c toml.Tree, path ...string) *tomlTree {
|
||||
tree := c.GetPath(path).(*toml.Tree)
|
||||
return (*tomlTree)(tree)
|
||||
}
|
||||
|
||||
func (t *tomlTree) insert(other map[string]interface{}) error {
|
||||
|
||||
for key, value := range other {
|
||||
if insertsubtree, ok := value.(map[string]interface{}); ok {
|
||||
subtree := (*toml.Tree)(t).Get(key).(*toml.Tree)
|
||||
return (*tomlTree)(subtree).insert(insertsubtree)
|
||||
}
|
||||
(*toml.Tree)(t).Set(key, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tomlTree) applyOverrides(overrides ...map[string]interface{}) error {
|
||||
for _, override := range overrides {
|
||||
subconfig, err := toml.TreeFromMap(override)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid toml config: %w", err)
|
||||
}
|
||||
if err := t.insert(subconfig.ToMap()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -19,10 +19,9 @@ package crio
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
// Config represents the cri-o config
|
||||
@@ -31,6 +30,23 @@ type Config struct {
|
||||
Logger logger.Interface
|
||||
}
|
||||
|
||||
type crioRuntime struct {
|
||||
tree *toml.Tree
|
||||
}
|
||||
|
||||
var _ engine.RuntimeConfig = (*crioRuntime)(nil)
|
||||
|
||||
// GetBinaryPath retrieves the path to the low-level runtime binary for a runtime.
|
||||
// If no path is available, the empty string is returned.
|
||||
func (c *crioRuntime) GetBinaryPath() string {
|
||||
if c.tree != nil {
|
||||
if binaryPath, ok := c.tree.GetPath([]string{"runtime_path"}).(string); ok {
|
||||
return binaryPath
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var _ engine.Interface = (*Config)(nil)
|
||||
|
||||
// New creates a cri-o config with the specified options
|
||||
@@ -39,23 +55,34 @@ func New(opts ...Option) (engine.Interface, error) {
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
if b.logger == nil {
|
||||
b.logger = logger.New()
|
||||
}
|
||||
if b.configSource == nil {
|
||||
b.configSource = toml.FromFile(b.path)
|
||||
}
|
||||
|
||||
return b.build()
|
||||
tomlConfig, err := b.configSource.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := Config{
|
||||
Tree: tomlConfig,
|
||||
Logger: b.logger,
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// AddRuntime adds a new runtime to the crio config
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool, _ ...map[string]interface{}) error {
|
||||
func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("config is nil")
|
||||
}
|
||||
|
||||
config := *c.Tree
|
||||
|
||||
// By default we extract the runtime options from the runc settings; if this does not exist we get the options from the default runtime specified in the config.
|
||||
runtimeNamesForConfig := []string{"runc"}
|
||||
if name, ok := config.GetPath([]string{"crio", "runtime", "default_runtime"}).(string); ok && name != "" {
|
||||
runtimeNamesForConfig = append(runtimeNamesForConfig, name)
|
||||
}
|
||||
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
|
||||
for _, r := range runtimeNamesForConfig {
|
||||
if options, ok := config.GetPath([]string{"crio", "runtime", "runtimes", r}).(*toml.Tree); ok {
|
||||
c.Logger.Debugf("using options from runtime %v: %v", r, options.String())
|
||||
@@ -116,21 +143,28 @@ func (c *Config) RemoveRuntime(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the specified cri-o option.
|
||||
func (c *Config) Set(key string, value interface{}) {
|
||||
config := *c.Tree
|
||||
config.Set(key, value)
|
||||
*c.Tree = config
|
||||
func (c *Config) GetRuntimeConfig(name string) (engine.RuntimeConfig, error) {
|
||||
if c == nil || c.Tree == nil {
|
||||
return nil, fmt.Errorf("config is nil")
|
||||
}
|
||||
runtimeData := c.GetSubtreeByPath([]string{"crio", "runtime", "runtimes", name})
|
||||
return &crioRuntime{
|
||||
tree: runtimeData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Save writes the config to the specified path
|
||||
func (c Config) Save(path string) (int64, error) {
|
||||
config := c.Tree
|
||||
output, err := config.Marshal()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to convert to TOML: %v", err)
|
||||
// CommandLineSource returns the CLI-based crio config loader
|
||||
func CommandLineSource(hostRoot string) toml.Loader {
|
||||
return toml.LoadFirst(
|
||||
toml.FromCommandLine(chrootIfRequired(hostRoot, "crio", "status", "config")...),
|
||||
toml.FromCommandLine(chrootIfRequired(hostRoot, "crio-status", "config")...),
|
||||
)
|
||||
}
|
||||
|
||||
func chrootIfRequired(hostRoot string, commandLine ...string) []string {
|
||||
if hostRoot == "" || hostRoot == "/" {
|
||||
return commandLine
|
||||
}
|
||||
|
||||
n, err := engine.Config(path).Write(output)
|
||||
return int64(n), err
|
||||
return append([]string{"chroot", hostRoot}, commandLine...)
|
||||
}
|
||||
|
||||
@@ -19,20 +19,20 @@ package crio
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
func TestAddRuntime(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
configOverrides []map[string]interface{}
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
description string
|
||||
config string
|
||||
setAsDefault bool
|
||||
expectedConfig string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "empty config not default runtime",
|
||||
@@ -91,7 +91,7 @@ func TestAddRuntime(t *testing.T) {
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "options from runc take precedence over default runtime",
|
||||
description: "options from the default runtime take precedence over runc",
|
||||
config: `
|
||||
[crio]
|
||||
[crio.runtime]
|
||||
@@ -120,27 +120,85 @@ func TestAddRuntime(t *testing.T) {
|
||||
[crio.runtime.runtimes.test]
|
||||
runtime_path = "/usr/bin/test"
|
||||
runtime_type = "oci"
|
||||
runc_option = "option"
|
||||
default_option = "option"
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
config, err := toml.Load(tc.config)
|
||||
cfg, err := toml.Load(tc.config)
|
||||
require.NoError(t, err)
|
||||
expectedConfig, err := toml.Load(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &Config{
|
||||
Logger: logger,
|
||||
Tree: config,
|
||||
Tree: cfg,
|
||||
}
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault, tc.configOverrides...)
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), config.String())
|
||||
require.EqualValues(t, expectedConfig.String(), cfg.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRuntimeConfig(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
config := `
|
||||
[crio.image]
|
||||
signature_policy = "/etc/crio/policy.json"
|
||||
|
||||
[crio.runtime]
|
||||
default_runtime = "crun"
|
||||
|
||||
[crio.runtime.runtimes.crun]
|
||||
runtime_path = "/usr/libexec/crio/crun"
|
||||
runtime_root = "/run/crun"
|
||||
monitor_path = "/usr/libexec/crio/conmon"
|
||||
allowed_annotations = [
|
||||
"io.containers.trace-syscall",
|
||||
]
|
||||
|
||||
[crio.runtime.runtimes.runc]
|
||||
runtime_path = "/usr/libexec/crio/runc"
|
||||
runtime_root = "/run/runc"
|
||||
monitor_path = "/usr/libexec/crio/conmon"
|
||||
`
|
||||
testCases := []struct {
|
||||
description string
|
||||
runtime string
|
||||
expected string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "valid runtime config, existing runtime",
|
||||
runtime: "crun",
|
||||
expected: "/usr/libexec/crio/crun",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "valid runtime config, non-existing runtime",
|
||||
runtime: "some-other-runtime",
|
||||
expected: "",
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg, err := toml.Load(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
}
|
||||
|
||||
rc, err := c.GetRuntimeConfig(tc.runtime)
|
||||
require.Equal(t, tc.expectedError, err)
|
||||
require.Equal(t, tc.expected, rc.GetBinaryPath())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user