mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2025-06-26 18:18:24 +00:00
Compare commits
205 Commits
v1.17.0
...
pull-reque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54ea4a24a7 | ||
|
|
e436533a6f | ||
|
|
0f299c3431 | ||
|
|
f852043078 | ||
|
|
ef0b16bc24 | ||
|
|
225dfec83f | ||
|
|
03c48a6824 | ||
|
|
6530826293 | ||
|
|
971fd195b3 | ||
|
|
3b10afd0fe | ||
|
|
6b7ed26fba | ||
|
|
8d5f1e2427 | ||
|
|
d82a9ccd89 | ||
|
|
8ac213e3e6 | ||
|
|
0128762832 | ||
|
|
d7b150a2e6 | ||
|
|
57c917e3b1 | ||
|
|
bc9ec77fdd | ||
|
|
82f2eb7b73 | ||
|
|
712d829018 | ||
|
|
598b9740fc | ||
|
|
968e2ccca4 | ||
|
|
aff9301f2e | ||
|
|
011fb72330 | ||
|
|
2adef9903e | ||
|
|
70b1f5af98 | ||
|
|
c9422f12b3 | ||
|
|
b7fbd56f7e | ||
|
|
bd87c009ba | ||
|
|
fc65d3a784 | ||
|
|
52b9631333 | ||
|
|
9429fbac5f | ||
|
|
04e9bf4ac1 | ||
|
|
3ceaf1f85c | ||
|
|
9f0c1042c4 | ||
|
|
352b55c8ce | ||
|
|
b13139793b | ||
|
|
05f44b7752 | ||
|
|
a109f28cb6 | ||
|
|
65b575fa96 | ||
|
|
6e413d8445 | ||
|
|
5d9b27f107 | ||
|
|
767aee913d | ||
|
|
25bac4c62a | ||
|
|
7e371aa024 | ||
|
|
bc2b937574 | ||
|
|
ae8561b509 | ||
|
|
64633903c7 | ||
|
|
1273f879cc | ||
|
|
8713a173fe | ||
|
|
e6d99934f8 | ||
|
|
0cf2d60406 | ||
|
|
0e759d4ad8 | ||
|
|
9e85fb54fc | ||
|
|
ce79d01cf5 | ||
|
|
5d27489d18 | ||
|
|
d80e8b9733 | ||
|
|
a72b9cd12c | ||
|
|
c2f4ac0959 | ||
|
|
5db5ddb31b | ||
|
|
ff1897f2e4 | ||
|
|
88f0157cae | ||
|
|
27f1738c3d | ||
|
|
f52df9522e | ||
|
|
edf79d6e8b | ||
|
|
6aadd6ed21 | ||
|
|
a1d60910b6 | ||
|
|
03f5a09aec | ||
|
|
1f2232fc5f | ||
|
|
b19f5d8f7d | ||
|
|
fa1379c6d5 | ||
|
|
4ded119c31 | ||
|
|
e0813e0c4d | ||
|
|
4add0a6bf2 | ||
|
|
fbcf8ef12e | ||
|
|
92472bd0ed | ||
|
|
ac236a80f8 | ||
|
|
b69d98d7cf | ||
|
|
adbe127afd | ||
|
|
82ccae23b1 | ||
|
|
44cf1f4e3b | ||
|
|
1d0777ee01 | ||
|
|
9f82e910f6 | ||
|
|
cb172ba336 | ||
|
|
bbdb13c47d | ||
|
|
03152dba8d | ||
|
|
cf026dce9a | ||
|
|
073fb138d7 | ||
|
|
948bc113f0 | ||
|
|
b457247a0c | ||
|
|
bae4b3ebd3 | ||
|
|
3a06066f6b | ||
|
|
c0292f5048 | ||
|
|
cd8937bc5b | ||
|
|
cec3445318 | ||
|
|
f830653738 | ||
|
|
6e9ff446c8 | ||
|
|
9a07de0ee8 | ||
|
|
517873e97d | ||
|
|
49b45693ee | ||
|
|
78d6cdc7f7 | ||
|
|
61640591ba | ||
|
|
df4c87b877 | ||
|
|
d6c312956b | ||
|
|
51f765dd71 | ||
|
|
d8cd5438e4 | ||
|
|
5ed25bb375 | ||
|
|
a7786d4d41 | ||
|
|
be1ac24f2a | ||
|
|
dd86f598b5 | ||
|
|
2b417c1a9a | ||
|
|
e89be14c86 | ||
|
|
f625242ed6 | ||
|
|
df73db7e1e | ||
|
|
89f33bdf71 | ||
|
|
6834d5e0a4 | ||
|
|
c91a1b1dc8 | ||
|
|
96d91df78e | ||
|
|
a990860bfa | ||
|
|
bf9d618ff2 | ||
|
|
7ae5c2901f | ||
|
|
6b236746ce | ||
|
|
ed3b52eb8d | ||
|
|
1176430278 | ||
|
|
c22f3bd56c | ||
|
|
6375e832ff | ||
|
|
991b9c222f | ||
|
|
fdad3927b4 | ||
|
|
6bd292eff8 | ||
|
|
9429ce039b | ||
|
|
d953bbb977 | ||
|
|
5cbf3f82d9 | ||
|
|
69375d7889 | ||
|
|
9753096398 | ||
|
|
76edd1d7ca | ||
|
|
a6476193c8 | ||
|
|
d75b1adeda | ||
|
|
9aa2f67a22 | ||
|
|
b89e7ca849 | ||
|
|
7c5ed8157b | ||
|
|
a401e4a5a6 | ||
|
|
2bc4201205 | ||
|
|
cb52e779ba | ||
|
|
8cc672bed9 | ||
|
|
be001d938c | ||
|
|
d584f9a40b | ||
|
|
11acbe7ca2 | ||
|
|
95bda3ad72 | ||
|
|
6e4b0632c5 | ||
|
|
e4be26ff55 | ||
|
|
8e6b57b38a | ||
|
|
c935779693 | ||
|
|
4b43a29957 | ||
|
|
b3b0d107de | ||
|
|
d22fca51b8 | ||
|
|
adad9b9c92 | ||
|
|
b6d360fbbe | ||
|
|
5699ec1cff | ||
|
|
ec182705f1 | ||
|
|
7598fe14bc | ||
|
|
4fc181a418 | ||
|
|
4bf5894833 | ||
|
|
272f891204 | ||
|
|
4b352e6bac | ||
|
|
178369eb8e | ||
|
|
3c012499db | ||
|
|
6e883bf10a | ||
|
|
cc61f186a8 | ||
|
|
aac0a62528 | ||
|
|
2529aebd6c | ||
|
|
784917a0d9 | ||
|
|
1330467652 | ||
|
|
22035d4561 | ||
|
|
9318aaf275 | ||
|
|
70261d84f1 | ||
|
|
f8f506fd85 | ||
|
|
b0a5fb9f86 | ||
|
|
83790dbccd | ||
|
|
7ceae92ac1 | ||
|
|
8dfa61338b | ||
|
|
484cae273c | ||
|
|
6e67bb247b | ||
|
|
8bd7dd7060 | ||
|
|
e25c052aa6 | ||
|
|
21ba9932d0 | ||
|
|
dcd65a6410 | ||
|
|
8603d605aa | ||
|
|
e1efa28fe6 | ||
|
|
75a934e11c | ||
|
|
43aca46f10 | ||
|
|
00f1d5a627 | ||
|
|
c0764366d9 | ||
|
|
1afada7de5 | ||
|
|
5c3ffc2fba | ||
|
|
e188090a92 | ||
|
|
1995925a7d | ||
|
|
46fc47c67a | ||
|
|
b142091234 | ||
|
|
e9b8ad95df | ||
|
|
f77a808105 | ||
|
|
832818084d | ||
|
|
ab0a4eaa19 | ||
|
|
0c687be794 | ||
|
|
8d869acce5 | ||
|
|
324096c979 |
3
.github/copy-pr-bot.yaml
vendored
Normal file
3
.github/copy-pr-bot.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://docs.gha-runners.nvidia.com/apps/copy-pr-bot/#configuration
|
||||
|
||||
enabled: true
|
||||
128
.github/dependabot.yml
vendored
128
.github/dependabot.yml
vendored
@@ -3,63 +3,43 @@
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# main branch
|
||||
- package-ecosystem: "gomod"
|
||||
target-branch: main
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
ignore:
|
||||
- dependency-name: k8s.io/*
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: main
|
||||
directory: "/deployments/container"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
# This defines a specific dependabot rule for the latest release-* branch.
|
||||
target-branch: release-1.16
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
ignore:
|
||||
- dependency-name: k8s.io/*
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: release-1.16
|
||||
directory: "/deployments/container"
|
||||
directories:
|
||||
- "/"
|
||||
- "deployments/devel"
|
||||
- "tests"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
groups:
|
||||
k8sio:
|
||||
patterns:
|
||||
- k8s.io/*
|
||||
exclude-patterns:
|
||||
- k8s.io/klog/*
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
target-branch: main
|
||||
directory: "deployments/devel"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
|
||||
# A dependabot rule to bump the golang version.
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: main
|
||||
directory: "/deployments/devel"
|
||||
directories:
|
||||
# CUDA image
|
||||
- "/deployments/container"
|
||||
# Golang version
|
||||
- "/deployments/devel"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
target-branch: main
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
# Allow dependabot to update the libnvidia-container submodule.
|
||||
- package-ecosystem: "gitsubmodule"
|
||||
@@ -72,3 +52,69 @@ updates:
|
||||
labels:
|
||||
- dependencies
|
||||
- libnvidia-container
|
||||
|
||||
# The release branch(es):
|
||||
- package-ecosystem: "gomod"
|
||||
target-branch: release-1.17
|
||||
directories:
|
||||
- "/"
|
||||
# We don't update development or test dependencies on release branches
|
||||
# - "deployments/devel"
|
||||
# - "tests"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
ignore:
|
||||
# For release branches we only consider patch updates.
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- version-update:semver-minor
|
||||
groups:
|
||||
k8sio:
|
||||
patterns:
|
||||
- k8s.io/*
|
||||
exclude-patterns:
|
||||
- k8s.io/klog/*
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
target-branch: release-1.17
|
||||
directories:
|
||||
# CUDA image
|
||||
- "/deployments/container"
|
||||
# Golang version
|
||||
- "/deployments/devel"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
ignore:
|
||||
# For release branches we only apply patch updates to the golang version.
|
||||
- dependency-name: "*golang*"
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- version-update:semver-minor
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
target-branch: release-1.17
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
labels:
|
||||
- dependencies
|
||||
- maintenance
|
||||
|
||||
# Github actions need to be gh-pages branches.
|
||||
- package-ecosystem: "github-actions"
|
||||
target-branch: gh-pages
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
53
.github/workflows/ci.yaml
vendored
Normal file
53
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Copyright 2025 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: CI Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "pull-request/[0-9]+"
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
code-scanning:
|
||||
uses: ./.github/workflows/code_scanning.yaml
|
||||
|
||||
variables:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Generate Commit Short SHA
|
||||
id: version
|
||||
run: echo "version=$(echo $GITHUB_SHA | cut -c1-8)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
golang:
|
||||
uses: ./.github/workflows/golang.yaml
|
||||
|
||||
image:
|
||||
uses: ./.github/workflows/image.yaml
|
||||
needs: [variables, golang, code-scanning]
|
||||
secrets: inherit
|
||||
with:
|
||||
version: ${{ needs.variables.outputs.version }}
|
||||
build_multi_arch_images: ${{ github.ref_name == 'main' || startsWith(github.ref_name, 'release-') }}
|
||||
|
||||
e2e-test:
|
||||
needs: [image, variables]
|
||||
secrets: inherit
|
||||
uses: ./.github/workflows/e2e.yaml
|
||||
with:
|
||||
version: ${{ needs.variables.outputs.version }}
|
||||
5
.github/workflows/code_scanning.yaml
vendored
5
.github/workflows/code_scanning.yaml
vendored
@@ -15,6 +15,7 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
@@ -22,10 +23,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
||||
98
.github/workflows/e2e.yaml
vendored
Normal file
98
.github/workflows/e2e.yaml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
# Copyright 2025 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: End-to-end Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
AWS_ACCESS_KEY_ID:
|
||||
required: true
|
||||
AWS_SECRET_ACCESS_KEY:
|
||||
required: true
|
||||
AWS_SSH_KEY:
|
||||
required: true
|
||||
E2E_SSH_USER:
|
||||
required: true
|
||||
SLACK_BOT_TOKEN:
|
||||
required: true
|
||||
SLACK_CHANNEL_ID:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
runs-on: linux-amd64-cpu4
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Calculate build vars
|
||||
id: vars
|
||||
run: |
|
||||
echo "COMMIT_SHORT_SHA=${GITHUB_SHA:0:8}" >> $GITHUB_ENV
|
||||
echo "LOWERCASE_REPO_OWNER=$(echo "${GITHUB_REPOSITORY_OWNER}" | awk '{print tolower($0)}')" >> $GITHUB_ENV
|
||||
GOLANG_VERSION=$(./hack/golang-version.sh)
|
||||
echo "GOLANG_VERSION=${GOLANG_VERSION##GOLANG_VERSION := }" >> $GITHUB_ENV
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
- name: Set up Holodeck
|
||||
uses: NVIDIA/holodeck@v0.2.6
|
||||
with:
|
||||
aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws_ssh_key: ${{ secrets.AWS_SSH_KEY }}
|
||||
holodeck_config: "tests/e2e/infra/aws.yaml"
|
||||
|
||||
- name: Get public dns name
|
||||
id: holodeck_public_dns_name
|
||||
uses: mikefarah/yq@master
|
||||
with:
|
||||
cmd: yq '.status.properties[] | select(.name == "public-dns-name") | .value' /github/workspace/.cache/holodeck.yaml
|
||||
|
||||
- name: Run e2e tests
|
||||
env:
|
||||
IMAGE_NAME: ghcr.io/nvidia/container-toolkit
|
||||
VERSION: ${{ inputs.version }}
|
||||
SSH_KEY: ${{ secrets.AWS_SSH_KEY }}
|
||||
E2E_SSH_USER: ${{ secrets.E2E_SSH_USER }}
|
||||
E2E_SSH_HOST: ${{ steps.holodeck_public_dns_name.outputs.result }}
|
||||
E2E_INSTALL_CTK: "true"
|
||||
run: |
|
||||
e2e_ssh_key=$(mktemp)
|
||||
echo "$SSH_KEY" > "$e2e_ssh_key"
|
||||
chmod 600 "$e2e_ssh_key"
|
||||
export E2E_SSH_KEY="$e2e_ssh_key"
|
||||
|
||||
make -f tests/e2e/Makefile test
|
||||
|
||||
- name: Send Slack alert notification
|
||||
if: ${{ failure() }}
|
||||
uses: slackapi/slack-github-action@v2.0.0
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload: |
|
||||
channel: ${{ secrets.SLACK_CHANNEL_ID }}
|
||||
text: |
|
||||
:x: On repository ${{ github.repository }}, the Workflow *${{ github.workflow }}* has failed.
|
||||
|
||||
Details: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
5
.github/workflows/golang.yaml
vendored
5
.github/workflows/golang.yaml
vendored
@@ -15,6 +15,7 @@
|
||||
name: Golang
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
@@ -22,10 +23,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
check:
|
||||
|
||||
68
.github/workflows/image.yaml
vendored
68
.github/workflows/image.yaml
vendored
@@ -16,21 +16,18 @@
|
||||
name: image
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
build_multi_arch_images:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
packages:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: linux-amd64-cpu4
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
@@ -41,7 +38,7 @@ jobs:
|
||||
- centos7-x86_64
|
||||
- centos8-ppc64le
|
||||
ispr:
|
||||
- ${{github.event_name == 'pull_request'}}
|
||||
- ${{ github.ref_name != 'main' && !startsWith( github.ref_name, 'release-' ) }}
|
||||
exclude:
|
||||
- ispr: true
|
||||
target: ubuntu18.04-arm64
|
||||
@@ -52,18 +49,25 @@ jobs:
|
||||
- ispr: true
|
||||
target: centos8-ppc64le
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: Check out code
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:master
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: build ${{ matrix.target }} packages
|
||||
run: |
|
||||
sudo apt-get install -y coreutils build-essential sed git bash make
|
||||
echo "Building packages"
|
||||
./scripts/build-packages.sh ${{ matrix.target }}
|
||||
|
||||
- name: 'Upload Artifacts'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -72,7 +76,7 @@ jobs:
|
||||
path: ${{ github.workspace }}/dist/*
|
||||
|
||||
image:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: linux-amd64-cpu4
|
||||
strategy:
|
||||
matrix:
|
||||
dist:
|
||||
@@ -80,7 +84,7 @@ jobs:
|
||||
- ubi8
|
||||
- packaging
|
||||
ispr:
|
||||
- ${{github.event_name == 'pull_request'}}
|
||||
- ${{ github.ref_name != 'main' && !startsWith( github.ref_name, 'release-' ) }}
|
||||
exclude:
|
||||
- ispr: true
|
||||
dist: ubi8
|
||||
@@ -88,34 +92,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: Check out code
|
||||
- name: Calculate build vars
|
||||
id: vars
|
||||
run: |
|
||||
echo "COMMIT_SHORT_SHA=${GITHUB_SHA:0:8}" >> $GITHUB_ENV
|
||||
echo "LOWERCASE_REPO_OWNER=$(echo "${GITHUB_REPOSITORY_OWNER}" | awk '{print tolower($0)}')" >> $GITHUB_ENV
|
||||
REPO_FULL_NAME="${{ github.event.pull_request.head.repo.full_name }}"
|
||||
echo "${REPO_FULL_NAME}"
|
||||
echo "LABEL_IMAGE_SOURCE=https://github.com/${REPO_FULL_NAME}" >> $GITHUB_ENV
|
||||
|
||||
PUSH_ON_BUILD="false"
|
||||
BUILD_MULTI_ARCH_IMAGES="false"
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
if [[ "${{ github.actor }}" != "dependabot[bot]" && "${{ github.event.pull_request.head.repo.full_name }}" == "${{ github.repository }}" ]]; then
|
||||
# For non-fork PRs that are not created by dependabot we do push images
|
||||
PUSH_ON_BUILD="true"
|
||||
fi
|
||||
elif [[ "${{ github.event_name }}" == "push" ]]; then
|
||||
# On push events we do generate images and enable muilti-arch builds
|
||||
PUSH_ON_BUILD="true"
|
||||
BUILD_MULTI_ARCH_IMAGES="true"
|
||||
fi
|
||||
echo "PUSH_ON_BUILD=${PUSH_ON_BUILD}" >> $GITHUB_ENV
|
||||
echo "BUILD_MULTI_ARCH_IMAGES=${BUILD_MULTI_ARCH_IMAGES}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:master
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Get built packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -129,10 +114,13 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build image
|
||||
env:
|
||||
IMAGE_NAME: ghcr.io/${LOWERCASE_REPO_OWNER}/container-toolkit
|
||||
VERSION: ${COMMIT_SHORT_SHA}
|
||||
IMAGE_NAME: ghcr.io/nvidia/container-toolkit
|
||||
VERSION: ${{ inputs.version }}
|
||||
PUSH_ON_BUILD: "true"
|
||||
BUILD_MULTI_ARCH_IMAGES: ${{ inputs.build_multi_arch_images }}
|
||||
run: |
|
||||
echo "${VERSION}"
|
||||
make -f deployments/container/Makefile build-${{ matrix.dist }}
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,13 +1,13 @@
|
||||
dist
|
||||
artifacts
|
||||
/dist
|
||||
/artifacts
|
||||
*.swp
|
||||
*.swo
|
||||
/coverage.out*
|
||||
/test/output/
|
||||
/tests/output/
|
||||
/nvidia-container-runtime
|
||||
/nvidia-container-runtime.*
|
||||
/nvidia-container-runtime-hook
|
||||
/nvidia-container-toolkit
|
||||
/nvidia-ctk
|
||||
/shared-*
|
||||
/release-*
|
||||
/release-*
|
||||
|
||||
@@ -176,12 +176,6 @@ image-packaging:
|
||||
optional: true
|
||||
|
||||
# Define publish test helpers
|
||||
.test:toolkit:
|
||||
extends:
|
||||
- .integration
|
||||
variables:
|
||||
TEST_CASES: "toolkit"
|
||||
|
||||
.test:docker:
|
||||
extends:
|
||||
- .integration
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,5 +1,35 @@
|
||||
# NVIDIA Container Toolkit Changelog
|
||||
|
||||
## v1.17.4
|
||||
- Disable mounting of compat libs from container by default
|
||||
- Add allow-cuda-compat-libs-from-container feature flag
|
||||
- Skip graphics modifier in CSV mode
|
||||
- Properly pass configSearchPaths to a Driver constructor
|
||||
- Add support for containerd version 3 config
|
||||
- Add string TOML source
|
||||
|
||||
### Changes in libnvidia-container
|
||||
- Add no-cntlibs CLI option to nvidia-container-cli
|
||||
|
||||
### Changes in the Toolkit Container
|
||||
- Bump CUDA base image version to 12.6.3
|
||||
|
||||
## v1.17.3
|
||||
- Only allow host-relative LDConfig paths by default.
|
||||
### Changes in libnvidia-container
|
||||
- Create virtual copy of host ldconfig binary before calling fexecve()
|
||||
|
||||
## v1.17.2
|
||||
- Fixed a bug where legacy images would set imex channels as `all`.
|
||||
|
||||
## v1.17.1
|
||||
- Fixed a bug where specific symlinks existing in a container image could cause a container to fail to start.
|
||||
- Fixed a bug on Tegra-based systems where a container would fail to start.
|
||||
- Fixed a bug where the default container runtime config path was not properly set.
|
||||
|
||||
### Changes in the Toolkit Container
|
||||
- Fallback to using a config file if the current runtime config can not be determined from the command line.
|
||||
|
||||
## v1.17.0
|
||||
- Promote v1.17.0-rc.2 to v1.17.0
|
||||
- Fix bug when using just-in-time CDI spec generation
|
||||
|
||||
@@ -34,7 +34,7 @@ environment variables.
|
||||
|
||||
## Testing packages locally
|
||||
|
||||
The [test/release](./test/release/) folder contains documentation on how the installation of local or staged packages can be tested.
|
||||
The [tests/release](./tests/release/) folder contains documentation on how the installation of local or staged packages can be tested.
|
||||
|
||||
|
||||
## Releasing
|
||||
|
||||
29
Makefile
29
Makefile
@@ -90,13 +90,32 @@ goimports:
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
vendor:
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
go mod verify
|
||||
vendor: | mod-tidy mod-vendor mod-verify
|
||||
|
||||
mod-tidy:
|
||||
@for mod in $$(find . -name go.mod -not -path "./testdata/*" -not -path "./third_party/*"); do \
|
||||
echo "Tidying $$mod..."; ( \
|
||||
cd $$(dirname $$mod) && go mod tidy \
|
||||
) || exit 1; \
|
||||
done
|
||||
|
||||
mod-vendor:
|
||||
@for mod in $$(find . -name go.mod -not -path "./testdata/*" -not -path "./third_party/*" -not -path "./deployments/*"); do \
|
||||
echo "Vendoring $$mod..."; ( \
|
||||
cd $$(dirname $$mod) && go mod vendor \
|
||||
) || exit 1; \
|
||||
done
|
||||
|
||||
mod-verify:
|
||||
@for mod in $$(find . -name go.mod -not -path "./testdata/*" -not -path "./third_party/*"); do \
|
||||
echo "Verifying $$mod..."; ( \
|
||||
cd $$(dirname $$mod) && go mod verify | sed 's/^/ /g' \
|
||||
) || exit 1; \
|
||||
done
|
||||
|
||||
|
||||
check-vendor: vendor
|
||||
git diff --quiet HEAD -- go.mod go.sum vendor
|
||||
git diff --exit-code HEAD -- go.mod go.sum vendor
|
||||
|
||||
licenses:
|
||||
go-licenses csv $(MODULE)/...
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/chmod"
|
||||
symlinks "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/create-symlinks"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/cudacompat"
|
||||
ldcache "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-cdi-hook/update-ldcache"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
@@ -32,5 +33,6 @@ func New(logger logger.Interface) []*cli.Command {
|
||||
ldcache.NewCommand(logger),
|
||||
symlinks.NewCommand(logger),
|
||||
chmod.NewCommand(logger),
|
||||
cudacompat.NewCommand(logger),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func (m command) build() *cli.Command {
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "link",
|
||||
Usage: "Specify a specific link to create. The link is specified as target::link",
|
||||
Usage: "Specify a specific link to create. The link is specified as target::link. If the link exists in the container root, it is removed.",
|
||||
Destination: &cfg.links,
|
||||
},
|
||||
// The following flags are testing-only flags.
|
||||
@@ -112,18 +112,19 @@ func (m command) run(c *cli.Context, cfg *config) error {
|
||||
// createLink creates a symbolic link in the specified container root.
|
||||
// This is equivalent to:
|
||||
//
|
||||
// chroot {{ .containerRoot }} ln -s {{ .target }} {{ .link }}
|
||||
// chroot {{ .containerRoot }} ln -f -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.
|
||||
// operation is a no-op.
|
||||
// If a file exists at the link path or the link points to a different target
|
||||
// this file is removed before creating the link.
|
||||
//
|
||||
// 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)
|
||||
exists, err := linkExists(targetPath, linkPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if link exists: %w", err)
|
||||
}
|
||||
@@ -132,17 +133,21 @@ func (m command) createLink(containerRoot string, targetPath string, link string
|
||||
return nil
|
||||
}
|
||||
|
||||
resolvedLinkPath, err := symlink.FollowSymlinkInScope(linkPath, containerRoot)
|
||||
// We resolve the parent of the symlink that we're creating in the container root.
|
||||
// If we resolve the full link path, an existing link at the location itself
|
||||
// is also resolved here and we are unable to force create the link.
|
||||
resolvedLinkParent, err := symlink.FollowSymlinkInScope(filepath.Dir(linkPath), containerRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to follow path for link %v relative to %v: %w", link, containerRoot, err)
|
||||
}
|
||||
resolvedLinkPath := filepath.Join(resolvedLinkParent, filepath.Base(linkPath))
|
||||
|
||||
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(targetPath, resolvedLinkPath)
|
||||
err = symlinks.ForceCreate(targetPath, resolvedLinkPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create symlink: %v", err)
|
||||
}
|
||||
@@ -150,9 +155,9 @@ func (m command) createLink(containerRoot string, targetPath string, link string
|
||||
return 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) {
|
||||
// linkExists checks whether the specified link exists.
|
||||
// A link exists if the path exists, is a symlink, and points to the specified target.
|
||||
func linkExists(target string, link string) (bool, error) {
|
||||
currentTarget, err := symlinks.Resolve(link)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
@@ -163,5 +168,5 @@ func doesLinkExist(target string, link string) (bool, error) {
|
||||
if currentTarget == target {
|
||||
return true, nil
|
||||
}
|
||||
return true, fmt.Errorf("unexpected link target: %s", currentTarget)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
|
||||
)
|
||||
|
||||
func TestDoesLinkExist(t *testing.T) {
|
||||
func TestLinkExist(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
require.NoError(
|
||||
t,
|
||||
@@ -22,21 +22,23 @@ func TestDoesLinkExist(t *testing.T) {
|
||||
),
|
||||
)
|
||||
|
||||
exists, err := doesLinkExist("d", filepath.Join(tmpDir, "/a/b/c"))
|
||||
exists, err := linkExists("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"))
|
||||
exists, err = linkExists("/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)
|
||||
exists, err = linkExists("different-target", filepath.Join(tmpDir, "/a/b/c"))
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
|
||||
_, err = doesLinkExist("/a/b/d", filepath.Join(tmpDir, "/a/b/c"))
|
||||
require.Error(t, err)
|
||||
exists, err = linkExists("/a/b/d", filepath.Join(tmpDir, "/a/b/c"))
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
|
||||
exists, err = doesLinkExist("foo", filepath.Join(tmpDir, "/a/b/does-not-exist"))
|
||||
exists, err = linkExists("foo", filepath.Join(tmpDir, "/a/b/does-not-exist"))
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
}
|
||||
@@ -190,43 +192,55 @@ func TestCreateLinkAbsolutePath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLinkAlreadyExists(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
testCases := []struct {
|
||||
description string
|
||||
containerContents []dirOrLink
|
||||
shouldExist []string
|
||||
}{
|
||||
{
|
||||
description: "link already exists with correct target",
|
||||
containerContents: []dirOrLink{{path: "/lib/libfoo.so", target: "libfoo.so.1"}},
|
||||
shouldExist: []string{},
|
||||
},
|
||||
{
|
||||
description: "link already exists with different target",
|
||||
containerContents: []dirOrLink{{path: "/lib/libfoo.so", target: "different-target"}, {path: "different-target"}},
|
||||
shouldExist: []string{"{{ .containerRoot }}/different-target"},
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t, makeFs(containerRoot, dirOrLink{path: "/lib/libfoo.so", target: "libfoo.so.1"}))
|
||||
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 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)
|
||||
}
|
||||
// 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)
|
||||
for _, p := range tc.shouldExist {
|
||||
require.DirExists(t, strings.ReplaceAll(p, "{{ .containerRoot }}", containerRoot))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateLinkOutOfBounds(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root/")
|
||||
hostRoot := filepath.Join(tmpDir, "/host-root")
|
||||
containerRoot := filepath.Join(tmpDir, "/container-root")
|
||||
|
||||
require.NoError(t, makeFs(hostRoot))
|
||||
require.NoError(t,
|
||||
makeFs(hostRoot,
|
||||
dirOrLink{path: "libfoo.so"},
|
||||
),
|
||||
)
|
||||
require.NoError(t,
|
||||
makeFs(containerRoot,
|
||||
dirOrLink{path: "/lib"},
|
||||
@@ -240,12 +254,13 @@ func TestCreateLinkOutOfBounds(t *testing.T) {
|
||||
|
||||
// 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)
|
||||
|
||||
target, err := symlinks.Resolve(filepath.Join(containerRoot, hostRoot, "libfoo.so"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "../libfoo.so.1", target)
|
||||
|
||||
require.DirExists(t, filepath.Join(hostRoot, "libfoo.so"))
|
||||
}
|
||||
|
||||
type dirOrLink struct {
|
||||
|
||||
76
cmd/nvidia-cdi-hook/cudacompat/container-root.go
Normal file
76
cmd/nvidia-cdi-hook/cudacompat/container-root.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package cudacompat
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/moby/sys/symlink"
|
||||
)
|
||||
|
||||
// A containerRoot represents the root filesystem of a container.
|
||||
type containerRoot string
|
||||
|
||||
// hasPath checks whether the specified path exists in the root.
|
||||
func (r containerRoot) hasPath(path string) bool {
|
||||
resolved, err := r.resolve(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if _, err := os.Stat(resolved); err != nil && os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// globFiles matches the specified pattern in the root.
|
||||
// The files that match must be regular files.
|
||||
func (r containerRoot) globFiles(pattern string) ([]string, error) {
|
||||
patternPath, err := r.resolve(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches, err := filepath.Glob(patternPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var files []string
|
||||
for _, match := range matches {
|
||||
info, err := os.Lstat(match)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Ignore symlinks.
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
continue
|
||||
}
|
||||
// Ignore directories.
|
||||
if info.IsDir() {
|
||||
continue
|
||||
}
|
||||
files = append(files, match)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// resolve returns the absolute path including root path.
|
||||
// Symlinks are resolved, but are guaranteed to resolve in the root.
|
||||
func (r containerRoot) resolve(path string) (string, error) {
|
||||
absolute := filepath.Clean(filepath.Join(string(r), path))
|
||||
return symlink.FollowSymlinkInScope(absolute, string(r))
|
||||
}
|
||||
221
cmd/nvidia-cdi-hook/cudacompat/cudacompat.go
Normal file
221
cmd/nvidia-cdi-hook/cudacompat/cudacompat.go
Normal file
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package cudacompat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
const (
|
||||
cudaCompatPath = "/usr/local/cuda/compat"
|
||||
// cudaCompatLdsoconfdFilenamePattern specifies the pattern for the filename
|
||||
// in ld.so.conf.d that includes a reference to the CUDA compat path.
|
||||
// The 00-compat prefix is chosen to ensure that these libraries have a
|
||||
// higher precedence than other libraries on the system.
|
||||
cudaCompatLdsoconfdFilenamePattern = "00-compat-*.conf"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
|
||||
type options struct {
|
||||
hostDriverVersion string
|
||||
containerSpec string
|
||||
}
|
||||
|
||||
// NewCommand constructs a cuda-compat command with the specified logger
|
||||
func NewCommand(logger logger.Interface) *cli.Command {
|
||||
c := command{
|
||||
logger: logger,
|
||||
}
|
||||
return c.build()
|
||||
}
|
||||
|
||||
// build the enable-cuda-compat command
|
||||
func (m command) build() *cli.Command {
|
||||
cfg := options{}
|
||||
|
||||
// Create the 'enable-cuda-compat' command
|
||||
c := cli.Command{
|
||||
Name: "enable-cuda-compat",
|
||||
Usage: "This hook ensures that the folder containing the CUDA compat libraries is added to the ldconfig search path if required.",
|
||||
Before: func(c *cli.Context) error {
|
||||
return m.validateFlags(c, &cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
return m.run(c, &cfg)
|
||||
},
|
||||
}
|
||||
|
||||
c.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "host-driver-version",
|
||||
Usage: "Specify the host driver version. If the CUDA compat libraries detected in the container do not have a higher MAJOR version, the hook is a no-op.",
|
||||
Destination: &cfg.hostDriverVersion,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "container-spec",
|
||||
Hidden: true,
|
||||
Category: "testing-only",
|
||||
Usage: "Specify the path to the OCI container spec. If empty or '-' the spec will be read from STDIN",
|
||||
Destination: &cfg.containerSpec,
|
||||
},
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (m command) validateFlags(_ *cli.Context, cfg *options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m command) run(_ *cli.Context, cfg *options) error {
|
||||
if cfg.hostDriverVersion == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
s, err := oci.LoadContainerState(cfg.containerSpec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load container state: %w", err)
|
||||
}
|
||||
|
||||
containerRootDir, err := s.GetContainerRoot()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determined container root: %w", err)
|
||||
}
|
||||
|
||||
containerForwardCompatDir, err := m.getContainerForwardCompatDir(containerRoot(containerRootDir), cfg.hostDriverVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get container forward compat directory: %w", err)
|
||||
}
|
||||
if containerForwardCompatDir == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.createLdsoconfdFile(containerRoot(containerRootDir), cudaCompatLdsoconfdFilenamePattern, containerForwardCompatDir)
|
||||
}
|
||||
|
||||
func (m command) getContainerForwardCompatDir(containerRoot containerRoot, hostDriverVersion string) (string, error) {
|
||||
if hostDriverVersion == "" {
|
||||
m.logger.Debugf("Host driver version not specified")
|
||||
return "", nil
|
||||
}
|
||||
if !containerRoot.hasPath(cudaCompatPath) {
|
||||
m.logger.Debugf("No CUDA forward compatibility libraries directory in container")
|
||||
return "", nil
|
||||
}
|
||||
if !containerRoot.hasPath("/etc/ld.so.cache") {
|
||||
m.logger.Debugf("The container does not have an LDCache")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
libs, err := containerRoot.globFiles(filepath.Join(cudaCompatPath, "libcuda.so.*.*"))
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to find CUDA compat library: %w", err)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(libs) == 0 {
|
||||
m.logger.Debugf("No CUDA forward compatibility libraries container")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(libs) != 1 {
|
||||
m.logger.Warningf("Unexpected number of CUDA compat libraries in container: %v", libs)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
compatDriverVersion := strings.TrimPrefix(filepath.Base(libs[0]), "libcuda.so.")
|
||||
compatMajor, err := extractMajorVersion(compatDriverVersion)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to extract major version from %q: %v", compatDriverVersion, err)
|
||||
}
|
||||
|
||||
driverMajor, err := extractMajorVersion(hostDriverVersion)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to extract major version from %q: %v", hostDriverVersion, err)
|
||||
}
|
||||
|
||||
if driverMajor >= compatMajor {
|
||||
m.logger.Debugf("Compat major version is not greater than the host driver major version (%v >= %v)", hostDriverVersion, compatDriverVersion)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
resolvedCompatDir := strings.TrimPrefix(filepath.Dir(libs[0]), string(containerRoot))
|
||||
return resolvedCompatDir, nil
|
||||
}
|
||||
|
||||
// createLdsoconfdFile creates a file at /etc/ld.so.conf.d/ in the specified root.
|
||||
// The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
|
||||
// contains the specified directories on each line.
|
||||
func (m command) createLdsoconfdFile(in containerRoot, pattern string, dirs ...string) error {
|
||||
if len(dirs) == 0 {
|
||||
m.logger.Debugf("No directories to add to /etc/ld.so.conf")
|
||||
return nil
|
||||
}
|
||||
|
||||
ldsoconfdDir, err := in.resolve("/etc/ld.so.conf.d")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
|
||||
}
|
||||
|
||||
configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config file: %w", err)
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
m.logger.Debugf("Adding directories %v to %v", dirs, configFile.Name())
|
||||
|
||||
added := make(map[string]bool)
|
||||
for _, dir := range dirs {
|
||||
if added[dir] {
|
||||
continue
|
||||
}
|
||||
_, err = configFile.WriteString(fmt.Sprintf("%s\n", dir))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update config file: %w", err)
|
||||
}
|
||||
added[dir] = true
|
||||
}
|
||||
|
||||
// The created file needs to be world readable for the cases where the container is run as a non-root user.
|
||||
if err := configFile.Chmod(0644); err != nil {
|
||||
return fmt.Errorf("failed to chmod config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractMajorVersion parses a version string and returns the major version as an int.
|
||||
func extractMajorVersion(version string) (int, error) {
|
||||
majorString := strings.SplitN(version, ".", 2)[0]
|
||||
return strconv.Atoi(majorString)
|
||||
}
|
||||
182
cmd/nvidia-cdi-hook/cudacompat/cudacompat_test.go
Normal file
182
cmd/nvidia-cdi-hook/cudacompat/cudacompat_test.go
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
*/
|
||||
|
||||
package cudacompat
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCompatLibs(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
contents map[string]string
|
||||
hostDriverVersion string
|
||||
expectedContainerForwardCompatDir string
|
||||
}{
|
||||
{
|
||||
description: "empty root",
|
||||
hostDriverVersion: "222.55.66",
|
||||
},
|
||||
{
|
||||
description: "compat lib is newer; no ldcache",
|
||||
contents: map[string]string{
|
||||
"/usr/local/cuda/compat/libcuda.so.333.88.99": "",
|
||||
},
|
||||
hostDriverVersion: "222.55.66",
|
||||
},
|
||||
{
|
||||
description: "compat lib is newer; ldcache",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/usr/local/cuda/compat/libcuda.so.333.88.99": "",
|
||||
},
|
||||
hostDriverVersion: "222.55.66",
|
||||
expectedContainerForwardCompatDir: "/usr/local/cuda/compat",
|
||||
},
|
||||
{
|
||||
description: "compat lib is older; ldcache",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/usr/local/cuda/compat/libcuda.so.111.88.99": "",
|
||||
},
|
||||
hostDriverVersion: "222.55.66",
|
||||
expectedContainerForwardCompatDir: "",
|
||||
},
|
||||
{
|
||||
description: "compat lib has same major version; ldcache",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/usr/local/cuda/compat/libcuda.so.222.88.99": "",
|
||||
},
|
||||
hostDriverVersion: "222.55.66",
|
||||
expectedContainerForwardCompatDir: "",
|
||||
},
|
||||
{
|
||||
description: "numeric comparison is used; ldcache",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/usr/local/cuda/compat/libcuda.so.222.88.99": "",
|
||||
},
|
||||
hostDriverVersion: "99.55.66",
|
||||
expectedContainerForwardCompatDir: "/usr/local/cuda/compat",
|
||||
},
|
||||
{
|
||||
description: "driver version empty; ldcache",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/usr/local/cuda/compat/libcuda.so.222.88.99": "",
|
||||
},
|
||||
hostDriverVersion: "",
|
||||
},
|
||||
{
|
||||
description: "symlinks are followed",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/etc/alternatives/cuda/compat/libcuda.so.333.88.99": "",
|
||||
"/usr/local/cuda": "symlink=/etc/alternatives/cuda",
|
||||
},
|
||||
hostDriverVersion: "222.55.66",
|
||||
expectedContainerForwardCompatDir: "/etc/alternatives/cuda/compat",
|
||||
},
|
||||
{
|
||||
description: "symlinks stay in container",
|
||||
contents: map[string]string{
|
||||
"/etc/ld.so.cache": "",
|
||||
"/compat/libcuda.so.333.88.99": "",
|
||||
"/usr/local/cuda": "symlink=../../../../../../",
|
||||
},
|
||||
hostDriverVersion: "222.55.66",
|
||||
expectedContainerForwardCompatDir: "/compat",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
containerRootDir := t.TempDir()
|
||||
for name, contents := range tc.contents {
|
||||
target := filepath.Join(containerRootDir, name)
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(target), 0755))
|
||||
|
||||
if strings.HasPrefix(contents, "symlink=") {
|
||||
require.NoError(t, os.Symlink(strings.TrimPrefix(contents, "symlink="), target))
|
||||
continue
|
||||
}
|
||||
|
||||
require.NoError(t, os.WriteFile(target, []byte(contents), 0600))
|
||||
}
|
||||
|
||||
c := command{
|
||||
logger: logger,
|
||||
}
|
||||
containerForwardCompatDir, err := c.getContainerForwardCompatDir(containerRoot(containerRootDir), tc.hostDriverVersion)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, tc.expectedContainerForwardCompatDir, containerForwardCompatDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLdconfig(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
description string
|
||||
folders []string
|
||||
expectedContents string
|
||||
}{
|
||||
{
|
||||
description: "no folders; have no contents",
|
||||
},
|
||||
{
|
||||
description: "single folder is added",
|
||||
folders: []string{"/usr/local/cuda/compat"},
|
||||
expectedContents: "/usr/local/cuda/compat\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
containerRootDir := t.TempDir()
|
||||
c := command{
|
||||
logger: logger,
|
||||
}
|
||||
err := c.createLdsoconfdFile(containerRoot(containerRootDir), cudaCompatLdsoconfdFilenamePattern, tc.folders...)
|
||||
require.NoError(t, err)
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(containerRootDir, "/etc/ld.so.conf.d/00-compat-*.conf"))
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.expectedContents == "" {
|
||||
require.Empty(t, matches)
|
||||
return
|
||||
}
|
||||
|
||||
require.Len(t, matches, 1)
|
||||
contents, err := os.ReadFile(matches[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, tc.expectedContents, string(contents))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
46
cmd/nvidia-cdi-hook/update-ldcache/container-root.go
Normal file
46
cmd/nvidia-cdi-hook/update-ldcache/container-root.go
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package ldcache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/moby/sys/symlink"
|
||||
)
|
||||
|
||||
// A containerRoot represents the root filesystem of a container.
|
||||
type containerRoot string
|
||||
|
||||
// hasPath checks whether the specified path exists in the root.
|
||||
func (r containerRoot) hasPath(path string) bool {
|
||||
resolved, err := r.resolve(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if _, err := os.Stat(resolved); err != nil && os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// resolve returns the absolute path including root path.
|
||||
// Symlinks are resolved, but are guaranteed to resolve in the root.
|
||||
func (r containerRoot) resolve(path string) (string, error) {
|
||||
absolute := filepath.Clean(filepath.Join(string(r), path))
|
||||
return symlink.FollowSymlinkInScope(absolute, string(r))
|
||||
}
|
||||
57
cmd/nvidia-cdi-hook/update-ldcache/safe-exec_linux.go
Normal file
57
cmd/nvidia-cdi-hook/update-ldcache/safe-exec_linux.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package ldcache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/dmz"
|
||||
)
|
||||
|
||||
// SafeExec attempts to clone the specified binary (as an memfd, for example) before executing it.
|
||||
func (m command) SafeExec(path string, args []string, envv []string) error {
|
||||
safeExe, err := cloneBinary(path)
|
||||
if err != nil {
|
||||
m.logger.Warningf("Failed to clone binary %q: %v; falling back to Exec", path, err)
|
||||
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
|
||||
return syscall.Exec(path, args, envv)
|
||||
}
|
||||
defer safeExe.Close()
|
||||
|
||||
exePath := "/proc/self/fd/" + strconv.Itoa(int(safeExe.Fd()))
|
||||
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
|
||||
return syscall.Exec(exePath, args, envv)
|
||||
}
|
||||
|
||||
func cloneBinary(path string) (*os.File, error) {
|
||||
exe, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening current binary: %w", err)
|
||||
}
|
||||
defer exe.Close()
|
||||
|
||||
stat, err := exe.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("checking %v size: %w", path, err)
|
||||
}
|
||||
size := stat.Size()
|
||||
|
||||
return dmz.CloneBinary(exe, size, path, os.TempDir())
|
||||
}
|
||||
29
cmd/nvidia-cdi-hook/update-ldcache/safe-exec_other.go
Normal file
29
cmd/nvidia-cdi-hook/update-ldcache/safe-exec_other.go
Normal file
@@ -0,0 +1,29 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package ldcache
|
||||
|
||||
import "syscall"
|
||||
|
||||
// SafeExec is not implemented on non-linux systems and forwards directly to the
|
||||
// Exec syscall.
|
||||
func (m *command) SafeExec(path string, args []string, envv []string) error {
|
||||
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
|
||||
return syscall.Exec(path, args, envv)
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
@@ -31,6 +30,15 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
const (
|
||||
// ldsoconfdFilenamePattern specifies the pattern for the filename
|
||||
// in ld.so.conf.d that includes references to the specified directories.
|
||||
// The 00-nvcr prefix is chosen to ensure that these libraries have a
|
||||
// higher precedence than other libraries on the system, but lower than
|
||||
// the 00-cuda-compat that is included in some containers.
|
||||
ldsoconfdFilenamePattern = "00-nvcr-*.conf"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
@@ -100,18 +108,20 @@ func (m command) run(c *cli.Context, cfg *options) error {
|
||||
return fmt.Errorf("failed to load container state: %v", err)
|
||||
}
|
||||
|
||||
containerRoot, err := s.GetContainerRoot()
|
||||
containerRootDir, err := s.GetContainerRoot()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to determined container root: %v", err)
|
||||
}
|
||||
|
||||
ldconfigPath := m.resolveLDConfigPath(cfg.ldconfigPath)
|
||||
args := []string{filepath.Base(ldconfigPath)}
|
||||
if containerRoot != "" {
|
||||
args = append(args, "-r", containerRoot)
|
||||
if containerRootDir != "" {
|
||||
args = append(args, "-r", containerRootDir)
|
||||
}
|
||||
|
||||
if root(containerRoot).hasPath("/etc/ld.so.cache") {
|
||||
containerRoot := containerRoot(containerRootDir)
|
||||
|
||||
if containerRoot.hasPath("/etc/ld.so.cache") {
|
||||
args = append(args, "-C", "/etc/ld.so.cache")
|
||||
} else {
|
||||
m.logger.Debugf("No ld.so.cache found, skipping update")
|
||||
@@ -119,8 +129,8 @@ func (m command) run(c *cli.Context, cfg *options) error {
|
||||
}
|
||||
|
||||
folders := cfg.folders.Value()
|
||||
if root(containerRoot).hasPath("/etc/ld.so.conf.d") {
|
||||
err := m.createConfig(containerRoot, folders)
|
||||
if containerRoot.hasPath("/etc/ld.so.conf.d") {
|
||||
err := m.createLdsoconfdFile(containerRoot, ldsoconfdFilenamePattern, folders...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update ld.so.conf.d: %v", err)
|
||||
}
|
||||
@@ -132,18 +142,7 @@ func (m command) run(c *cli.Context, cfg *options) error {
|
||||
// be configured to use a different config file by default.
|
||||
args = append(args, "-f", "/etc/ld.so.conf")
|
||||
|
||||
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
|
||||
return syscall.Exec(ldconfigPath, args, nil)
|
||||
}
|
||||
|
||||
type root string
|
||||
|
||||
func (r root) hasPath(path string) bool {
|
||||
_, err := os.Stat(filepath.Join(string(r), path))
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return m.SafeExec(ldconfigPath, args, nil)
|
||||
}
|
||||
|
||||
// resolveLDConfigPath determines the LDConfig path to use for the system.
|
||||
@@ -153,44 +152,46 @@ func (m command) resolveLDConfigPath(path string) string {
|
||||
return strings.TrimPrefix(config.NormalizeLDConfigPath("@"+path), "@")
|
||||
}
|
||||
|
||||
// createConfig creates (or updates) /etc/ld.so.conf.d/00-nvcr-<RANDOM_STRING>.conf in the container
|
||||
// to include the required paths.
|
||||
// Note that the 00-nvcr prefix is chosen to ensure that these libraries have
|
||||
// a higher precedence than other libraries on the system but are applied AFTER
|
||||
// 00-cuda-compat.conf.
|
||||
func (m command) createConfig(root string, folders []string) error {
|
||||
if len(folders) == 0 {
|
||||
m.logger.Debugf("No folders to add to /etc/ld.so.conf")
|
||||
// createLdsoconfdFile creates a file at /etc/ld.so.conf.d/ in the specified root.
|
||||
// The file is created at /etc/ld.so.conf.d/{{ .pattern }} using `CreateTemp` and
|
||||
// contains the specified directories on each line.
|
||||
func (m command) createLdsoconfdFile(in containerRoot, pattern string, dirs ...string) error {
|
||||
if len(dirs) == 0 {
|
||||
m.logger.Debugf("No directories to add to /etc/ld.so.conf")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(root, "/etc/ld.so.conf.d"), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create ld.so.conf.d: %v", err)
|
||||
ldsoconfdDir, err := in.resolve("/etc/ld.so.conf.d")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(ldsoconfdDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create ld.so.conf.d: %w", err)
|
||||
}
|
||||
|
||||
configFile, err := os.CreateTemp(filepath.Join(root, "/etc/ld.so.conf.d"), "00-nvcr-*.conf")
|
||||
configFile, err := os.CreateTemp(ldsoconfdDir, pattern)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config file: %v", err)
|
||||
return fmt.Errorf("failed to create config file: %w", err)
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
m.logger.Debugf("Adding folders %v to %v", folders, configFile.Name())
|
||||
m.logger.Debugf("Adding directories %v to %v", dirs, configFile.Name())
|
||||
|
||||
configured := make(map[string]bool)
|
||||
for _, folder := range folders {
|
||||
if configured[folder] {
|
||||
added := make(map[string]bool)
|
||||
for _, dir := range dirs {
|
||||
if added[dir] {
|
||||
continue
|
||||
}
|
||||
_, err = configFile.WriteString(fmt.Sprintf("%s\n", folder))
|
||||
_, err = configFile.WriteString(fmt.Sprintf("%s\n", dir))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update ld.so.conf.d: %v", err)
|
||||
return fmt.Errorf("failed to update config file: %w", err)
|
||||
}
|
||||
configured[folder] = true
|
||||
added[dir] = true
|
||||
}
|
||||
|
||||
// The created file needs to be world readable for the cases where the container is run as a non-root user.
|
||||
if err := os.Chmod(configFile.Name(), 0644); err != nil {
|
||||
return fmt.Errorf("failed to chmod config file: %v", err)
|
||||
if err := configFile.Chmod(0644); err != nil {
|
||||
return fmt.Errorf("failed to chmod config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -157,7 +157,7 @@ func getDevicesFromEnvvar(containerImage image.CUDA, swarmResourceEnvvars []stri
|
||||
return containerImage.VisibleDevicesFromEnvVar()
|
||||
}
|
||||
|
||||
func getDevices(hookConfig *HookConfig, image image.CUDA, privileged bool) []string {
|
||||
func (hookConfig *hookConfig) getDevices(image image.CUDA, privileged bool) []string {
|
||||
// If enabled, try and get the device list from volume mounts first
|
||||
if hookConfig.AcceptDeviceListAsVolumeMounts {
|
||||
devices := image.VisibleDevicesFromMounts()
|
||||
@@ -197,7 +197,11 @@ func getMigDevices(image image.CUDA, envvar string) *string {
|
||||
return &devices
|
||||
}
|
||||
|
||||
func getImexChannels(hookConfig *HookConfig, image image.CUDA, privileged bool) []string {
|
||||
func (hookConfig *hookConfig) getImexChannels(image image.CUDA, privileged bool) []string {
|
||||
if hookConfig.Features.IgnoreImexChannelRequests.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If enabled, try and get the device list from volume mounts first
|
||||
if hookConfig.AcceptDeviceListAsVolumeMounts {
|
||||
devices := image.ImexChannelsFromMounts()
|
||||
@@ -217,10 +221,10 @@ func getImexChannels(hookConfig *HookConfig, image image.CUDA, privileged bool)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HookConfig) getDriverCapabilities(cudaImage image.CUDA, legacyImage bool) image.DriverCapabilities {
|
||||
func (hookConfig *hookConfig) getDriverCapabilities(cudaImage image.CUDA, legacyImage bool) image.DriverCapabilities {
|
||||
// We use the default driver capabilities by default. This is filtered to only include the
|
||||
// supported capabilities
|
||||
supportedDriverCapabilities := image.NewDriverCapabilities(c.SupportedDriverCapabilities)
|
||||
supportedDriverCapabilities := image.NewDriverCapabilities(hookConfig.SupportedDriverCapabilities)
|
||||
|
||||
capabilities := supportedDriverCapabilities.Intersection(image.DefaultDriverCapabilities)
|
||||
|
||||
@@ -244,10 +248,10 @@ func (c *HookConfig) getDriverCapabilities(cudaImage image.CUDA, legacyImage boo
|
||||
return capabilities
|
||||
}
|
||||
|
||||
func getNvidiaConfig(hookConfig *HookConfig, image image.CUDA, privileged bool) *nvidiaConfig {
|
||||
func (hookConfig *hookConfig) getNvidiaConfig(image image.CUDA, privileged bool) *nvidiaConfig {
|
||||
legacyImage := image.IsLegacy()
|
||||
|
||||
devices := getDevices(hookConfig, image, privileged)
|
||||
devices := hookConfig.getDevices(image, privileged)
|
||||
if len(devices) == 0 {
|
||||
// empty devices means this is not a GPU container.
|
||||
return nil
|
||||
@@ -269,7 +273,7 @@ func getNvidiaConfig(hookConfig *HookConfig, image image.CUDA, privileged bool)
|
||||
log.Panicln("cannot set MIG_MONITOR_DEVICES in non privileged container")
|
||||
}
|
||||
|
||||
imexChannels := getImexChannels(hookConfig, image, privileged)
|
||||
imexChannels := hookConfig.getImexChannels(image, privileged)
|
||||
|
||||
driverCapabilities := hookConfig.getDriverCapabilities(image, legacyImage).String()
|
||||
|
||||
@@ -288,7 +292,7 @@ func getNvidiaConfig(hookConfig *HookConfig, image image.CUDA, privileged bool)
|
||||
}
|
||||
}
|
||||
|
||||
func getContainerConfig(hook HookConfig) (config containerConfig) {
|
||||
func (hookConfig *hookConfig) getContainerConfig() (config containerConfig) {
|
||||
var h HookState
|
||||
d := json.NewDecoder(os.Stdin)
|
||||
if err := d.Decode(&h); err != nil {
|
||||
@@ -305,7 +309,7 @@ func getContainerConfig(hook HookConfig) (config containerConfig) {
|
||||
image, err := image.New(
|
||||
image.WithEnv(s.Process.Env),
|
||||
image.WithMounts(s.Mounts),
|
||||
image.WithDisableRequire(hook.DisableRequire),
|
||||
image.WithDisableRequire(hookConfig.DisableRequire),
|
||||
)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
@@ -316,6 +320,6 @@ func getContainerConfig(hook HookConfig) (config containerConfig) {
|
||||
Pid: h.Pid,
|
||||
Rootfs: s.Root.Path,
|
||||
Image: image,
|
||||
Nvidia: getNvidiaConfig(&hook, image, privileged),
|
||||
Nvidia: hookConfig.getNvidiaConfig(image, privileged),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,7 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
description string
|
||||
env map[string]string
|
||||
privileged bool
|
||||
hookConfig *HookConfig
|
||||
hookConfig *hookConfig
|
||||
expectedConfig *nvidiaConfig
|
||||
expectedPanic bool
|
||||
}{
|
||||
@@ -394,8 +395,10 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
image.EnvVarNvidiaDriverCapabilities: "all",
|
||||
},
|
||||
privileged: true,
|
||||
hookConfig: &HookConfig{
|
||||
SupportedDriverCapabilities: "video,display",
|
||||
hookConfig: &hookConfig{
|
||||
Config: &config.Config{
|
||||
SupportedDriverCapabilities: "video,display",
|
||||
},
|
||||
},
|
||||
expectedConfig: &nvidiaConfig{
|
||||
Devices: []string{"all"},
|
||||
@@ -409,8 +412,10 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
image.EnvVarNvidiaDriverCapabilities: "video,display",
|
||||
},
|
||||
privileged: true,
|
||||
hookConfig: &HookConfig{
|
||||
SupportedDriverCapabilities: "video,display,compute,utility",
|
||||
hookConfig: &hookConfig{
|
||||
Config: &config.Config{
|
||||
SupportedDriverCapabilities: "video,display,compute,utility",
|
||||
},
|
||||
},
|
||||
expectedConfig: &nvidiaConfig{
|
||||
Devices: []string{"all"},
|
||||
@@ -423,8 +428,10 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
image.EnvVarNvidiaVisibleDevices: "all",
|
||||
},
|
||||
privileged: true,
|
||||
hookConfig: &HookConfig{
|
||||
SupportedDriverCapabilities: "video,display,utility,compute",
|
||||
hookConfig: &hookConfig{
|
||||
Config: &config.Config{
|
||||
SupportedDriverCapabilities: "video,display,utility,compute",
|
||||
},
|
||||
},
|
||||
expectedConfig: &nvidiaConfig{
|
||||
Devices: []string{"all"},
|
||||
@@ -438,9 +445,11 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
"DOCKER_SWARM_RESOURCE": "GPU1,GPU2",
|
||||
},
|
||||
privileged: true,
|
||||
hookConfig: &HookConfig{
|
||||
SwarmResource: "DOCKER_SWARM_RESOURCE",
|
||||
SupportedDriverCapabilities: "video,display,utility,compute",
|
||||
hookConfig: &hookConfig{
|
||||
Config: &config.Config{
|
||||
SwarmResource: "DOCKER_SWARM_RESOURCE",
|
||||
SupportedDriverCapabilities: "video,display,utility,compute",
|
||||
},
|
||||
},
|
||||
expectedConfig: &nvidiaConfig{
|
||||
Devices: []string{"GPU1", "GPU2"},
|
||||
@@ -454,9 +463,11 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
"DOCKER_SWARM_RESOURCE": "GPU1,GPU2",
|
||||
},
|
||||
privileged: true,
|
||||
hookConfig: &HookConfig{
|
||||
SwarmResource: "NOT_DOCKER_SWARM_RESOURCE,DOCKER_SWARM_RESOURCE",
|
||||
SupportedDriverCapabilities: "video,display,utility,compute",
|
||||
hookConfig: &hookConfig{
|
||||
Config: &config.Config{
|
||||
SwarmResource: "NOT_DOCKER_SWARM_RESOURCE,DOCKER_SWARM_RESOURCE",
|
||||
SupportedDriverCapabilities: "video,display,utility,compute",
|
||||
},
|
||||
},
|
||||
expectedConfig: &nvidiaConfig{
|
||||
Devices: []string{"GPU1", "GPU2"},
|
||||
@@ -470,14 +481,14 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
image.WithEnvMap(tc.env),
|
||||
)
|
||||
// Wrap the call to getNvidiaConfig() in a closure.
|
||||
var config *nvidiaConfig
|
||||
var cfg *nvidiaConfig
|
||||
getConfig := func() {
|
||||
hookConfig := tc.hookConfig
|
||||
if hookConfig == nil {
|
||||
defaultConfig, _ := getDefaultHookConfig()
|
||||
hookConfig = &defaultConfig
|
||||
hookCfg := tc.hookConfig
|
||||
if hookCfg == nil {
|
||||
defaultConfig, _ := config.GetDefault()
|
||||
hookCfg = &hookConfig{defaultConfig}
|
||||
}
|
||||
config = getNvidiaConfig(hookConfig, image, tc.privileged)
|
||||
cfg = hookCfg.getNvidiaConfig(image, tc.privileged)
|
||||
}
|
||||
|
||||
// For any tests that are expected to panic, make sure they do.
|
||||
@@ -491,18 +502,18 @@ func TestGetNvidiaConfig(t *testing.T) {
|
||||
|
||||
// And start comparing the test results to the expected results.
|
||||
if tc.expectedConfig == nil {
|
||||
require.Nil(t, config, tc.description)
|
||||
require.Nil(t, cfg, tc.description)
|
||||
return
|
||||
}
|
||||
|
||||
require.NotNil(t, config, tc.description)
|
||||
require.NotNil(t, cfg, tc.description)
|
||||
|
||||
require.Equal(t, tc.expectedConfig.Devices, config.Devices)
|
||||
require.Equal(t, tc.expectedConfig.MigConfigDevices, config.MigConfigDevices)
|
||||
require.Equal(t, tc.expectedConfig.MigMonitorDevices, config.MigMonitorDevices)
|
||||
require.Equal(t, tc.expectedConfig.DriverCapabilities, config.DriverCapabilities)
|
||||
require.Equal(t, tc.expectedConfig.Devices, cfg.Devices)
|
||||
require.Equal(t, tc.expectedConfig.MigConfigDevices, cfg.MigConfigDevices)
|
||||
require.Equal(t, tc.expectedConfig.MigMonitorDevices, cfg.MigMonitorDevices)
|
||||
require.Equal(t, tc.expectedConfig.DriverCapabilities, cfg.DriverCapabilities)
|
||||
|
||||
require.ElementsMatch(t, tc.expectedConfig.Requirements, config.Requirements)
|
||||
require.ElementsMatch(t, tc.expectedConfig.Requirements, cfg.Requirements)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -612,10 +623,11 @@ func TestDeviceListSourcePriority(t *testing.T) {
|
||||
),
|
||||
image.WithMounts(tc.mountDevices),
|
||||
)
|
||||
hookConfig, _ := getDefaultHookConfig()
|
||||
hookConfig.AcceptEnvvarUnprivileged = tc.acceptUnprivileged
|
||||
hookConfig.AcceptDeviceListAsVolumeMounts = tc.acceptMounts
|
||||
devices = getDevices(&hookConfig, image, tc.privileged)
|
||||
defaultConfig, _ := config.GetDefault()
|
||||
cfg := &hookConfig{defaultConfig}
|
||||
cfg.AcceptEnvvarUnprivileged = tc.acceptUnprivileged
|
||||
cfg.AcceptDeviceListAsVolumeMounts = tc.acceptMounts
|
||||
devices = cfg.getDevices(image, tc.privileged)
|
||||
}
|
||||
|
||||
// For all other tests, just grab the devices and check the results
|
||||
@@ -940,8 +952,10 @@ func TestGetDriverCapabilities(t *testing.T) {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
var capabilities string
|
||||
|
||||
c := HookConfig{
|
||||
SupportedDriverCapabilities: tc.supportedCapabilities,
|
||||
c := hookConfig{
|
||||
Config: &config.Config{
|
||||
SupportedDriverCapabilities: tc.supportedCapabilities,
|
||||
},
|
||||
}
|
||||
|
||||
image, _ := image.New(
|
||||
|
||||
@@ -17,16 +17,10 @@ const (
|
||||
driverPath = "/run/nvidia/driver"
|
||||
)
|
||||
|
||||
// HookConfig : options for the nvidia-container-runtime-hook.
|
||||
type HookConfig config.Config
|
||||
|
||||
func getDefaultHookConfig() (HookConfig, error) {
|
||||
defaultCfg, err := config.GetDefault()
|
||||
if err != nil {
|
||||
return HookConfig{}, err
|
||||
}
|
||||
|
||||
return *(*HookConfig)(defaultCfg), nil
|
||||
// hookConfig wraps the toolkit config.
|
||||
// This allows for functions to be defined on the local type.
|
||||
type hookConfig struct {
|
||||
*config.Config
|
||||
}
|
||||
|
||||
// loadConfig loads the required paths for the hook config.
|
||||
@@ -56,12 +50,12 @@ func loadConfig() (*config.Config, error) {
|
||||
return config.GetDefault()
|
||||
}
|
||||
|
||||
func getHookConfig() (*HookConfig, error) {
|
||||
func getHookConfig() (*hookConfig, error) {
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load config: %v", err)
|
||||
}
|
||||
config := (*HookConfig)(cfg)
|
||||
config := &hookConfig{cfg}
|
||||
|
||||
allSupportedDriverCapabilities := image.SupportedDriverCapabilities
|
||||
if config.SupportedDriverCapabilities == "all" {
|
||||
@@ -79,7 +73,7 @@ func getHookConfig() (*HookConfig, error) {
|
||||
|
||||
// getConfigOption returns the toml config option associated with the
|
||||
// specified struct field.
|
||||
func (c HookConfig) getConfigOption(fieldName string) string {
|
||||
func (c hookConfig) getConfigOption(fieldName string) string {
|
||||
t := reflect.TypeOf(c)
|
||||
f, ok := t.FieldByName(fieldName)
|
||||
if !ok {
|
||||
@@ -93,7 +87,7 @@ func (c HookConfig) getConfigOption(fieldName string) string {
|
||||
}
|
||||
|
||||
// getSwarmResourceEnvvars returns the swarm resource envvars for the config.
|
||||
func (c *HookConfig) getSwarmResourceEnvvars() []string {
|
||||
func (c *hookConfig) getSwarmResourceEnvvars() []string {
|
||||
if c.SwarmResource == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
)
|
||||
|
||||
@@ -89,10 +90,10 @@ func TestGetHookConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var config HookConfig
|
||||
var cfg hookConfig
|
||||
getHookConfig := func() {
|
||||
c, _ := getHookConfig()
|
||||
config = *c
|
||||
cfg = *c
|
||||
}
|
||||
|
||||
if tc.expectedPanic {
|
||||
@@ -102,7 +103,7 @@ func TestGetHookConfig(t *testing.T) {
|
||||
|
||||
getHookConfig()
|
||||
|
||||
require.EqualValues(t, tc.expectedDriverCapabilities, config.SupportedDriverCapabilities)
|
||||
require.EqualValues(t, tc.expectedDriverCapabilities, cfg.SupportedDriverCapabilities)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -144,8 +145,10 @@ func TestGetSwarmResourceEnvvars(t *testing.T) {
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
c := &HookConfig{
|
||||
SwarmResource: tc.value,
|
||||
c := &hookConfig{
|
||||
Config: &config.Config{
|
||||
SwarmResource: tc.value,
|
||||
},
|
||||
}
|
||||
|
||||
envvars := c.getSwarmResourceEnvvars()
|
||||
|
||||
@@ -75,7 +75,7 @@ func doPrestart() {
|
||||
}
|
||||
cli := hook.NVIDIAContainerCLIConfig
|
||||
|
||||
container := getContainerConfig(*hook)
|
||||
container := hook.getContainerConfig()
|
||||
nvidia := container.Nvidia
|
||||
if nvidia == nil {
|
||||
// Not a GPU container, nothing to do.
|
||||
@@ -114,6 +114,9 @@ func doPrestart() {
|
||||
}
|
||||
args = append(args, "configure")
|
||||
|
||||
if !hook.Features.AllowCUDACompatLibsFromContainer.IsEnabled() {
|
||||
args = append(args, "--no-cntlibs")
|
||||
}
|
||||
if ldconfigPath := cli.NormalizeLDConfigPath(); ldconfigPath != "" {
|
||||
args = append(args, fmt.Sprintf("--ldconfig=%s", ldconfigPath))
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
const (
|
||||
nvidiaRuntime = "nvidia-container-runtime"
|
||||
nvidiaHook = "nvidia-container-runtime-hook"
|
||||
bundlePathSuffix = "test/output/bundle/"
|
||||
bundlePathSuffix = "tests/output/bundle/"
|
||||
specFile = "config.json"
|
||||
unmodifiedSpecFileSuffix = "test/input/test_spec.json"
|
||||
unmodifiedSpecFileSuffix = "tests/input/test_spec.json"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,8 +46,8 @@ func TestMain(m *testing.M) {
|
||||
if err != nil {
|
||||
log.Fatalf("error in test setup: could not get module root: %v", err)
|
||||
}
|
||||
testBinPath := filepath.Join(moduleRoot, "test", "bin")
|
||||
testInputPath := filepath.Join(moduleRoot, "test", "input")
|
||||
testBinPath := filepath.Join(moduleRoot, "tests", "bin")
|
||||
testInputPath := filepath.Join(moduleRoot, "tests", "input")
|
||||
|
||||
// Set the environment variables for the test
|
||||
os.Setenv("PATH", test.PrependToPath(testBinPath, moduleRoot))
|
||||
|
||||
@@ -24,8 +24,8 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/operator"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/operator"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,8 +36,10 @@ const (
|
||||
|
||||
// Options defines the shared options for the CLIs to configure containers runtimes.
|
||||
type Options struct {
|
||||
Config string
|
||||
Socket string
|
||||
Config string
|
||||
Socket string
|
||||
// EnabledCDI indicates whether CDI should be enabled.
|
||||
EnableCDI bool
|
||||
RuntimeName string
|
||||
RuntimeDir string
|
||||
SetAsDefault bool
|
||||
@@ -111,6 +113,10 @@ func (o Options) UpdateConfig(cfg engine.Interface) error {
|
||||
}
|
||||
}
|
||||
|
||||
if o.EnableCDI {
|
||||
cfg.EnableCDI()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/containerd"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
)
|
||||
|
||||
func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
|
||||
@@ -88,37 +88,36 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
|
||||
SetAsDefault: tc.setAsDefault,
|
||||
}
|
||||
|
||||
cfg, err := toml.Empty.Load()
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: !tc.legacyConfig,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithUseLegacyConfig(tc.legacyConfig),
|
||||
containerd.WithConfigVersion(1),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
defaultRuntimeName := v1.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"})
|
||||
require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName, "%d: %v", i, tc)
|
||||
cfg := v1.(*containerd.ConfigV1)
|
||||
defaultRuntimeName := cfg.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"})
|
||||
require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName)
|
||||
|
||||
defaultRuntime := v1.GetPath([]string{"plugins", "cri", "containerd", "default_runtime"})
|
||||
defaultRuntime := cfg.GetPath([]string{"plugins", "cri", "containerd", "default_runtime"})
|
||||
if tc.expectedDefaultRuntimeBinary == nil {
|
||||
require.Nil(t, defaultRuntime, "%d: %v", i, tc)
|
||||
require.Nil(t, defaultRuntime)
|
||||
} else {
|
||||
require.NotNil(t, defaultRuntime)
|
||||
|
||||
expected, err := defaultRuntimeTomlConfigV1(tc.expectedDefaultRuntimeBinary.(string))
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
configContents, _ := toml.Marshal(defaultRuntime.(*toml.Tree))
|
||||
expectedContents, _ := toml.Marshal(expected)
|
||||
|
||||
require.Equal(t, string(expectedContents), string(configContents), "%d: %v: %v", i, tc)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -234,24 +233,22 @@ func TestUpdateV1Config(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.Empty.Load()
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithConfigVersion(1),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: true,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v1.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -393,29 +390,73 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.TreeFromMap(runcConfigMapV1("/runc-binary"))
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(runcConfigMapV1("/runc-binary"))),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithConfigVersion(1),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: true,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v1.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateV1EnableCDI(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
const runtimeDir = "/test/runtime/dir"
|
||||
|
||||
testCases := []struct {
|
||||
enableCDI bool
|
||||
expectedEnableCDIValue interface{}
|
||||
}{
|
||||
{},
|
||||
{
|
||||
enableCDI: false,
|
||||
expectedEnableCDIValue: nil,
|
||||
},
|
||||
{
|
||||
enableCDI: true,
|
||||
expectedEnableCDIValue: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%v", tc.enableCDI), func(t *testing.T) {
|
||||
o := &container.Options{
|
||||
EnableCDI: tc.enableCDI,
|
||||
RuntimeName: "nvidia",
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.Empty.Load()
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v1)
|
||||
require.NoError(t, err)
|
||||
|
||||
enableCDIValue := v1.GetPath([]string{"plugins", "cri", "containerd", "enable_cdi"})
|
||||
require.EqualValues(t, tc.expectedEnableCDIValue, enableCDIValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevertV1Config(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
config map[string]interface {
|
||||
}
|
||||
@@ -469,25 +510,22 @@ func TestRevertV1Config(t *testing.T) {
|
||||
RuntimeName: "nvidia",
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(tc.config)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expected)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
v1 := &containerd.ConfigV1{
|
||||
Tree: cfg,
|
||||
UseDefaultRuntimeName: true,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
v1, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(tc.config)),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = o.RevertConfig(v1)
|
||||
require.NoError(t, err, "%d: %v", i, tc)
|
||||
require.NoError(t, err)
|
||||
|
||||
configContents, _ := toml.Marshal(cfg)
|
||||
expectedContents, _ := toml.Marshal(expected)
|
||||
|
||||
require.Equal(t, string(expectedContents), string(configContents), "%d: %v", i, tc)
|
||||
require.Equal(t, expected.String(), v1.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/containerd"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -72,18 +72,19 @@ func TestUpdateV2ConfigDefaultRuntime(t *testing.T) {
|
||||
SetAsDefault: tc.setAsDefault,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(map[string]interface{}{})
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := v2.(*containerd.Config)
|
||||
|
||||
defaultRuntimeName := cfg.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"})
|
||||
require.EqualValues(t, tc.expectedDefaultRuntimeName, defaultRuntimeName)
|
||||
})
|
||||
@@ -195,23 +196,21 @@ func TestUpdateV2Config(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(map[string]interface{}{})
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.Empty),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v2.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -348,28 +347,75 @@ func TestUpdateV2ConfigWithRuncPresent(t *testing.T) {
|
||||
RuntimeDir: runtimeDir,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(runcConfigMapV2("/runc-binary"))
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(runcConfigMapV2("/runc-binary"))),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
ContainerAnnotations: []string{"cdi.k8s.io/*"},
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expectedConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.String(), cfg.String())
|
||||
require.Equal(t, expected.String(), v2.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateV2ConfigEnableCDI(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
const runtimeDir = "/test/runtime/dir"
|
||||
|
||||
testCases := []struct {
|
||||
enableCDI bool
|
||||
expectedEnableCDIValue interface{}
|
||||
}{
|
||||
{},
|
||||
{
|
||||
enableCDI: false,
|
||||
expectedEnableCDIValue: nil,
|
||||
},
|
||||
{
|
||||
enableCDI: true,
|
||||
expectedEnableCDIValue: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%v", tc.enableCDI), func(t *testing.T) {
|
||||
o := &container.Options{
|
||||
EnableCDI: tc.enableCDI,
|
||||
RuntimeName: "nvidia",
|
||||
RuntimeDir: runtimeDir,
|
||||
SetAsDefault: false,
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(map[string]interface{}{})
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Logger: logger,
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
CRIRuntimePluginName: "io.containerd.grpc.v1.cri",
|
||||
}
|
||||
|
||||
err = o.UpdateConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
enableCDIValue := cfg.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "enable_cdi"})
|
||||
require.EqualValues(t, tc.expectedEnableCDIValue, enableCDIValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevertV2Config(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testCases := []struct {
|
||||
config map[string]interface {
|
||||
}
|
||||
@@ -418,24 +464,21 @@ func TestRevertV2Config(t *testing.T) {
|
||||
RuntimeName: "nvidia",
|
||||
}
|
||||
|
||||
cfg, err := toml.LoadMap(tc.config)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := toml.TreeFromMap(tc.expected)
|
||||
require.NoError(t, err)
|
||||
|
||||
v2 := &containerd.Config{
|
||||
Tree: cfg,
|
||||
RuntimeType: runtimeType,
|
||||
}
|
||||
v2, err := containerd.New(
|
||||
containerd.WithLogger(logger),
|
||||
containerd.WithConfigSource(toml.FromMap(tc.config)),
|
||||
containerd.WithRuntimeType(runtimeType),
|
||||
containerd.WithContainerAnnotations("cdi.k8s.io/*"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = o.RevertConfig(v2)
|
||||
require.NoError(t, err)
|
||||
|
||||
configContents, _ := toml.Marshal(cfg)
|
||||
expectedContents, _ := toml.Marshal(expected)
|
||||
|
||||
require.Equal(t, string(expectedContents), string(configContents))
|
||||
require.Equal(t, expected.String(), v2.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -454,6 +497,7 @@ func runtimeMapV2(binary string) map[string]interface{} {
|
||||
|
||||
func runcConfigMapV2(binary string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"version": 2,
|
||||
"plugins": map[string]interface{}{
|
||||
"io.containerd.grpc.v1.cri": map[string]interface{}{
|
||||
"containerd": map[string]interface{}{
|
||||
@@ -23,9 +23,10 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
cli "github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/containerd"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -51,8 +52,11 @@ type Options struct {
|
||||
func Flags(opts *Options) []cli.Flag {
|
||||
flags := []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "use-legacy-config",
|
||||
Usage: "Specify whether a legacy (pre v1.3) config should be used",
|
||||
Name: "use-legacy-config",
|
||||
Usage: "Specify whether a legacy (pre v1.3) config should be used. " +
|
||||
"This ensures that a version 1 container config is created by default and that the " +
|
||||
"containerd.runtimes.default_runtime config section is used to define the default " +
|
||||
"runtime instead of container.default_runtime_name.",
|
||||
Destination: &opts.useLegacyConfig,
|
||||
EnvVars: []string{"CONTAINERD_USE_LEGACY_CONFIG"},
|
||||
},
|
||||
@@ -84,13 +88,7 @@ func Flags(opts *Options) []cli.Flag {
|
||||
func Setup(c *cli.Context, o *container.Options, co *Options) error {
|
||||
log.Infof("Starting 'setup' for %v", c.App.Name)
|
||||
|
||||
cfg, err := containerd.New(
|
||||
containerd.WithPath(o.Config),
|
||||
containerd.WithConfigSource(containerd.CommandLineSource(o.HostRootMount)),
|
||||
containerd.WithRuntimeType(co.runtimeType),
|
||||
containerd.WithUseLegacyConfig(co.useLegacyConfig),
|
||||
containerd.WithContainerAnnotations(co.containerAnnotationsFromCDIPrefixes()...),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o, co)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config: %v", err)
|
||||
}
|
||||
@@ -114,13 +112,7 @@ func Setup(c *cli.Context, o *container.Options, co *Options) error {
|
||||
func Cleanup(c *cli.Context, o *container.Options, co *Options) error {
|
||||
log.Infof("Starting 'cleanup' for %v", c.App.Name)
|
||||
|
||||
cfg, err := containerd.New(
|
||||
containerd.WithPath(o.Config),
|
||||
containerd.WithConfigSource(containerd.CommandLineSource(o.HostRootMount)),
|
||||
containerd.WithRuntimeType(co.runtimeType),
|
||||
containerd.WithUseLegacyConfig(co.useLegacyConfig),
|
||||
containerd.WithContainerAnnotations(co.containerAnnotationsFromCDIPrefixes()...),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o, co)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config: %v", err)
|
||||
}
|
||||
@@ -169,13 +161,24 @@ func (o *Options) runtimeConfigOverride() (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
func GetLowlevelRuntimePaths(o *container.Options, co *Options) ([]string, error) {
|
||||
cfg, err := containerd.New(
|
||||
containerd.WithConfigSource(containerd.CommandLineSource(o.HostRootMount)),
|
||||
containerd.WithRuntimeType(co.runtimeType),
|
||||
containerd.WithUseLegacyConfig(co.useLegacyConfig),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o, co)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load containerd config: %w", err)
|
||||
}
|
||||
return engine.GetBinaryPathsForRuntimes(cfg), nil
|
||||
}
|
||||
|
||||
func getRuntimeConfig(o *container.Options, co *Options) (engine.Interface, error) {
|
||||
return containerd.New(
|
||||
containerd.WithPath(o.Config),
|
||||
containerd.WithConfigSource(
|
||||
toml.LoadFirst(
|
||||
containerd.CommandLineSource(o.HostRootMount),
|
||||
toml.FromFile(o.Config),
|
||||
),
|
||||
),
|
||||
containerd.WithRuntimeType(co.runtimeType),
|
||||
containerd.WithUseLegacyConfig(co.useLegacyConfig),
|
||||
containerd.WithContainerAnnotations(co.containerAnnotationsFromCDIPrefixes()...),
|
||||
)
|
||||
}
|
||||
@@ -24,11 +24,12 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
cli "github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/crio"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/ocihook"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -116,10 +117,7 @@ func setupHook(o *container.Options, co *Options) error {
|
||||
func setupConfig(o *container.Options) error {
|
||||
log.Infof("Updating config file")
|
||||
|
||||
cfg, err := crio.New(
|
||||
crio.WithPath(o.Config),
|
||||
crio.WithConfigSource(crio.CommandLineSource(o.HostRootMount)),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config: %v", err)
|
||||
}
|
||||
@@ -168,10 +166,7 @@ func cleanupHook(co *Options) error {
|
||||
func cleanupConfig(o *container.Options) error {
|
||||
log.Infof("Reverting config file modifications")
|
||||
|
||||
cfg, err := crio.New(
|
||||
crio.WithPath(o.Config),
|
||||
crio.WithConfigSource(crio.CommandLineSource(o.HostRootMount)),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config: %v", err)
|
||||
}
|
||||
@@ -195,11 +190,21 @@ func RestartCrio(o *container.Options) error {
|
||||
}
|
||||
|
||||
func GetLowlevelRuntimePaths(o *container.Options) ([]string, error) {
|
||||
cfg, err := crio.New(
|
||||
crio.WithConfigSource(crio.CommandLineSource(o.HostRootMount)),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load crio config: %w", err)
|
||||
}
|
||||
return engine.GetBinaryPathsForRuntimes(cfg), nil
|
||||
}
|
||||
|
||||
func getRuntimeConfig(o *container.Options) (engine.Interface, error) {
|
||||
return crio.New(
|
||||
crio.WithPath(o.Config),
|
||||
crio.WithConfigSource(
|
||||
toml.LoadFirst(
|
||||
crio.CommandLineSource(o.HostRootMount),
|
||||
toml.FromFile(o.Config),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
cli "github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/docker"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -45,9 +45,7 @@ func Flags(opts *Options) []cli.Flag {
|
||||
func Setup(c *cli.Context, o *container.Options) error {
|
||||
log.Infof("Starting 'setup' for %v", c.App.Name)
|
||||
|
||||
cfg, err := docker.New(
|
||||
docker.WithPath(o.Config),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config: %v", err)
|
||||
}
|
||||
@@ -71,9 +69,7 @@ func Setup(c *cli.Context, o *container.Options) error {
|
||||
func Cleanup(c *cli.Context, o *container.Options) error {
|
||||
log.Infof("Starting 'cleanup' for %v", c.App.Name)
|
||||
|
||||
cfg, err := docker.New(
|
||||
docker.WithPath(o.Config),
|
||||
)
|
||||
cfg, err := getRuntimeConfig(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config: %v", err)
|
||||
}
|
||||
@@ -107,3 +103,9 @@ func GetLowlevelRuntimePaths(o *container.Options) ([]string, error) {
|
||||
}
|
||||
return engine.GetBinaryPathsForRuntimes(cfg), nil
|
||||
}
|
||||
|
||||
func getRuntimeConfig(o *container.Options) (engine.Interface, error) {
|
||||
return docker.New(
|
||||
docker.WithPath(o.Config),
|
||||
)
|
||||
}
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine/docker"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
)
|
||||
|
||||
func TestUpdateConfigDefaultRuntime(t *testing.T) {
|
||||
@@ -21,10 +21,11 @@ import (
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/runtime/containerd"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/runtime/crio"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/runtime/docker"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/runtime/containerd"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/runtime/crio"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/runtime/docker"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/toolkit"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -66,6 +67,12 @@ func Flags(opts *Options) []cli.Flag {
|
||||
Destination: &opts.RestartMode,
|
||||
EnvVars: []string{"RUNTIME_RESTART_MODE"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "enable-cdi-in-runtime",
|
||||
Usage: "Enable CDI in the configured runt ime",
|
||||
Destination: &opts.EnableCDI,
|
||||
EnvVars: []string{"RUNTIME_ENABLE_CDI"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "host-root",
|
||||
Usage: "Specify the path to the host root to be used when restarting the runtime using systemd",
|
||||
@@ -98,10 +105,14 @@ func Flags(opts *Options) []cli.Flag {
|
||||
}
|
||||
|
||||
// ValidateOptions checks whether the specified options are valid
|
||||
func ValidateOptions(opts *Options, runtime string, toolkitRoot string) error {
|
||||
func ValidateOptions(c *cli.Context, opts *Options, runtime string, toolkitRoot string, to *toolkit.Options) error {
|
||||
// We set this option here to ensure that it is available in future calls.
|
||||
opts.RuntimeDir = toolkitRoot
|
||||
|
||||
if !c.IsSet("enable-cdi-in-runtime") {
|
||||
opts.EnableCDI = to.CDI.Enabled
|
||||
}
|
||||
|
||||
// Apply the runtime-specific config changes.
|
||||
switch runtime {
|
||||
case containerd.Name:
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type executableTarget struct {
|
||||
@@ -33,6 +31,7 @@ type executableTarget struct {
|
||||
}
|
||||
|
||||
type executable struct {
|
||||
fileInstaller
|
||||
source string
|
||||
target executableTarget
|
||||
env map[string]string
|
||||
@@ -43,21 +42,21 @@ type executable struct {
|
||||
// install installs an executable component of the NVIDIA container toolkit. The source executable
|
||||
// is copied to a `.real` file and a wapper is created to set up the environment as required.
|
||||
func (e executable) install(destFolder string) (string, error) {
|
||||
log.Infof("Installing executable '%v' to %v", e.source, destFolder)
|
||||
e.logger.Infof("Installing executable '%v' to %v", e.source, destFolder)
|
||||
|
||||
dotfileName := e.dotfileName()
|
||||
|
||||
installedDotfileName, err := installFileToFolderWithName(destFolder, dotfileName, e.source)
|
||||
installedDotfileName, err := e.installFileToFolderWithName(destFolder, dotfileName, e.source)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error installing file '%v' as '%v': %v", e.source, dotfileName, err)
|
||||
}
|
||||
log.Infof("Installed '%v'", installedDotfileName)
|
||||
e.logger.Infof("Installed '%v'", installedDotfileName)
|
||||
|
||||
wrapperFilename, err := e.installWrapper(destFolder, installedDotfileName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error wrapping '%v': %v", installedDotfileName, err)
|
||||
}
|
||||
log.Infof("Installed wrapper '%v'", wrapperFilename)
|
||||
e.logger.Infof("Installed wrapper '%v'", wrapperFilename)
|
||||
|
||||
return wrapperFilename, nil
|
||||
}
|
||||
@@ -23,10 +23,13 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWrapper(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
const shebang = "#! /bin/sh"
|
||||
const destFolder = "/dest/folder"
|
||||
const dotfileName = "source.real"
|
||||
@@ -98,6 +101,8 @@ func TestWrapper(t *testing.T) {
|
||||
for i, tc := range testCases {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
tc.e.logger = logger
|
||||
|
||||
err := tc.e.writeWrapperTo(buf, destFolder, dotfileName)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -107,6 +112,8 @@ func TestWrapper(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstallExecutable(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
inputFolder, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(inputFolder)
|
||||
@@ -121,6 +128,9 @@ func TestInstallExecutable(t *testing.T) {
|
||||
require.NoError(t, sourceFile.Close())
|
||||
|
||||
e := executable{
|
||||
fileInstaller: fileInstaller{
|
||||
logger: logger,
|
||||
},
|
||||
source: source,
|
||||
target: executableTarget{
|
||||
dotfileName: "input.real",
|
||||
95
cmd/nvidia-ctk-installer/container/toolkit/file-installer.go
Normal file
95
cmd/nvidia-ctk-installer/container/toolkit/file-installer.go
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
# 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 toolkit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
type fileInstaller struct {
|
||||
logger logger.Interface
|
||||
// sourceRoot specifies the root that is searched for the components to install.
|
||||
sourceRoot string
|
||||
}
|
||||
|
||||
// installFileToFolder copies a source file to a destination folder.
|
||||
// The path of the input file is ignored.
|
||||
// e.g. installFileToFolder("/some/path/file.txt", "/output/path")
|
||||
// will result in a file "/output/path/file.txt" being generated
|
||||
func (t *fileInstaller) installFileToFolder(destFolder string, src string) (string, error) {
|
||||
name := filepath.Base(src)
|
||||
return t.installFileToFolderWithName(destFolder, name, src)
|
||||
}
|
||||
|
||||
// cp src destFolder/name
|
||||
func (t *fileInstaller) installFileToFolderWithName(destFolder string, name, src string) (string, error) {
|
||||
dest := filepath.Join(destFolder, name)
|
||||
err := t.installFile(dest, src)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error copying '%v' to '%v': %v", src, dest, err)
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// installFile copies a file from src to dest and maintains
|
||||
// file modes
|
||||
func (t *fileInstaller) installFile(dest string, src string) error {
|
||||
src = filepath.Join(t.sourceRoot, src)
|
||||
t.logger.Infof("Installing '%v' to '%v'", src, dest)
|
||||
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening source: %v", err)
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
destination, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating destination: %v", err)
|
||||
}
|
||||
defer destination.Close()
|
||||
|
||||
_, err = io.Copy(destination, source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying file: %v", err)
|
||||
}
|
||||
|
||||
err = applyModeFromSource(dest, src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting destination file mode: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyModeFromSource sets the file mode for a destination file
|
||||
// to match that of a specified source file
|
||||
func applyModeFromSource(dest string, src string) error {
|
||||
sourceInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting file info for '%v': %v", src, err)
|
||||
}
|
||||
err = os.Chmod(dest, sourceInfo.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting mode for '%v': %v", dest, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
40
cmd/nvidia-ctk-installer/container/toolkit/options.go
Normal file
40
cmd/nvidia-ctk-installer/container/toolkit/options.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
# 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 toolkit
|
||||
|
||||
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
|
||||
// An Option provides a mechanism to configure an Installer.
|
||||
type Option func(*Installer)
|
||||
|
||||
func WithLogger(logger logger.Interface) Option {
|
||||
return func(i *Installer) {
|
||||
i.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func WithToolkitRoot(toolkitRoot string) Option {
|
||||
return func(i *Installer) {
|
||||
i.toolkitRoot = toolkitRoot
|
||||
}
|
||||
}
|
||||
|
||||
func WithSourceRoot(sourceRoot string) Option {
|
||||
return func(i *Installer) {
|
||||
i.sourceRoot = sourceRoot
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/operator"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/operator"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,10 +29,10 @@ const (
|
||||
|
||||
// installContainerRuntimes sets up the NVIDIA container runtimes, copying the executables
|
||||
// and implementing the required wrapper
|
||||
func installContainerRuntimes(toolkitDir string, driverRoot string) error {
|
||||
func (t *Installer) installContainerRuntimes(toolkitDir string) error {
|
||||
runtimes := operator.GetRuntimes()
|
||||
for _, runtime := range runtimes {
|
||||
r := newNvidiaContainerRuntimeInstaller(runtime.Path)
|
||||
r := t.newNvidiaContainerRuntimeInstaller(runtime.Path)
|
||||
|
||||
_, err := r.install(toolkitDir)
|
||||
if err != nil {
|
||||
@@ -46,17 +46,17 @@ func installContainerRuntimes(toolkitDir string, driverRoot string) error {
|
||||
// This installer will copy the specified source executable to the toolkit directory.
|
||||
// The executable is copied to a file with the same name as the source, but with a ".real" suffix and a wrapper is
|
||||
// created to allow for the configuration of the runtime environment.
|
||||
func newNvidiaContainerRuntimeInstaller(source string) *executable {
|
||||
func (t *Installer) newNvidiaContainerRuntimeInstaller(source string) *executable {
|
||||
wrapperName := filepath.Base(source)
|
||||
dotfileName := wrapperName + ".real"
|
||||
target := executableTarget{
|
||||
dotfileName: dotfileName,
|
||||
wrapperName: wrapperName,
|
||||
}
|
||||
return newRuntimeInstaller(source, target, nil)
|
||||
return t.newRuntimeInstaller(source, target, nil)
|
||||
}
|
||||
|
||||
func newRuntimeInstaller(source string, target executableTarget, env map[string]string) *executable {
|
||||
func (t *Installer) newRuntimeInstaller(source string, target executableTarget, env map[string]string) *executable {
|
||||
preLines := []string{
|
||||
"",
|
||||
"cat /proc/modules | grep -e \"^nvidia \" >/dev/null 2>&1",
|
||||
@@ -74,10 +74,11 @@ func newRuntimeInstaller(source string, target executableTarget, env map[string]
|
||||
}
|
||||
|
||||
r := executable{
|
||||
source: source,
|
||||
target: target,
|
||||
env: runtimeEnv,
|
||||
preLines: preLines,
|
||||
fileInstaller: t.fileInstaller,
|
||||
source: source,
|
||||
target: target,
|
||||
env: runtimeEnv,
|
||||
preLines: preLines,
|
||||
}
|
||||
|
||||
return &r
|
||||
@@ -21,11 +21,18 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNvidiaContainerRuntimeInstallerWrapper(t *testing.T) {
|
||||
r := newNvidiaContainerRuntimeInstaller(nvidiaContainerRuntimeSource)
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
i := Installer{
|
||||
fileInstaller: fileInstaller{
|
||||
logger: logger,
|
||||
},
|
||||
}
|
||||
r := i.newNvidiaContainerRuntimeInstaller(nvidiaContainerRuntimeSource)
|
||||
|
||||
const shebang = "#! /bin/sh"
|
||||
const destFolder = "/dest/folder"
|
||||
@@ -17,19 +17,17 @@
|
||||
package toolkit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"tags.cncf.io/container-device-interface/pkg/cdi"
|
||||
"tags.cncf.io/container-device-interface/pkg/parser"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/system/nvdevices"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
|
||||
transformroot "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform/root"
|
||||
@@ -44,10 +42,16 @@ const (
|
||||
|
||||
nvidiaContainerToolkitConfigSource = "/etc/nvidia-container-runtime/config.toml"
|
||||
configFilename = "config.toml"
|
||||
|
||||
toolkitPidFilename = "toolkit.pid"
|
||||
)
|
||||
|
||||
type cdiOptions struct {
|
||||
Enabled bool
|
||||
outputDir string
|
||||
kind string
|
||||
vendor string
|
||||
class string
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
DriverRoot string
|
||||
DevRoot string
|
||||
@@ -67,11 +71,8 @@ type Options struct {
|
||||
|
||||
ContainerCLIDebug string
|
||||
|
||||
cdiEnabled bool
|
||||
cdiOutputDir string
|
||||
cdiKind string
|
||||
cdiVendor string
|
||||
cdiClass string
|
||||
// CDI stores the CDI options for the toolkit.
|
||||
CDI cdiOptions
|
||||
|
||||
createDeviceNodes cli.StringSlice
|
||||
|
||||
@@ -174,21 +175,21 @@ func Flags(opts *Options) []cli.Flag {
|
||||
Name: "cdi-enabled",
|
||||
Aliases: []string{"enable-cdi"},
|
||||
Usage: "enable the generation of a CDI specification",
|
||||
Destination: &opts.cdiEnabled,
|
||||
Destination: &opts.CDI.Enabled,
|
||||
EnvVars: []string{"CDI_ENABLED", "ENABLE_CDI"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "cdi-output-dir",
|
||||
Usage: "the directory where the CDI output files are to be written. If this is set to '', no CDI specification is generated.",
|
||||
Value: "/var/run/cdi",
|
||||
Destination: &opts.cdiOutputDir,
|
||||
Destination: &opts.CDI.outputDir,
|
||||
EnvVars: []string{"CDI_OUTPUT_DIR"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "cdi-kind",
|
||||
Usage: "the vendor string to use for the generated CDI specification",
|
||||
Value: "management.nvidia.com/gpu",
|
||||
Destination: &opts.cdiKind,
|
||||
Destination: &opts.CDI.kind,
|
||||
EnvVars: []string{"CDI_KIND"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
@@ -215,25 +216,48 @@ func Flags(opts *Options) []cli.Flag {
|
||||
return flags
|
||||
}
|
||||
|
||||
// ValidateOptions checks whether the specified options are valid
|
||||
func ValidateOptions(opts *Options, toolkitRoot string) error {
|
||||
if toolkitRoot == "" {
|
||||
return fmt.Errorf("invalid --toolkit-root option: %v", toolkitRoot)
|
||||
// An Installer is used to install the NVIDIA Container Toolkit from the toolkit container.
|
||||
type Installer struct {
|
||||
fileInstaller
|
||||
// toolkitRoot specifies the destination path at which the toolkit is installed.
|
||||
toolkitRoot string
|
||||
}
|
||||
|
||||
// NewInstaller creates an installer for the NVIDIA Container Toolkit.
|
||||
func NewInstaller(opts ...Option) *Installer {
|
||||
i := &Installer{}
|
||||
for _, opt := range opts {
|
||||
opt(i)
|
||||
}
|
||||
|
||||
vendor, class := parser.ParseQualifier(opts.cdiKind)
|
||||
if i.logger == nil {
|
||||
i.logger = logger.New()
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// ValidateOptions checks whether the specified options are valid
|
||||
func (t *Installer) ValidateOptions(opts *Options) error {
|
||||
if t == nil {
|
||||
return fmt.Errorf("toolkit installer is not initilized")
|
||||
}
|
||||
if t.toolkitRoot == "" {
|
||||
return fmt.Errorf("invalid --toolkit-root option: %v", t.toolkitRoot)
|
||||
}
|
||||
|
||||
vendor, class := parser.ParseQualifier(opts.CDI.kind)
|
||||
if err := parser.ValidateVendorName(vendor); err != nil {
|
||||
return fmt.Errorf("invalid CDI vendor name: %v", err)
|
||||
}
|
||||
if err := parser.ValidateClassName(class); err != nil {
|
||||
return fmt.Errorf("invalid CDI class name: %v", err)
|
||||
}
|
||||
opts.cdiVendor = vendor
|
||||
opts.cdiClass = class
|
||||
opts.CDI.vendor = vendor
|
||||
opts.CDI.class = class
|
||||
|
||||
if opts.cdiEnabled && opts.cdiOutputDir == "" {
|
||||
log.Warning("Skipping CDI spec generation (no output directory specified)")
|
||||
opts.cdiEnabled = false
|
||||
if opts.CDI.Enabled && opts.CDI.outputDir == "" {
|
||||
t.logger.Warning("Skipping CDI spec generation (no output directory specified)")
|
||||
opts.CDI.Enabled = false
|
||||
}
|
||||
|
||||
isDisabled := false
|
||||
@@ -246,8 +270,8 @@ func ValidateOptions(opts *Options, toolkitRoot string) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !opts.cdiEnabled && !isDisabled {
|
||||
log.Info("disabling device node creation since --cdi-enabled=false")
|
||||
if !opts.CDI.Enabled && !isDisabled {
|
||||
t.logger.Info("disabling device node creation since --cdi-enabled=false")
|
||||
isDisabled = true
|
||||
}
|
||||
if isDisabled {
|
||||
@@ -257,117 +281,93 @@ func ValidateOptions(opts *Options, toolkitRoot string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TryDelete attempts to remove the specified toolkit folder.
|
||||
// A toolkit.pid file -- if present -- is skipped.
|
||||
func TryDelete(cli *cli.Context, toolkitRoot string) error {
|
||||
log.Infof("Attempting to delete NVIDIA container toolkit from '%v'", toolkitRoot)
|
||||
|
||||
contents, err := os.ReadDir(toolkitRoot)
|
||||
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to read the contents of %v: %w", toolkitRoot, err)
|
||||
}
|
||||
|
||||
for _, content := range contents {
|
||||
if content.Name() == toolkitPidFilename {
|
||||
continue
|
||||
}
|
||||
name := filepath.Join(toolkitRoot, content.Name())
|
||||
if err := os.RemoveAll(name); err != nil {
|
||||
log.Warningf("could not remove %v: %v", name, err)
|
||||
}
|
||||
}
|
||||
if err := os.RemoveAll(toolkitRoot); err != nil {
|
||||
log.Warningf("could not remove %v: %v", toolkitRoot, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Install installs the components of the NVIDIA container toolkit.
|
||||
// Any existing installation is removed.
|
||||
func Install(cli *cli.Context, opts *Options, toolkitRoot string) error {
|
||||
log.Infof("Installing NVIDIA container toolkit to '%v'", toolkitRoot)
|
||||
func (t *Installer) Install(cli *cli.Context, opts *Options) error {
|
||||
if t == nil {
|
||||
return fmt.Errorf("toolkit installer is not initilized")
|
||||
}
|
||||
t.logger.Infof("Installing NVIDIA container toolkit to '%v'", t.toolkitRoot)
|
||||
|
||||
log.Infof("Removing existing NVIDIA container toolkit installation")
|
||||
err := os.RemoveAll(toolkitRoot)
|
||||
t.logger.Infof("Removing existing NVIDIA container toolkit installation")
|
||||
err := os.RemoveAll(t.toolkitRoot)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error removing toolkit directory: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error removing toolkit directory: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error removing toolkit directory: %v", err))
|
||||
}
|
||||
|
||||
toolkitConfigDir := filepath.Join(toolkitRoot, ".config", "nvidia-container-runtime")
|
||||
toolkitConfigDir := filepath.Join(t.toolkitRoot, ".config", "nvidia-container-runtime")
|
||||
toolkitConfigPath := filepath.Join(toolkitConfigDir, configFilename)
|
||||
|
||||
err = createDirectories(toolkitRoot, toolkitConfigDir)
|
||||
err = t.createDirectories(t.toolkitRoot, toolkitConfigDir)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("could not create required directories: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("could not create required directories: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("could not create required directories: %v", err))
|
||||
}
|
||||
|
||||
err = installContainerLibraries(toolkitRoot)
|
||||
err = t.installContainerLibraries(t.toolkitRoot)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA container library: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container library: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container library: %v", err))
|
||||
}
|
||||
|
||||
err = installContainerRuntimes(toolkitRoot, opts.DriverRoot)
|
||||
err = t.installContainerRuntimes(t.toolkitRoot)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA container runtime: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container runtime: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container runtime: %v", err))
|
||||
}
|
||||
|
||||
nvidiaContainerCliExecutable, err := installContainerCLI(toolkitRoot)
|
||||
nvidiaContainerCliExecutable, err := t.installContainerCLI(t.toolkitRoot)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA container CLI: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container CLI: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container CLI: %v", err))
|
||||
}
|
||||
|
||||
nvidiaContainerRuntimeHookPath, err := installRuntimeHook(toolkitRoot, toolkitConfigPath)
|
||||
nvidiaContainerRuntimeHookPath, err := t.installRuntimeHook(t.toolkitRoot, toolkitConfigPath)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA container runtime hook: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container runtime hook: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container runtime hook: %v", err))
|
||||
}
|
||||
|
||||
nvidiaCTKPath, err := installContainerToolkitCLI(toolkitRoot)
|
||||
nvidiaCTKPath, err := t.installContainerToolkitCLI(t.toolkitRoot)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA Container Toolkit CLI: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA Container Toolkit CLI: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA Container Toolkit CLI: %v", err))
|
||||
}
|
||||
|
||||
nvidiaCDIHookPath, err := installContainerCDIHookCLI(toolkitRoot)
|
||||
nvidiaCDIHookPath, err := t.installContainerCDIHookCLI(t.toolkitRoot)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA Container CDI Hook CLI: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA Container CDI Hook CLI: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA Container CDI Hook CLI: %v", err))
|
||||
}
|
||||
|
||||
err = installToolkitConfig(cli, toolkitConfigPath, nvidiaContainerCliExecutable, nvidiaCTKPath, nvidiaContainerRuntimeHookPath, opts)
|
||||
err = t.installToolkitConfig(cli, toolkitConfigPath, nvidiaContainerCliExecutable, nvidiaCTKPath, nvidiaContainerRuntimeHookPath, opts)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error installing NVIDIA container toolkit config: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container toolkit config: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error installing NVIDIA container toolkit config: %v", err))
|
||||
}
|
||||
|
||||
err = createDeviceNodes(opts)
|
||||
err = t.createDeviceNodes(opts)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error creating device nodes: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error creating device nodes: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error creating device nodes: %v", err))
|
||||
}
|
||||
|
||||
err = generateCDISpec(opts, nvidiaCDIHookPath)
|
||||
err = t.generateCDISpec(opts, nvidiaCDIHookPath)
|
||||
if err != nil && !opts.ignoreErrors {
|
||||
return fmt.Errorf("error generating CDI specification: %v", err)
|
||||
} else if err != nil {
|
||||
log.Errorf("Ignoring error: %v", fmt.Errorf("error generating CDI specification: %v", err))
|
||||
t.logger.Errorf("Ignoring error: %v", fmt.Errorf("error generating CDI specification: %v", err))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -378,8 +378,8 @@ func Install(cli *cli.Context, opts *Options, toolkitRoot string) error {
|
||||
// A predefined set of library candidates are considered, with the first one
|
||||
// resulting in success being installed to the toolkit folder. The install process
|
||||
// resolves the symlink for the library and copies the versioned library itself.
|
||||
func installContainerLibraries(toolkitRoot string) error {
|
||||
log.Infof("Installing NVIDIA container library to '%v'", toolkitRoot)
|
||||
func (t *Installer) installContainerLibraries(toolkitRoot string) error {
|
||||
t.logger.Infof("Installing NVIDIA container library to '%v'", toolkitRoot)
|
||||
|
||||
libs := []string{
|
||||
"libnvidia-container.so.1",
|
||||
@@ -387,7 +387,7 @@ func installContainerLibraries(toolkitRoot string) error {
|
||||
}
|
||||
|
||||
for _, l := range libs {
|
||||
err := installLibrary(l, toolkitRoot)
|
||||
err := t.installLibrary(l, toolkitRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install %s: %v", l, err)
|
||||
}
|
||||
@@ -397,23 +397,23 @@ func installContainerLibraries(toolkitRoot string) error {
|
||||
}
|
||||
|
||||
// installLibrary installs the specified library to the toolkit directory.
|
||||
func installLibrary(libName string, toolkitRoot string) error {
|
||||
libraryPath, err := findLibrary("", libName)
|
||||
func (t *Installer) installLibrary(libName string, toolkitRoot string) error {
|
||||
libraryPath, err := t.findLibrary(libName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error locating NVIDIA container library: %v", err)
|
||||
}
|
||||
|
||||
installedLibPath, err := installFileToFolder(toolkitRoot, libraryPath)
|
||||
installedLibPath, err := t.installFileToFolder(toolkitRoot, libraryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error installing %v to %v: %v", libraryPath, toolkitRoot, err)
|
||||
}
|
||||
log.Infof("Installed '%v' to '%v'", libraryPath, installedLibPath)
|
||||
t.logger.Infof("Installed '%v' to '%v'", libraryPath, installedLibPath)
|
||||
|
||||
if filepath.Base(installedLibPath) == libName {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = installSymlink(toolkitRoot, libName, installedLibPath)
|
||||
err = t.installSymlink(toolkitRoot, libName, installedLibPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error installing symlink for NVIDIA container library: %v", err)
|
||||
}
|
||||
@@ -423,8 +423,8 @@ func installLibrary(libName string, toolkitRoot string) error {
|
||||
|
||||
// installToolkitConfig installs the config file for the NVIDIA container toolkit ensuring
|
||||
// that the settings are updated to match the desired install and nvidia driver directories.
|
||||
func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContainerCliExecutablePath string, nvidiaCTKPath string, nvidaContainerRuntimeHookPath string, opts *Options) error {
|
||||
log.Infof("Installing NVIDIA container toolkit config '%v'", toolkitConfigPath)
|
||||
func (t *Installer) installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContainerCliExecutablePath string, nvidiaCTKPath string, nvidaContainerRuntimeHookPath string, opts *Options) error {
|
||||
t.logger.Infof("Installing NVIDIA container toolkit config '%v'", toolkitConfigPath)
|
||||
|
||||
cfg, err := config.New(
|
||||
config.WithConfigFile(nvidiaContainerToolkitConfigSource),
|
||||
@@ -486,11 +486,11 @@ func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContai
|
||||
|
||||
for key, value := range optionalConfigValues {
|
||||
if !c.IsSet(key) {
|
||||
log.Infof("Skipping unset option: %v", key)
|
||||
t.logger.Infof("Skipping unset option: %v", key)
|
||||
continue
|
||||
}
|
||||
if value == nil {
|
||||
log.Infof("Skipping option with nil value: %v", key)
|
||||
t.logger.Infof("Skipping option with nil value: %v", key)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContai
|
||||
}
|
||||
value = v.Value()
|
||||
default:
|
||||
log.Warningf("Unexpected type for option %v=%v: %T", key, value, v)
|
||||
t.logger.Warningf("Unexpected type for option %v=%v: %T", key, value, v)
|
||||
}
|
||||
|
||||
cfg.Set(key, value)
|
||||
@@ -517,16 +517,17 @@ func installToolkitConfig(c *cli.Context, toolkitConfigPath string, nvidiaContai
|
||||
|
||||
os.Stdout.WriteString("Using config:\n")
|
||||
if _, err = cfg.WriteTo(os.Stdout); err != nil {
|
||||
log.Warningf("Failed to output config to STDOUT: %v", err)
|
||||
t.logger.Warningf("Failed to output config to STDOUT: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// installContainerToolkitCLI installs the nvidia-ctk CLI executable and wrapper.
|
||||
func installContainerToolkitCLI(toolkitDir string) (string, error) {
|
||||
func (t *Installer) installContainerToolkitCLI(toolkitDir string) (string, error) {
|
||||
e := executable{
|
||||
source: "/usr/bin/nvidia-ctk",
|
||||
fileInstaller: t.fileInstaller,
|
||||
source: "/usr/bin/nvidia-ctk",
|
||||
target: executableTarget{
|
||||
dotfileName: "nvidia-ctk.real",
|
||||
wrapperName: "nvidia-ctk",
|
||||
@@ -537,9 +538,10 @@ func installContainerToolkitCLI(toolkitDir string) (string, error) {
|
||||
}
|
||||
|
||||
// installContainerCDIHookCLI installs the nvidia-cdi-hook CLI executable and wrapper.
|
||||
func installContainerCDIHookCLI(toolkitDir string) (string, error) {
|
||||
func (t *Installer) installContainerCDIHookCLI(toolkitDir string) (string, error) {
|
||||
e := executable{
|
||||
source: "/usr/bin/nvidia-cdi-hook",
|
||||
fileInstaller: t.fileInstaller,
|
||||
source: "/usr/bin/nvidia-cdi-hook",
|
||||
target: executableTarget{
|
||||
dotfileName: "nvidia-cdi-hook.real",
|
||||
wrapperName: "nvidia-cdi-hook",
|
||||
@@ -551,15 +553,16 @@ func installContainerCDIHookCLI(toolkitDir string) (string, error) {
|
||||
|
||||
// installContainerCLI sets up the NVIDIA container CLI executable, copying the executable
|
||||
// and implementing the required wrapper
|
||||
func installContainerCLI(toolkitRoot string) (string, error) {
|
||||
log.Infof("Installing NVIDIA container CLI from '%v'", nvidiaContainerCliSource)
|
||||
func (t *Installer) installContainerCLI(toolkitRoot string) (string, error) {
|
||||
t.logger.Infof("Installing NVIDIA container CLI from '%v'", nvidiaContainerCliSource)
|
||||
|
||||
env := map[string]string{
|
||||
"LD_LIBRARY_PATH": toolkitRoot,
|
||||
}
|
||||
|
||||
e := executable{
|
||||
source: nvidiaContainerCliSource,
|
||||
fileInstaller: t.fileInstaller,
|
||||
source: nvidiaContainerCliSource,
|
||||
target: executableTarget{
|
||||
dotfileName: "nvidia-container-cli.real",
|
||||
wrapperName: "nvidia-container-cli",
|
||||
@@ -576,15 +579,16 @@ func installContainerCLI(toolkitRoot string) (string, error) {
|
||||
|
||||
// installRuntimeHook sets up the NVIDIA runtime hook, copying the executable
|
||||
// and implementing the required wrapper
|
||||
func installRuntimeHook(toolkitRoot string, configFilePath string) (string, error) {
|
||||
log.Infof("Installing NVIDIA container runtime hook from '%v'", nvidiaContainerRuntimeHookSource)
|
||||
func (t *Installer) installRuntimeHook(toolkitRoot string, configFilePath string) (string, error) {
|
||||
t.logger.Infof("Installing NVIDIA container runtime hook from '%v'", nvidiaContainerRuntimeHookSource)
|
||||
|
||||
argLines := []string{
|
||||
fmt.Sprintf("-config \"%s\"", configFilePath),
|
||||
}
|
||||
|
||||
e := executable{
|
||||
source: nvidiaContainerRuntimeHookSource,
|
||||
fileInstaller: t.fileInstaller,
|
||||
source: nvidiaContainerRuntimeHookSource,
|
||||
target: executableTarget{
|
||||
dotfileName: "nvidia-container-runtime-hook.real",
|
||||
wrapperName: "nvidia-container-runtime-hook",
|
||||
@@ -597,7 +601,7 @@ func installRuntimeHook(toolkitRoot string, configFilePath string) (string, erro
|
||||
return "", fmt.Errorf("error installing NVIDIA container runtime hook: %v", err)
|
||||
}
|
||||
|
||||
err = installSymlink(toolkitRoot, "nvidia-container-toolkit", installedPath)
|
||||
err = t.installSymlink(toolkitRoot, "nvidia-container-toolkit", installedPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error installing symlink to NVIDIA container runtime hook: %v", err)
|
||||
}
|
||||
@@ -607,10 +611,10 @@ func installRuntimeHook(toolkitRoot string, configFilePath string) (string, erro
|
||||
|
||||
// installSymlink creates a symlink in the toolkitDirectory that points to the specified target.
|
||||
// Note: The target is assumed to be local to the toolkit directory
|
||||
func installSymlink(toolkitRoot string, link string, target string) error {
|
||||
func (t *Installer) installSymlink(toolkitRoot string, link string, target string) error {
|
||||
symlinkPath := filepath.Join(toolkitRoot, link)
|
||||
targetPath := filepath.Base(target)
|
||||
log.Infof("Creating symlink '%v' -> '%v'", symlinkPath, targetPath)
|
||||
t.logger.Infof("Creating symlink '%v' -> '%v'", symlinkPath, targetPath)
|
||||
|
||||
err := os.Symlink(targetPath, symlinkPath)
|
||||
if err != nil {
|
||||
@@ -619,72 +623,10 @@ func installSymlink(toolkitRoot string, link string, target string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// installFileToFolder copies a source file to a destination folder.
|
||||
// The path of the input file is ignored.
|
||||
// e.g. installFileToFolder("/some/path/file.txt", "/output/path")
|
||||
// will result in a file "/output/path/file.txt" being generated
|
||||
func installFileToFolder(destFolder string, src string) (string, error) {
|
||||
name := filepath.Base(src)
|
||||
return installFileToFolderWithName(destFolder, name, src)
|
||||
}
|
||||
|
||||
// cp src destFolder/name
|
||||
func installFileToFolderWithName(destFolder string, name, src string) (string, error) {
|
||||
dest := filepath.Join(destFolder, name)
|
||||
err := installFile(dest, src)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error copying '%v' to '%v': %v", src, dest, err)
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// installFile copies a file from src to dest and maintains
|
||||
// file modes
|
||||
func installFile(dest string, src string) error {
|
||||
log.Infof("Installing '%v' to '%v'", src, dest)
|
||||
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening source: %v", err)
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
destination, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating destination: %v", err)
|
||||
}
|
||||
defer destination.Close()
|
||||
|
||||
_, err = io.Copy(destination, source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying file: %v", err)
|
||||
}
|
||||
|
||||
err = applyModeFromSource(dest, src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting destination file mode: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyModeFromSource sets the file mode for a destination file
|
||||
// to match that of a specified source file
|
||||
func applyModeFromSource(dest string, src string) error {
|
||||
sourceInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting file info for '%v': %v", src, err)
|
||||
}
|
||||
err = os.Chmod(dest, sourceInfo.Mode())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting mode for '%v': %v", dest, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLibrary searches a set of candidate libraries in the specified root for
|
||||
// a given library name
|
||||
func findLibrary(root string, libName string) (string, error) {
|
||||
log.Infof("Finding library %v (root=%v)", libName, root)
|
||||
func (t *Installer) findLibrary(libName string) (string, error) {
|
||||
t.logger.Infof("Finding library %v (root=%v)", libName)
|
||||
|
||||
candidateDirs := []string{
|
||||
"/usr/lib64",
|
||||
@@ -693,16 +635,16 @@ func findLibrary(root string, libName string) (string, error) {
|
||||
}
|
||||
|
||||
for _, d := range candidateDirs {
|
||||
l := filepath.Join(root, d, libName)
|
||||
log.Infof("Checking library candidate '%v'", l)
|
||||
l := filepath.Join(t.sourceRoot, d, libName)
|
||||
t.logger.Infof("Checking library candidate '%v'", l)
|
||||
|
||||
libraryCandidate, err := resolveLink(l)
|
||||
libraryCandidate, err := t.resolveLink(l)
|
||||
if err != nil {
|
||||
log.Infof("Skipping library candidate '%v': %v", l, err)
|
||||
t.logger.Infof("Skipping library candidate '%v': %v", l, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return libraryCandidate, nil
|
||||
return strings.TrimPrefix(libraryCandidate, t.sourceRoot), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("error locating library '%v'", libName)
|
||||
@@ -711,20 +653,20 @@ func findLibrary(root string, libName string) (string, error) {
|
||||
// resolveLink finds the target of a symlink or the file itself in the
|
||||
// case of a regular file.
|
||||
// This is equivalent to running `readlink -f ${l}`
|
||||
func resolveLink(l string) (string, error) {
|
||||
func (t *Installer) resolveLink(l string) (string, error) {
|
||||
resolved, err := filepath.EvalSymlinks(l)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error resolving link '%v': %v", l, err)
|
||||
}
|
||||
if l != resolved {
|
||||
log.Infof("Resolved link: '%v' => '%v'", l, resolved)
|
||||
t.logger.Infof("Resolved link: '%v' => '%v'", l, resolved)
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
func createDirectories(dir ...string) error {
|
||||
func (t *Installer) createDirectories(dir ...string) error {
|
||||
for _, d := range dir {
|
||||
log.Infof("Creating directory '%v'", d)
|
||||
t.logger.Infof("Creating directory '%v'", d)
|
||||
err := os.MkdirAll(d, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating directory: %v", err)
|
||||
@@ -733,7 +675,7 @@ func createDirectories(dir ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDeviceNodes(opts *Options) error {
|
||||
func (t *Installer) createDeviceNodes(opts *Options) error {
|
||||
modes := opts.createDeviceNodes.Value()
|
||||
if len(modes) == 0 {
|
||||
return nil
|
||||
@@ -747,9 +689,9 @@ func createDeviceNodes(opts *Options) error {
|
||||
}
|
||||
|
||||
for _, mode := range modes {
|
||||
log.Infof("Creating %v device nodes at %v", mode, opts.DevRootCtrPath)
|
||||
t.logger.Infof("Creating %v device nodes at %v", mode, opts.DevRootCtrPath)
|
||||
if mode != "control" {
|
||||
log.Warningf("Unrecognised device mode: %v", mode)
|
||||
t.logger.Warningf("Unrecognised device mode: %v", mode)
|
||||
continue
|
||||
}
|
||||
if err := devices.CreateNVIDIAControlDevices(); err != nil {
|
||||
@@ -760,18 +702,19 @@ func createDeviceNodes(opts *Options) error {
|
||||
}
|
||||
|
||||
// generateCDISpec generates a CDI spec for use in management containers
|
||||
func generateCDISpec(opts *Options, nvidiaCDIHookPath string) error {
|
||||
if !opts.cdiEnabled {
|
||||
func (t *Installer) generateCDISpec(opts *Options, nvidiaCDIHookPath string) error {
|
||||
if !opts.CDI.Enabled {
|
||||
return nil
|
||||
}
|
||||
log.Info("Generating CDI spec for management containers")
|
||||
t.logger.Info("Generating CDI spec for management containers")
|
||||
cdilib, err := nvcdi.New(
|
||||
nvcdi.WithLogger(t.logger),
|
||||
nvcdi.WithMode(nvcdi.ModeManagement),
|
||||
nvcdi.WithDriverRoot(opts.DriverRootCtrPath),
|
||||
nvcdi.WithDevRoot(opts.DevRootCtrPath),
|
||||
nvcdi.WithNVIDIACDIHookPath(nvidiaCDIHookPath),
|
||||
nvcdi.WithVendor(opts.cdiVendor),
|
||||
nvcdi.WithClass(opts.cdiClass),
|
||||
nvcdi.WithVendor(opts.CDI.vendor),
|
||||
nvcdi.WithClass(opts.CDI.class),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create CDI library for management containers: %v", err)
|
||||
@@ -796,7 +739,7 @@ func generateCDISpec(opts *Options, nvidiaCDIHookPath string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate CDI name for management containers: %v", err)
|
||||
}
|
||||
err = spec.Save(filepath.Join(opts.cdiOutputDir, name))
|
||||
err = spec.Save(filepath.Join(opts.CDI.outputDir, name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save CDI spec for management containers: %v", err)
|
||||
}
|
||||
217
cmd/nvidia-ctk-installer/container/toolkit/toolkit_test.go
Normal file
217
cmd/nvidia-ctk-installer/container/toolkit/toolkit_test.go
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
# 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 toolkit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
t.Setenv("__NVCT_TESTING_DEVICES_ARE_FILES", "true")
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
artifactRoot := filepath.Join(moduleRoot, "testdata", "installer", "artifacts")
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
hostRoot string
|
||||
packageType string
|
||||
cdiEnabled bool
|
||||
expectedError error
|
||||
expectedCdiSpec string
|
||||
}{
|
||||
{
|
||||
hostRoot: "rootfs-empty",
|
||||
packageType: "deb",
|
||||
},
|
||||
{
|
||||
hostRoot: "rootfs-empty",
|
||||
packageType: "rpm",
|
||||
},
|
||||
{
|
||||
hostRoot: "rootfs-empty",
|
||||
packageType: "deb",
|
||||
cdiEnabled: true,
|
||||
expectedError: fmt.Errorf("no NVIDIA device nodes found"),
|
||||
},
|
||||
{
|
||||
hostRoot: "rootfs-1",
|
||||
packageType: "deb",
|
||||
cdiEnabled: true,
|
||||
expectedCdiSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits:
|
||||
env:
|
||||
- NVIDIA_VISIBLE_DEVICES=void
|
||||
hooks:
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- create-symlinks
|
||||
- --link
|
||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||
hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- update-ldcache
|
||||
- --folder
|
||||
- /lib/x86_64-linux-gnu
|
||||
hookName: createContainer
|
||||
path: {{ .toolkitRoot }}/nvidia-cdi-hook
|
||||
mounts:
|
||||
- containerPath: /lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
hostPath: /host/driver/root/lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
options:
|
||||
- ro
|
||||
- nosuid
|
||||
- nodev
|
||||
- bind
|
||||
devices:
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: /host/driver/root/dev/nvidia0
|
||||
path: /dev/nvidia0
|
||||
- hostPath: /host/driver/root/dev/nvidiactl
|
||||
path: /dev/nvidiactl
|
||||
- hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel0
|
||||
path: /dev/nvidia-caps-imex-channels/channel0
|
||||
- hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel1
|
||||
path: /dev/nvidia-caps-imex-channels/channel1
|
||||
- hostPath: /host/driver/root/dev/nvidia-caps-imex-channels/channel2047
|
||||
path: /dev/nvidia-caps-imex-channels/channel2047
|
||||
name: all
|
||||
kind: example.com/class
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
// hostRoot := filepath.Join(moduleRoot, "testdata", "lookup", tc.hostRoot)
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
testRoot := t.TempDir()
|
||||
toolkitRoot := filepath.Join(testRoot, "toolkit-test")
|
||||
cdiOutputDir := filepath.Join(moduleRoot, "toolkit-test", "/var/cdi")
|
||||
sourceRoot := filepath.Join(artifactRoot, tc.packageType)
|
||||
options := Options{
|
||||
DriverRoot: "/host/driver/root",
|
||||
DriverRootCtrPath: filepath.Join(moduleRoot, "testdata", "lookup", tc.hostRoot),
|
||||
CDI: cdiOptions{
|
||||
Enabled: tc.cdiEnabled,
|
||||
outputDir: cdiOutputDir,
|
||||
kind: "example.com/class",
|
||||
},
|
||||
}
|
||||
|
||||
ti := NewInstaller(
|
||||
WithLogger(logger),
|
||||
WithToolkitRoot(toolkitRoot),
|
||||
WithSourceRoot(sourceRoot),
|
||||
)
|
||||
require.NoError(t, ti.ValidateOptions(&options))
|
||||
|
||||
err := ti.Install(&cli.Context{}, &options)
|
||||
if tc.expectedError == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Contains(t, err.Error(), tc.expectedError.Error())
|
||||
}
|
||||
|
||||
require.DirExists(t, toolkitRoot)
|
||||
requireSymlink(t, toolkitRoot, "libnvidia-container.so.1", "libnvidia-container.so.99.88.77")
|
||||
requireSymlink(t, toolkitRoot, "libnvidia-container-go.so.1", "libnvidia-container-go.so.99.88.77")
|
||||
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-cdi-hook")
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-container-cli")
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-container-runtime")
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-container-runtime-hook")
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-container-runtime.cdi")
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-container-runtime.legacy")
|
||||
requireWrappedExecutable(t, toolkitRoot, "nvidia-ctk")
|
||||
|
||||
requireSymlink(t, toolkitRoot, "nvidia-container-toolkit", "nvidia-container-runtime-hook")
|
||||
|
||||
// TODO: Add checks for wrapper contents
|
||||
// grep -q -E "nvidia driver modules are not yet loaded, invoking runc directly" "${shared_dir}/usr/local/nvidia/toolkit/nvidia-container-runtime"
|
||||
// grep -q -E "exec runc \".@\"" "${shared_dir}/usr/local/nvidia/toolkit/nvidia-container-runtime"
|
||||
|
||||
require.DirExists(t, filepath.Join(toolkitRoot, ".config"))
|
||||
require.DirExists(t, filepath.Join(toolkitRoot, ".config", "nvidia-container-runtime"))
|
||||
require.FileExists(t, filepath.Join(toolkitRoot, ".config", "nvidia-container-runtime", "config.toml"))
|
||||
|
||||
cfgToml, err := config.New(config.WithConfigFile(filepath.Join(toolkitRoot, ".config", "nvidia-container-runtime", "config.toml")))
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := cfgToml.Config()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure that the config file has the required contents.
|
||||
// TODO: Add checks for additional config options.
|
||||
require.Equal(t, "/host/driver/root", cfg.NVIDIAContainerCLIConfig.Root)
|
||||
require.Equal(t, "@/host/driver/root/sbin/ldconfig", string(cfg.NVIDIAContainerCLIConfig.Ldconfig))
|
||||
require.EqualValues(t, filepath.Join(toolkitRoot, "nvidia-container-cli"), cfg.NVIDIAContainerCLIConfig.Path)
|
||||
require.EqualValues(t, filepath.Join(toolkitRoot, "nvidia-ctk"), cfg.NVIDIACTKConfig.Path)
|
||||
|
||||
if len(tc.expectedCdiSpec) > 0 {
|
||||
cdiSpecFile := filepath.Join(cdiOutputDir, "example.com-class.yaml")
|
||||
require.FileExists(t, cdiSpecFile)
|
||||
info, err := os.Stat(cdiSpecFile)
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, info.Mode()&0004)
|
||||
contents, err := os.ReadFile(cdiSpecFile)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, strings.ReplaceAll(tc.expectedCdiSpec, "{{ .toolkitRoot }}", toolkitRoot), string(contents))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func requireWrappedExecutable(t *testing.T, toolkitRoot string, expectedExecutable string) {
|
||||
requireExecutable(t, toolkitRoot, expectedExecutable)
|
||||
requireExecutable(t, toolkitRoot, expectedExecutable+".real")
|
||||
}
|
||||
|
||||
func requireExecutable(t *testing.T, toolkitRoot string, expectedExecutable string) {
|
||||
executable := filepath.Join(toolkitRoot, expectedExecutable)
|
||||
require.FileExists(t, executable)
|
||||
info, err := os.Lstat(executable)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, info.Mode()&os.ModeSymlink)
|
||||
require.NotZero(t, info.Mode()&0111)
|
||||
}
|
||||
|
||||
func requireSymlink(t *testing.T, toolkitRoot string, expectedLink string, expectedTarget string) {
|
||||
link := filepath.Join(toolkitRoot, expectedLink)
|
||||
require.FileExists(t, link)
|
||||
target, err := symlinks.Resolve(link)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedTarget, target)
|
||||
}
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/runtime"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/toolkit"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/runtime"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk-installer/container/toolkit"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,6 +38,7 @@ type options struct {
|
||||
runtimeArgs string
|
||||
root string
|
||||
pidFile string
|
||||
sourceRoot string
|
||||
|
||||
toolkitOptions toolkit.Options
|
||||
runtimeOptions runtime.Options
|
||||
@@ -51,12 +52,46 @@ func (o options) toolkitRoot() string {
|
||||
var Version = "development"
|
||||
|
||||
func main() {
|
||||
remainingArgs, root, err := ParseArgs(os.Args)
|
||||
logger := logger.New()
|
||||
|
||||
remainingArgs, root, err := ParseArgs(logger, os.Args)
|
||||
if err != nil {
|
||||
log.Errorf("Error: unable to parse arguments: %v", err)
|
||||
logger.Errorf("Error: unable to parse arguments: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := NewApp(logger, root)
|
||||
|
||||
// Run the CLI
|
||||
logger.Infof("Starting %v", c.Name)
|
||||
if err := c.Run(remainingArgs); err != nil {
|
||||
logger.Errorf("error running nvidia-toolkit: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logger.Infof("Completed %v", c.Name)
|
||||
}
|
||||
|
||||
// An app represents the nvidia-ctk-installer.
|
||||
type app struct {
|
||||
logger logger.Interface
|
||||
// defaultRoot stores the root to use if the --root flag is not specified.
|
||||
defaultRoot string
|
||||
|
||||
toolkit *toolkit.Installer
|
||||
}
|
||||
|
||||
// NewApp creates the CLI app fro the specified options.
|
||||
// defaultRoot is used as the root if not specified via the --root flag.
|
||||
func NewApp(logger logger.Interface, defaultRoot string) *cli.App {
|
||||
a := app{
|
||||
logger: logger,
|
||||
defaultRoot: defaultRoot,
|
||||
}
|
||||
return a.build()
|
||||
}
|
||||
|
||||
func (a app) build() *cli.App {
|
||||
options := options{
|
||||
toolkitOptions: toolkit.Options{},
|
||||
}
|
||||
@@ -68,10 +103,10 @@ func main() {
|
||||
c.Description = "DESTINATION points to the host path underneath which the nvidia-container-toolkit should be installed.\nIt will be installed at ${DESTINATION}/toolkit"
|
||||
c.Version = Version
|
||||
c.Before = func(ctx *cli.Context) error {
|
||||
return validateFlags(ctx, &options)
|
||||
return a.Before(ctx, &options)
|
||||
}
|
||||
c.Action = func(ctx *cli.Context) error {
|
||||
return Run(ctx, &options)
|
||||
return a.Run(ctx, &options)
|
||||
}
|
||||
|
||||
// Setup flags for the CLI
|
||||
@@ -102,11 +137,18 @@ func main() {
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "root",
|
||||
Value: root,
|
||||
Value: a.defaultRoot,
|
||||
Usage: "the folder where the NVIDIA Container Toolkit is to be installed. It will be installed to `ROOT`/toolkit",
|
||||
Destination: &options.root,
|
||||
EnvVars: []string{"ROOT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "source-root",
|
||||
Value: "/",
|
||||
Usage: "The folder where the required toolkit artifacts can be found",
|
||||
Destination: &options.sourceRoot,
|
||||
EnvVars: []string{"SOURCE_ROOT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pid-file",
|
||||
Value: defaultPidFile,
|
||||
@@ -119,41 +161,47 @@ func main() {
|
||||
c.Flags = append(c.Flags, toolkit.Flags(&options.toolkitOptions)...)
|
||||
c.Flags = append(c.Flags, runtime.Flags(&options.runtimeOptions)...)
|
||||
|
||||
// Run the CLI
|
||||
log.Infof("Starting %v", c.Name)
|
||||
if err := c.Run(remainingArgs); err != nil {
|
||||
log.Errorf("error running nvidia-toolkit: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.Infof("Completed %v", c.Name)
|
||||
return c
|
||||
}
|
||||
|
||||
func validateFlags(_ *cli.Context, o *options) error {
|
||||
func (a *app) Before(c *cli.Context, o *options) error {
|
||||
a.toolkit = toolkit.NewInstaller(
|
||||
toolkit.WithLogger(a.logger),
|
||||
toolkit.WithSourceRoot(o.sourceRoot),
|
||||
toolkit.WithToolkitRoot(o.toolkitRoot()),
|
||||
)
|
||||
return a.validateFlags(c, o)
|
||||
}
|
||||
|
||||
func (a *app) validateFlags(c *cli.Context, o *options) error {
|
||||
if o.root == "" {
|
||||
return fmt.Errorf("the install root must be specified")
|
||||
}
|
||||
if _, exists := availableRuntimes[o.runtime]; !exists {
|
||||
return fmt.Errorf("unknown runtime: %v", o.runtime)
|
||||
}
|
||||
if filepath.Base(o.pidFile) != toolkitPidFilename {
|
||||
return fmt.Errorf("invalid toolkit.pid path %v", o.pidFile)
|
||||
}
|
||||
if err := toolkit.ValidateOptions(&o.toolkitOptions, o.toolkitRoot()); err != nil {
|
||||
|
||||
if err := a.toolkit.ValidateOptions(&o.toolkitOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runtime.ValidateOptions(&o.runtimeOptions, o.runtime, o.toolkitRoot()); err != nil {
|
||||
if err := runtime.ValidateOptions(c, &o.runtimeOptions, o.runtime, o.toolkitRoot(), &o.toolkitOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run runs the core logic of the CLI
|
||||
func Run(c *cli.Context, o *options) error {
|
||||
err := verifyFlags(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to verify flags: %v", err)
|
||||
}
|
||||
|
||||
err = initialize(o.pidFile)
|
||||
// Run installs the NVIDIA Container Toolkit and updates the requested runtime.
|
||||
// If the application is run as a daemon, the application waits and unconfigures
|
||||
// the runtime on termination.
|
||||
func (a *app) Run(c *cli.Context, o *options) error {
|
||||
err := a.initialize(o.pidFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize: %v", err)
|
||||
}
|
||||
defer shutdown(o.pidFile)
|
||||
defer a.shutdown(o.pidFile)
|
||||
|
||||
if len(o.toolkitOptions.ContainerRuntimeRuntimes.Value()) == 0 {
|
||||
lowlevelRuntimePaths, err := runtime.GetLowlevelRuntimePaths(&o.runtimeOptions, o.runtime)
|
||||
@@ -164,7 +212,8 @@ func Run(c *cli.Context, o *options) error {
|
||||
|
||||
o.toolkitOptions.ContainerRuntimeRuntimes = *cli.NewStringSlice(lowlevelRuntimePaths...)
|
||||
}
|
||||
err = toolkit.Install(c, &o.toolkitOptions, o.toolkitRoot())
|
||||
|
||||
err = a.toolkit.Install(c, &o.toolkitOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to install toolkit: %v", err)
|
||||
}
|
||||
@@ -175,7 +224,7 @@ func Run(c *cli.Context, o *options) error {
|
||||
}
|
||||
|
||||
if !o.noDaemon {
|
||||
err = waitForSignal()
|
||||
err = a.waitForSignal()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to wait for signal: %v", err)
|
||||
}
|
||||
@@ -191,8 +240,8 @@ func Run(c *cli.Context, o *options) error {
|
||||
|
||||
// ParseArgs checks if a single positional argument was defined and extracts this the root.
|
||||
// If no positional arguments are defined, it is assumed that the root is specified as a flag.
|
||||
func ParseArgs(args []string) ([]string, string, error) {
|
||||
log.Infof("Parsing arguments")
|
||||
func ParseArgs(logger logger.Interface, args []string) ([]string, string, error) {
|
||||
logger.Infof("Parsing arguments")
|
||||
|
||||
if len(args) < 2 {
|
||||
return args, "", nil
|
||||
@@ -217,20 +266,8 @@ func ParseArgs(args []string) ([]string, string, error) {
|
||||
return nil, "", fmt.Errorf("unexpected positional argument(s) %v", args[2:lastPositionalArg+1])
|
||||
}
|
||||
|
||||
func verifyFlags(o *options) error {
|
||||
log.Infof("Verifying Flags")
|
||||
if o.root == "" {
|
||||
return fmt.Errorf("the install root must be specified")
|
||||
}
|
||||
|
||||
if _, exists := availableRuntimes[o.runtime]; !exists {
|
||||
return fmt.Errorf("unknown runtime: %v", o.runtime)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initialize(pidFile string) error {
|
||||
log.Infof("Initializing")
|
||||
func (a *app) initialize(pidFile string) error {
|
||||
a.logger.Infof("Initializing")
|
||||
|
||||
if dir := filepath.Dir(pidFile); dir != "" {
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
@@ -246,8 +283,8 @@ func initialize(pidFile string) error {
|
||||
|
||||
err = unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
|
||||
if err != nil {
|
||||
log.Warningf("Unable to get exclusive lock on '%v'", pidFile)
|
||||
log.Warningf("This normally means an instance of the NVIDIA toolkit Container is already running, aborting")
|
||||
a.logger.Warningf("Unable to get exclusive lock on '%v'", pidFile)
|
||||
a.logger.Warningf("This normally means an instance of the NVIDIA toolkit Container is already running, aborting")
|
||||
return fmt.Errorf("unable to get flock on pidfile: %v", err)
|
||||
}
|
||||
|
||||
@@ -264,8 +301,8 @@ func initialize(pidFile string) error {
|
||||
case <-waitingForSignal:
|
||||
signalReceived <- true
|
||||
default:
|
||||
log.Infof("Signal received, exiting early")
|
||||
shutdown(pidFile)
|
||||
a.logger.Infof("Signal received, exiting early")
|
||||
a.shutdown(pidFile)
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
@@ -273,18 +310,18 @@ func initialize(pidFile string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForSignal() error {
|
||||
log.Infof("Waiting for signal")
|
||||
func (a *app) waitForSignal() error {
|
||||
a.logger.Infof("Waiting for signal")
|
||||
waitingForSignal <- true
|
||||
<-signalReceived
|
||||
return nil
|
||||
}
|
||||
|
||||
func shutdown(pidFile string) {
|
||||
log.Infof("Shutting Down")
|
||||
func (a *app) shutdown(pidFile string) {
|
||||
a.logger.Infof("Shutting Down")
|
||||
|
||||
err := os.Remove(pidFile)
|
||||
if err != nil {
|
||||
log.Warningf("Unable to remove pidfile: %v", err)
|
||||
a.logger.Warningf("Unable to remove pidfile: %v", err)
|
||||
}
|
||||
}
|
||||
516
cmd/nvidia-ctk-installer/main_test.go
Normal file
516
cmd/nvidia-ctk-installer/main_test.go
Normal file
@@ -0,0 +1,516 @@
|
||||
/**
|
||||
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestParseArgs(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
args []string
|
||||
expectedRemaining []string
|
||||
expectedRoot string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
args: []string{},
|
||||
expectedRemaining: []string{},
|
||||
expectedRoot: "",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
args: []string{"app"},
|
||||
expectedRemaining: []string{"app"},
|
||||
},
|
||||
{
|
||||
args: []string{"app", "root"},
|
||||
expectedRemaining: []string{"app"},
|
||||
expectedRoot: "root",
|
||||
},
|
||||
{
|
||||
args: []string{"app", "--flag"},
|
||||
expectedRemaining: []string{"app", "--flag"},
|
||||
},
|
||||
{
|
||||
args: []string{"app", "root", "--flag"},
|
||||
expectedRemaining: []string{"app", "--flag"},
|
||||
expectedRoot: "root",
|
||||
},
|
||||
{
|
||||
args: []string{"app", "root", "not-root", "--flag"},
|
||||
expectedError: fmt.Errorf("unexpected positional argument(s) [not-root]"),
|
||||
},
|
||||
{
|
||||
args: []string{"app", "root", "not-root"},
|
||||
expectedError: fmt.Errorf("unexpected positional argument(s) [not-root]"),
|
||||
},
|
||||
{
|
||||
args: []string{"app", "root", "not-root", "also"},
|
||||
expectedError: fmt.Errorf("unexpected positional argument(s) [not-root also]"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
remaining, root, err := ParseArgs(logger, tc.args)
|
||||
if tc.expectedError != nil {
|
||||
require.EqualError(t, err, tc.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, tc.expectedRemaining, remaining)
|
||||
require.Equal(t, tc.expectedRoot, root)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp(t *testing.T) {
|
||||
t.Setenv("__NVCT_TESTING_DEVICES_ARE_FILES", "true")
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
artifactRoot := filepath.Join(moduleRoot, "testdata", "installer", "artifacts")
|
||||
hostRoot := filepath.Join(moduleRoot, "testdata", "lookup", "rootfs-1")
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
args []string
|
||||
expectedToolkitConfig string
|
||||
expectedRuntimeConfig string
|
||||
}{
|
||||
{
|
||||
description: "no args",
|
||||
expectedToolkitConfig: `accept-nvidia-visible-devices-as-volume-mounts = false
|
||||
accept-nvidia-visible-devices-envvar-when-unprivileged = true
|
||||
disable-require = false
|
||||
supported-driver-capabilities = "compat32,compute,display,graphics,ngx,utility,video"
|
||||
swarm-resource = ""
|
||||
|
||||
[nvidia-container-cli]
|
||||
debug = ""
|
||||
environment = []
|
||||
ldcache = ""
|
||||
ldconfig = "@/run/nvidia/driver/sbin/ldconfig"
|
||||
load-kmods = true
|
||||
no-cgroups = false
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-cli"
|
||||
root = "/run/nvidia/driver"
|
||||
user = ""
|
||||
|
||||
[nvidia-container-runtime]
|
||||
debug = "/dev/null"
|
||||
log-level = "info"
|
||||
mode = "auto"
|
||||
runtimes = ["docker-runc", "runc", "crun"]
|
||||
|
||||
[nvidia-container-runtime.modes]
|
||||
|
||||
[nvidia-container-runtime.modes.cdi]
|
||||
annotation-prefixes = ["cdi.k8s.io/"]
|
||||
default-kind = "nvidia.com/gpu"
|
||||
spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
|
||||
[nvidia-ctk]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-ctk"
|
||||
`,
|
||||
expectedRuntimeConfig: `{
|
||||
"default-runtime": "nvidia",
|
||||
"runtimes": {
|
||||
"nvidia": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime"
|
||||
},
|
||||
"nvidia-cdi": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.cdi"
|
||||
},
|
||||
"nvidia-legacy": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.legacy"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "CDI enabled enables CDI in docker",
|
||||
args: []string{"--cdi-enabled", "--create-device-nodes=none"},
|
||||
expectedToolkitConfig: `accept-nvidia-visible-devices-as-volume-mounts = false
|
||||
accept-nvidia-visible-devices-envvar-when-unprivileged = true
|
||||
disable-require = false
|
||||
supported-driver-capabilities = "compat32,compute,display,graphics,ngx,utility,video"
|
||||
swarm-resource = ""
|
||||
|
||||
[nvidia-container-cli]
|
||||
debug = ""
|
||||
environment = []
|
||||
ldcache = ""
|
||||
ldconfig = "@/run/nvidia/driver/sbin/ldconfig"
|
||||
load-kmods = true
|
||||
no-cgroups = false
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-cli"
|
||||
root = "/run/nvidia/driver"
|
||||
user = ""
|
||||
|
||||
[nvidia-container-runtime]
|
||||
debug = "/dev/null"
|
||||
log-level = "info"
|
||||
mode = "auto"
|
||||
runtimes = ["docker-runc", "runc", "crun"]
|
||||
|
||||
[nvidia-container-runtime.modes]
|
||||
|
||||
[nvidia-container-runtime.modes.cdi]
|
||||
annotation-prefixes = ["cdi.k8s.io/"]
|
||||
default-kind = "nvidia.com/gpu"
|
||||
spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
|
||||
[nvidia-ctk]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-ctk"
|
||||
`,
|
||||
expectedRuntimeConfig: `{
|
||||
"default-runtime": "nvidia",
|
||||
"features": {
|
||||
"cdi": true
|
||||
},
|
||||
"runtimes": {
|
||||
"nvidia": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime"
|
||||
},
|
||||
"nvidia-cdi": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.cdi"
|
||||
},
|
||||
"nvidia-legacy": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.legacy"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "--enable-cdi-in-runtime=false overrides --cdi-enabled in Docker",
|
||||
args: []string{"--cdi-enabled", "--create-device-nodes=none", "--enable-cdi-in-runtime=false"},
|
||||
expectedToolkitConfig: `accept-nvidia-visible-devices-as-volume-mounts = false
|
||||
accept-nvidia-visible-devices-envvar-when-unprivileged = true
|
||||
disable-require = false
|
||||
supported-driver-capabilities = "compat32,compute,display,graphics,ngx,utility,video"
|
||||
swarm-resource = ""
|
||||
|
||||
[nvidia-container-cli]
|
||||
debug = ""
|
||||
environment = []
|
||||
ldcache = ""
|
||||
ldconfig = "@/run/nvidia/driver/sbin/ldconfig"
|
||||
load-kmods = true
|
||||
no-cgroups = false
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-cli"
|
||||
root = "/run/nvidia/driver"
|
||||
user = ""
|
||||
|
||||
[nvidia-container-runtime]
|
||||
debug = "/dev/null"
|
||||
log-level = "info"
|
||||
mode = "auto"
|
||||
runtimes = ["docker-runc", "runc", "crun"]
|
||||
|
||||
[nvidia-container-runtime.modes]
|
||||
|
||||
[nvidia-container-runtime.modes.cdi]
|
||||
annotation-prefixes = ["cdi.k8s.io/"]
|
||||
default-kind = "nvidia.com/gpu"
|
||||
spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
|
||||
[nvidia-ctk]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-ctk"
|
||||
`,
|
||||
expectedRuntimeConfig: `{
|
||||
"default-runtime": "nvidia",
|
||||
"runtimes": {
|
||||
"nvidia": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime"
|
||||
},
|
||||
"nvidia-cdi": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.cdi"
|
||||
},
|
||||
"nvidia-legacy": {
|
||||
"args": [],
|
||||
"path": "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.legacy"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "CDI enabled enables CDI in containerd",
|
||||
args: []string{"--cdi-enabled", "--runtime=containerd"},
|
||||
expectedToolkitConfig: `accept-nvidia-visible-devices-as-volume-mounts = false
|
||||
accept-nvidia-visible-devices-envvar-when-unprivileged = true
|
||||
disable-require = false
|
||||
supported-driver-capabilities = "compat32,compute,display,graphics,ngx,utility,video"
|
||||
swarm-resource = ""
|
||||
|
||||
[nvidia-container-cli]
|
||||
debug = ""
|
||||
environment = []
|
||||
ldcache = ""
|
||||
ldconfig = "@/run/nvidia/driver/sbin/ldconfig"
|
||||
load-kmods = true
|
||||
no-cgroups = false
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-cli"
|
||||
root = "/run/nvidia/driver"
|
||||
user = ""
|
||||
|
||||
[nvidia-container-runtime]
|
||||
debug = "/dev/null"
|
||||
log-level = "info"
|
||||
mode = "auto"
|
||||
runtimes = ["docker-runc", "runc", "crun"]
|
||||
|
||||
[nvidia-container-runtime.modes]
|
||||
|
||||
[nvidia-container-runtime.modes.cdi]
|
||||
annotation-prefixes = ["cdi.k8s.io/"]
|
||||
default-kind = "nvidia.com/gpu"
|
||||
spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
|
||||
[nvidia-ctk]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-ctk"
|
||||
`,
|
||||
expectedRuntimeConfig: `version = 2
|
||||
|
||||
[plugins]
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
enable_cdi = true
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
default_runtime_name = "nvidia"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
|
||||
BinaryName = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-cdi]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-cdi.options]
|
||||
BinaryName = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.cdi"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-legacy]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-legacy.options]
|
||||
BinaryName = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.legacy"
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "--enable-cdi-in-runtime=false overrides --cdi-enabled in containerd",
|
||||
args: []string{"--cdi-enabled", "--create-device-nodes=none", "--enable-cdi-in-runtime=false", "--runtime=containerd"},
|
||||
expectedToolkitConfig: `accept-nvidia-visible-devices-as-volume-mounts = false
|
||||
accept-nvidia-visible-devices-envvar-when-unprivileged = true
|
||||
disable-require = false
|
||||
supported-driver-capabilities = "compat32,compute,display,graphics,ngx,utility,video"
|
||||
swarm-resource = ""
|
||||
|
||||
[nvidia-container-cli]
|
||||
debug = ""
|
||||
environment = []
|
||||
ldcache = ""
|
||||
ldconfig = "@/run/nvidia/driver/sbin/ldconfig"
|
||||
load-kmods = true
|
||||
no-cgroups = false
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-cli"
|
||||
root = "/run/nvidia/driver"
|
||||
user = ""
|
||||
|
||||
[nvidia-container-runtime]
|
||||
debug = "/dev/null"
|
||||
log-level = "info"
|
||||
mode = "auto"
|
||||
runtimes = ["docker-runc", "runc", "crun"]
|
||||
|
||||
[nvidia-container-runtime.modes]
|
||||
|
||||
[nvidia-container-runtime.modes.cdi]
|
||||
annotation-prefixes = ["cdi.k8s.io/"]
|
||||
default-kind = "nvidia.com/gpu"
|
||||
spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime-hook"
|
||||
skip-mode-detection = true
|
||||
|
||||
[nvidia-ctk]
|
||||
path = "{{ .toolkitRoot }}/toolkit/nvidia-ctk"
|
||||
`,
|
||||
expectedRuntimeConfig: `version = 2
|
||||
|
||||
[plugins]
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd]
|
||||
default_runtime_name = "nvidia"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
|
||||
BinaryName = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-cdi]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-cdi.options]
|
||||
BinaryName = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.cdi"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-legacy]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-legacy.options]
|
||||
BinaryName = "{{ .toolkitRoot }}/toolkit/nvidia-container-runtime.legacy"
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
testRoot := t.TempDir()
|
||||
|
||||
cdiOutputDir := filepath.Join(testRoot, "/var/run/cdi")
|
||||
runtimeConfigFile := filepath.Join(testRoot, "config.file")
|
||||
|
||||
toolkitRoot := filepath.Join(testRoot, "toolkit-test")
|
||||
toolkitConfigFile := filepath.Join(toolkitRoot, "toolkit/.config/nvidia-container-runtime/config.toml")
|
||||
|
||||
app := NewApp(logger, toolkitRoot)
|
||||
|
||||
testArgs := []string{
|
||||
"nvidia-ctk-installer",
|
||||
"--no-daemon",
|
||||
"--cdi-output-dir=" + cdiOutputDir,
|
||||
"--config=" + runtimeConfigFile,
|
||||
"--create-device-nodes=none",
|
||||
"--driver-root-ctr-path=" + hostRoot,
|
||||
"--pid-file=" + filepath.Join(testRoot, "toolkit.pid"),
|
||||
"--restart-mode=none",
|
||||
"--source-root=" + filepath.Join(artifactRoot, "deb"),
|
||||
}
|
||||
|
||||
err := app.Run(append(testArgs, tc.args...))
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.FileExists(t, toolkitConfigFile)
|
||||
toolkitConfigFileContents, err := os.ReadFile(toolkitConfigFile)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, strings.ReplaceAll(tc.expectedToolkitConfig, "{{ .toolkitRoot }}", toolkitRoot), string(toolkitConfigFileContents))
|
||||
|
||||
require.FileExists(t, runtimeConfigFile)
|
||||
runtimeConfigFileContents, err := os.ReadFile(runtimeConfigFile)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, strings.ReplaceAll(tc.expectedRuntimeConfig, "{{ .toolkitRoot }}", toolkitRoot), string(runtimeConfigFileContents))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
cdi "tags.cncf.io/container-device-interface/pkg/parser"
|
||||
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
|
||||
@@ -60,6 +62,9 @@ type options struct {
|
||||
files cli.StringSlice
|
||||
ignorePatterns cli.StringSlice
|
||||
}
|
||||
|
||||
// the following are used for dependency injection during spec generation.
|
||||
nvmllib nvml.Interface
|
||||
}
|
||||
|
||||
// NewCommand constructs a generate-cdi command with the specified logger
|
||||
@@ -104,10 +109,12 @@ func (m command) build() *cli.Command {
|
||||
Destination: &opts.format,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "mode",
|
||||
Aliases: []string{"discovery-mode"},
|
||||
Usage: "The mode to use when discovering the available entities. One of [auto | nvml | wsl]. If mode is set to 'auto' the mode will be determined based on the system configuration.",
|
||||
Value: nvcdi.ModeAuto,
|
||||
Name: "mode",
|
||||
Aliases: []string{"discovery-mode"},
|
||||
Usage: "The mode to use when discovering the available entities. " +
|
||||
"One of [" + strings.Join(nvcdi.AllModes[string](), " | ") + "]. " +
|
||||
"If mode is set to 'auto' the mode will be determined based on the system configuration.",
|
||||
Value: string(nvcdi.ModeAuto),
|
||||
Destination: &opts.mode,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
@@ -184,13 +191,7 @@ func (m command) validateFlags(c *cli.Context, opts *options) error {
|
||||
}
|
||||
|
||||
opts.mode = strings.ToLower(opts.mode)
|
||||
switch opts.mode {
|
||||
case nvcdi.ModeAuto:
|
||||
case nvcdi.ModeCSV:
|
||||
case nvcdi.ModeNvml:
|
||||
case nvcdi.ModeWsl:
|
||||
case nvcdi.ModeManagement:
|
||||
default:
|
||||
if !nvcdi.IsValidMode(opts.mode) {
|
||||
return fmt.Errorf("invalid discovery mode: %v", opts.mode)
|
||||
}
|
||||
|
||||
@@ -273,6 +274,8 @@ func (m command) generateSpec(opts *options) (spec.Interface, error) {
|
||||
nvcdi.WithLibrarySearchPaths(opts.librarySearchPaths.Value()),
|
||||
nvcdi.WithCSVFiles(opts.csv.files.Value()),
|
||||
nvcdi.WithCSVIgnorePatterns(opts.csv.ignorePatterns.Value()),
|
||||
// We set the following to allow for dependency injection:
|
||||
nvcdi.WithNvmlLib(opts.nvmllib),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create CDI library: %v", err)
|
||||
|
||||
157
cmd/nvidia-ctk/cdi/generate/generate_test.go
Normal file
157
cmd/nvidia-ctk/cdi/generate/generate_test.go
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
**/
|
||||
|
||||
package generate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||
"github.com/NVIDIA/go-nvml/pkg/nvml/mock/dgxa100"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
func TestGenerateSpec(t *testing.T) {
|
||||
t.Setenv("__NVCT_TESTING_DEVICES_ARE_FILES", "true")
|
||||
moduleRoot, err := test.GetModuleRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
driverRoot := filepath.Join(moduleRoot, "testdata", "lookup", "rootfs-1")
|
||||
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
testCases := []struct {
|
||||
description string
|
||||
options options
|
||||
expectedValidateError error
|
||||
expectedOptions options
|
||||
expectedError error
|
||||
expectedSpec string
|
||||
}{
|
||||
{
|
||||
description: "default",
|
||||
options: options{
|
||||
format: "yaml",
|
||||
mode: "nvml",
|
||||
vendor: "example.com",
|
||||
class: "device",
|
||||
driverRoot: driverRoot,
|
||||
},
|
||||
expectedOptions: options{
|
||||
format: "yaml",
|
||||
mode: "nvml",
|
||||
vendor: "example.com",
|
||||
class: "device",
|
||||
nvidiaCDIHookPath: "/usr/bin/nvidia-cdi-hook",
|
||||
driverRoot: driverRoot,
|
||||
},
|
||||
expectedSpec: `---
|
||||
cdiVersion: 0.5.0
|
||||
containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .driverRoot }}/dev/nvidiactl
|
||||
path: /dev/nvidiactl
|
||||
env:
|
||||
- NVIDIA_VISIBLE_DEVICES=void
|
||||
hooks:
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- create-symlinks
|
||||
- --link
|
||||
- libcuda.so.1::/lib/x86_64-linux-gnu/libcuda.so
|
||||
hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- enable-cuda-compat
|
||||
- --host-driver-version=999.88.77
|
||||
hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
- args:
|
||||
- nvidia-cdi-hook
|
||||
- update-ldcache
|
||||
- --folder
|
||||
- /lib/x86_64-linux-gnu
|
||||
hookName: createContainer
|
||||
path: /usr/bin/nvidia-cdi-hook
|
||||
mounts:
|
||||
- containerPath: /lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
hostPath: {{ .driverRoot }}/lib/x86_64-linux-gnu/libcuda.so.999.88.77
|
||||
options:
|
||||
- ro
|
||||
- nosuid
|
||||
- nodev
|
||||
- bind
|
||||
devices:
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||
path: /dev/nvidia0
|
||||
name: "0"
|
||||
- containerEdits:
|
||||
deviceNodes:
|
||||
- hostPath: {{ .driverRoot }}/dev/nvidia0
|
||||
path: /dev/nvidia0
|
||||
name: all
|
||||
kind: example.com/device
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
c := command{
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
err := c.validateFlags(nil, &tc.options)
|
||||
require.ErrorIs(t, err, tc.expectedValidateError)
|
||||
require.EqualValues(t, tc.expectedOptions, tc.options)
|
||||
|
||||
// Set up a mock server, reusing the DGX A100 mock.
|
||||
server := dgxa100.New()
|
||||
// Override the driver version to match the version in our mock filesystem.
|
||||
server.SystemGetDriverVersionFunc = func() (string, nvml.Return) {
|
||||
return "999.88.77", nvml.SUCCESS
|
||||
}
|
||||
// Set the device count to 1 explicitly since we only have a single device node.
|
||||
server.DeviceGetCountFunc = func() (int, nvml.Return) {
|
||||
return 1, nvml.SUCCESS
|
||||
}
|
||||
for _, d := range server.Devices {
|
||||
// TODO: This is not implemented in the mock.
|
||||
(d.(*dgxa100.Device)).GetMaxMigDeviceCountFunc = func() (int, nvml.Return) {
|
||||
return 0, nvml.SUCCESS
|
||||
}
|
||||
}
|
||||
tc.options.nvmllib = server
|
||||
|
||||
spec, err := c.generateSpec(&tc.options)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = spec.WriteTo(&buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, strings.ReplaceAll(tc.expectedSpec, "{{ .driverRoot }}", driverRoot), buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func (m command) build() *cli.Command {
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "cdi.enabled",
|
||||
Aliases: []string{"cdi.enable"},
|
||||
Aliases: []string{"cdi.enable", "enable-cdi"},
|
||||
Usage: "Enable CDI in the configured runtime",
|
||||
Destination: &config.cdi.enabled,
|
||||
},
|
||||
@@ -225,6 +225,17 @@ func (m command) validateFlags(c *cli.Context, config *config) error {
|
||||
return fmt.Errorf("unrecognized Config Source: %v", config.configSource)
|
||||
}
|
||||
|
||||
if config.configFilePath == "" {
|
||||
switch config.runtime {
|
||||
case "containerd":
|
||||
config.configFilePath = defaultContainerdConfigFilePath
|
||||
case "crio":
|
||||
config.configFilePath = defaultCrioConfigFilePath
|
||||
case "docker":
|
||||
config.configFilePath = defaultDockerConfigFilePath
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -241,9 +252,6 @@ func (m command) configureWrapper(c *cli.Context, config *config) error {
|
||||
|
||||
// configureConfigFile updates the specified container engine config file to enable the NVIDIA runtime.
|
||||
func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
configFilePath := config.resolveConfigFilePath()
|
||||
|
||||
var err error
|
||||
configSource, err := config.resolveConfigSource()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -254,19 +262,19 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
case "containerd":
|
||||
cfg, err = containerd.New(
|
||||
containerd.WithLogger(m.logger),
|
||||
containerd.WithPath(configFilePath),
|
||||
containerd.WithPath(config.configFilePath),
|
||||
containerd.WithConfigSource(configSource),
|
||||
)
|
||||
case "crio":
|
||||
cfg, err = crio.New(
|
||||
crio.WithLogger(m.logger),
|
||||
crio.WithPath(configFilePath),
|
||||
crio.WithPath(config.configFilePath),
|
||||
crio.WithConfigSource(configSource),
|
||||
)
|
||||
case "docker":
|
||||
cfg, err = docker.New(
|
||||
docker.WithLogger(m.logger),
|
||||
docker.WithPath(configFilePath),
|
||||
docker.WithPath(config.configFilePath),
|
||||
)
|
||||
default:
|
||||
err = fmt.Errorf("unrecognized runtime '%v'", config.runtime)
|
||||
@@ -284,9 +292,8 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
return fmt.Errorf("unable to update config: %v", err)
|
||||
}
|
||||
|
||||
err = enableCDI(config, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable CDI in %s: %w", config.runtime, err)
|
||||
if config.cdi.enabled {
|
||||
cfg.EnableCDI()
|
||||
}
|
||||
|
||||
outputPath := config.getOutputConfigPath()
|
||||
@@ -307,22 +314,6 @@ func (m command) configureConfigFile(c *cli.Context, config *config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveConfigFilePath returns the default config file path for the configured container engine
|
||||
func (c *config) resolveConfigFilePath() string {
|
||||
if c.configFilePath != "" {
|
||||
return c.configFilePath
|
||||
}
|
||||
switch c.runtime {
|
||||
case "containerd":
|
||||
return defaultContainerdConfigFilePath
|
||||
case "crio":
|
||||
return defaultCrioConfigFilePath
|
||||
case "docker":
|
||||
return defaultDockerConfigFilePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// resolveConfigSource returns the default config source or the user provided config source
|
||||
func (c *config) resolveConfigSource() (toml.Loader, error) {
|
||||
switch c.configSource {
|
||||
@@ -351,7 +342,7 @@ func (c *config) getOutputConfigPath() string {
|
||||
if c.dryRun {
|
||||
return ""
|
||||
}
|
||||
return c.resolveConfigFilePath()
|
||||
return c.configFilePath
|
||||
}
|
||||
|
||||
// configureOCIHook creates and configures the OCI hook for the NVIDIA runtime
|
||||
@@ -362,19 +353,3 @@ func (m *command) configureOCIHook(c *cli.Context, config *config) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// enableCDI enables the use of CDI in the corresponding container engine
|
||||
func enableCDI(config *config, cfg engine.Interface) error {
|
||||
if !config.cdi.enabled {
|
||||
return nil
|
||||
}
|
||||
switch config.runtime {
|
||||
case "containerd":
|
||||
cfg.Set("enable_cdi", true)
|
||||
case "docker":
|
||||
cfg.Set("features", map[string]bool{"cdi": true})
|
||||
default:
|
||||
return fmt.Errorf("enabling CDI in %s is not supported", config.runtime)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,12 +19,8 @@ package devchar
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
@@ -44,7 +40,6 @@ type config struct {
|
||||
devCharPath string
|
||||
driverRoot string
|
||||
dryRun bool
|
||||
watch bool
|
||||
createAll bool
|
||||
createDeviceNodes bool
|
||||
loadKernelModules bool
|
||||
@@ -89,13 +84,6 @@ func (m command) build() *cli.Command {
|
||||
Destination: &cfg.driverRoot,
|
||||
EnvVars: []string{"NVIDIA_DRIVER_ROOT", "DRIVER_ROOT"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "watch",
|
||||
Usage: "If set, the command will watch for changes to the driver root and recreate the symlinks when changes are detected.",
|
||||
Value: false,
|
||||
Destination: &cfg.watch,
|
||||
EnvVars: []string{"WATCH"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "create-all",
|
||||
Usage: "Create all possible /dev/char symlinks instead of limiting these to existing device nodes.",
|
||||
@@ -127,7 +115,7 @@ func (m command) build() *cli.Command {
|
||||
}
|
||||
|
||||
func (m command) validateFlags(r *cli.Context, cfg *config) error {
|
||||
if cfg.createAll && cfg.watch {
|
||||
if cfg.createAll {
|
||||
return fmt.Errorf("create-all and watch are mutually exclusive")
|
||||
}
|
||||
|
||||
@@ -145,19 +133,6 @@ func (m command) validateFlags(r *cli.Context, cfg *config) error {
|
||||
}
|
||||
|
||||
func (m command) run(c *cli.Context, cfg *config) error {
|
||||
var watcher *fsnotify.Watcher
|
||||
var sigs chan os.Signal
|
||||
|
||||
if cfg.watch {
|
||||
watcher, err := newFSWatcher(filepath.Join(cfg.driverRoot, "dev"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create FS watcher: %v", err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
sigs = newOSWatcher(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
}
|
||||
|
||||
l, err := NewSymlinkCreator(
|
||||
WithLogger(m.logger),
|
||||
WithDevCharPath(cfg.devCharPath),
|
||||
@@ -171,47 +146,11 @@ func (m command) run(c *cli.Context, cfg *config) error {
|
||||
return fmt.Errorf("failed to create symlink creator: %v", err)
|
||||
}
|
||||
|
||||
create:
|
||||
err = l.CreateLinks()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create links: %v", err)
|
||||
}
|
||||
if !cfg.watch {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
select {
|
||||
|
||||
case event := <-watcher.Events:
|
||||
deviceNode := filepath.Base(event.Name)
|
||||
if !strings.HasPrefix(deviceNode, "nvidia") {
|
||||
continue
|
||||
}
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
m.logger.Infof("%s created, restarting.", event.Name)
|
||||
goto create
|
||||
}
|
||||
if event.Op&fsnotify.Create == fsnotify.Remove {
|
||||
m.logger.Infof("%s removed. Ignoring", event.Name)
|
||||
|
||||
}
|
||||
|
||||
// Watch for any other fs errors and log them.
|
||||
case err := <-watcher.Errors:
|
||||
m.logger.Errorf("inotify: %s", err)
|
||||
|
||||
// React to signals
|
||||
case s := <-sigs:
|
||||
switch s {
|
||||
case syscall.SIGHUP:
|
||||
m.logger.Infof("Received SIGHUP, recreating symlinks.")
|
||||
goto create
|
||||
default:
|
||||
m.logger.Infof("Received signal %q, shutting down.", s)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type linkCreator struct {
|
||||
@@ -399,27 +338,3 @@ type deviceNode struct {
|
||||
func (d deviceNode) devCharName() string {
|
||||
return fmt.Sprintf("%d:%d", d.major, d.minor)
|
||||
}
|
||||
|
||||
func newFSWatcher(files ...string) (*fsnotify.Watcher, error) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
err = watcher.Add(f)
|
||||
if err != nil {
|
||||
watcher.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return watcher, nil
|
||||
}
|
||||
|
||||
func newOSWatcher(sigs ...os.Signal) chan os.Signal {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, sigs...)
|
||||
|
||||
return sigChan
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
|
||||
FROM nvidia/cuda:12.6.2-base-ubuntu20.04
|
||||
FROM nvcr.io/nvidia/cuda:12.8.0-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.6.2-base-ubi8 as build
|
||||
FROM nvcr.io/nvidia/cuda:12.8.0-base-ubi8 AS build
|
||||
|
||||
RUN yum install -y \
|
||||
wget make git gcc \
|
||||
@@ -36,19 +36,18 @@ RUN set -eux; \
|
||||
| tar -C /usr/local -xz
|
||||
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH=/go
|
||||
ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
|
||||
# NOTE: Until the config utilities are properly integrated into the
|
||||
# nvidia-container-toolkit repository, these are built from the `tools` folder
|
||||
# and not `cmd`.
|
||||
RUN GOPATH=/artifacts go install -ldflags="-s -w -X 'main.Version=${VERSION}'" ./tools/...
|
||||
RUN mkdir /artifacts
|
||||
ARG VERSION="N/A"
|
||||
ARG GIT_COMMIT="unknown"
|
||||
RUN make PREFIX=/artifacts cmd-nvidia-ctk-installer
|
||||
|
||||
|
||||
FROM nvidia/cuda:12.6.2-base-ubi8
|
||||
FROM nvcr.io/nvidia/cuda:12.8.0-base-ubi8
|
||||
|
||||
ENV NVIDIA_DISABLE_REQUIRE="true"
|
||||
ENV NVIDIA_VISIBLE_DEVICES=void
|
||||
@@ -62,7 +61,8 @@ WORKDIR /artifacts/packages
|
||||
|
||||
ARG PACKAGE_VERSION
|
||||
ARG TARGETARCH
|
||||
ENV PACKAGE_ARCH ${TARGETARCH}
|
||||
ENV PACKAGE_ARCH=${TARGETARCH}
|
||||
|
||||
RUN PACKAGE_ARCH=${PACKAGE_ARCH/amd64/x86_64} && PACKAGE_ARCH=${PACKAGE_ARCH/arm64/aarch64} && \
|
||||
yum localinstall -y \
|
||||
${PACKAGE_DIST}/${PACKAGE_ARCH}/libnvidia-container1-1.*.rpm \
|
||||
@@ -71,10 +71,12 @@ RUN PACKAGE_ARCH=${PACKAGE_ARCH/amd64/x86_64} && PACKAGE_ARCH=${PACKAGE_ARCH/arm
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
COPY --from=build /artifacts/bin /work
|
||||
COPY --from=build /artifacts/nvidia-ctk-installer /work/nvidia-ctk-installer
|
||||
RUN ln -s nvidia-ctk-installer nvidia-toolkit
|
||||
|
||||
ENV PATH=/work:$PATH
|
||||
|
||||
ARG VERSION
|
||||
LABEL io.k8s.display-name="NVIDIA Container Runtime Config"
|
||||
LABEL name="NVIDIA Container Runtime Config"
|
||||
LABEL vendor="NVIDIA"
|
||||
@@ -85,4 +87,4 @@ LABEL description="See summary"
|
||||
|
||||
RUN mkdir /licenses && mv /NGC-DL-CONTAINER-LICENSE /licenses/NGC-DL-CONTAINER-LICENSE
|
||||
|
||||
ENTRYPOINT ["/work/nvidia-toolkit"]
|
||||
ENTRYPOINT ["/work/nvidia-ctk-installer"]
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
ARG GOLANG_VERSION=x.x.x
|
||||
ARG VERSION="N/A"
|
||||
|
||||
FROM nvidia/cuda:12.6.2-base-ubuntu20.04 as build
|
||||
FROM nvcr.io/nvidia/cuda:12.8.0-base-ubuntu20.04 AS build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget make git gcc \
|
||||
@@ -35,19 +35,18 @@ RUN set -eux; \
|
||||
wget -nv -O - https://storage.googleapis.com/golang/go${GOLANG_VERSION}.linux-${ARCH}.tar.gz \
|
||||
| tar -C /usr/local -xz
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH=/go
|
||||
ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
|
||||
# NOTE: Until the config utilities are properly integrated into the
|
||||
# nvidia-container-toolkit repository, these are built from the `tools` folder
|
||||
# and not `cmd`.
|
||||
RUN GOPATH=/artifacts go install -ldflags="-s -w -X 'main.Version=${VERSION}'" ./tools/...
|
||||
RUN mkdir /artifacts
|
||||
ARG VERSION="N/A"
|
||||
ARG GIT_COMMIT="unknown"
|
||||
RUN make PREFIX=/artifacts cmd-nvidia-ctk-installer
|
||||
|
||||
|
||||
FROM nvcr.io/nvidia/cuda:12.6.2-base-ubuntu20.04
|
||||
FROM nvcr.io/nvidia/cuda:12.8.0-base-ubuntu20.04
|
||||
|
||||
# Remove the CUDA repository configurations to avoid issues with rotated GPG keys
|
||||
RUN rm -f /etc/apt/sources.list.d/cuda.list
|
||||
@@ -71,7 +70,7 @@ WORKDIR /artifacts/packages
|
||||
|
||||
ARG PACKAGE_VERSION
|
||||
ARG TARGETARCH
|
||||
ENV PACKAGE_ARCH ${TARGETARCH}
|
||||
ENV PACKAGE_ARCH=${TARGETARCH}
|
||||
|
||||
RUN dpkg -i \
|
||||
${PACKAGE_DIST}/${PACKAGE_ARCH}/libnvidia-container1_1.*.deb \
|
||||
@@ -80,10 +79,12 @@ RUN dpkg -i \
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
COPY --from=build /artifacts/bin /work/
|
||||
COPY --from=build /artifacts/nvidia-ctk-installer /work/nvidia-ctk-installer
|
||||
RUN ln -s nvidia-ctk-installer nvidia-toolkit
|
||||
|
||||
ENV PATH=/work:$PATH
|
||||
|
||||
ARG VERSION
|
||||
LABEL io.k8s.display-name="NVIDIA Container Runtime Config"
|
||||
LABEL name="NVIDIA Container Runtime Config"
|
||||
LABEL vendor="NVIDIA"
|
||||
@@ -94,4 +95,4 @@ LABEL description="See summary"
|
||||
|
||||
RUN mkdir /licenses && mv /NGC-DL-CONTAINER-LICENSE /licenses/NGC-DL-CONTAINER-LICENSE
|
||||
|
||||
ENTRYPOINT ["/work/nvidia-toolkit"]
|
||||
ENTRYPOINT ["/work/nvidia-ctk-installer"]
|
||||
|
||||
@@ -27,12 +27,6 @@ DIST_DIR ?= $(CURDIR)/dist
|
||||
##### Global variables #####
|
||||
include $(CURDIR)/versions.mk
|
||||
|
||||
ifeq ($(IMAGE_NAME),)
|
||||
REGISTRY ?= nvidia
|
||||
IMAGE_NAME := $(REGISTRY)/container-toolkit
|
||||
endif
|
||||
|
||||
VERSION ?= $(LIB_VERSION)$(if $(LIB_TAG),-$(LIB_TAG))
|
||||
IMAGE_VERSION := $(VERSION)
|
||||
|
||||
IMAGE_TAG ?= $(VERSION)-$(DIST)
|
||||
@@ -49,6 +43,7 @@ DISTRIBUTIONS := ubuntu20.04 ubi8
|
||||
|
||||
META_TARGETS := packaging
|
||||
|
||||
IMAGE_TARGETS := $(patsubst %,image-%,$(DISTRIBUTIONS) $(META_TARGETS))
|
||||
BUILD_TARGETS := $(patsubst %,build-%,$(DISTRIBUTIONS) $(META_TARGETS))
|
||||
PUSH_TARGETS := $(patsubst %,push-%,$(DISTRIBUTIONS) $(META_TARGETS))
|
||||
TEST_TARGETS := $(patsubst %,test-%,$(DISTRIBUTIONS))
|
||||
@@ -89,7 +84,7 @@ build-%: DOCKERFILE = $(CURDIR)/deployments/container/Dockerfile.$(DOCKERFILE_SU
|
||||
ARTIFACTS_ROOT ?= $(shell realpath --relative-to=$(CURDIR) $(DIST_DIR))
|
||||
|
||||
# Use a generic build target to build the relevant images
|
||||
$(BUILD_TARGETS): build-%: $(ARTIFACTS_ROOT)
|
||||
$(IMAGE_TARGETS): image-%: $(ARTIFACTS_ROOT)
|
||||
DOCKER_BUILDKIT=1 \
|
||||
$(DOCKER) $(BUILDX) build --pull \
|
||||
--provenance=false --sbom=false \
|
||||
@@ -108,7 +103,6 @@ $(BUILD_TARGETS): build-%: $(ARTIFACTS_ROOT)
|
||||
-f $(DOCKERFILE) \
|
||||
$(CURDIR)
|
||||
|
||||
|
||||
build-ubuntu%: DOCKERFILE_SUFFIX := ubuntu
|
||||
build-ubuntu%: PACKAGE_DIST = ubuntu18.04
|
||||
|
||||
@@ -122,7 +116,13 @@ build-packaging: PACKAGE_DIST = all
|
||||
# Test targets
|
||||
test-%: DIST = $(*)
|
||||
|
||||
TEST_CASES ?= toolkit docker crio containerd
|
||||
# Handle the default build target.
|
||||
.PHONY: build
|
||||
build: $(DEFAULT_PUSH_TARGET)
|
||||
$(DEFAULT_PUSH_TARGET): build-$(DEFAULT_PUSH_TARGET)
|
||||
$(DEFAULT_PUSH_TARGET): DIST = $(DEFAULT_PUSH_TARGET)
|
||||
|
||||
TEST_CASES ?= docker crio containerd
|
||||
$(TEST_TARGETS): test-%:
|
||||
TEST_CASES="$(TEST_CASES)" bash -x $(CURDIR)/test/container/main.sh run \
|
||||
$(CURDIR)/shared-$(*) \
|
||||
|
||||
@@ -16,8 +16,7 @@ PUSH_ON_BUILD ?= false
|
||||
DOCKER_BUILD_OPTIONS = --output=type=image,push=$(PUSH_ON_BUILD)
|
||||
DOCKER_BUILD_PLATFORM_OPTIONS = --platform=linux/amd64,linux/arm64
|
||||
|
||||
# We only generate amd64 image for ubuntu18.04
|
||||
build-ubuntu18.04: DOCKER_BUILD_PLATFORM_OPTIONS = --platform=linux/amd64
|
||||
$(BUILD_TARGETS): build-%: image-%
|
||||
|
||||
# We only generate a single image for packaging targets
|
||||
build-packaging: DOCKER_BUILD_PLATFORM_OPTIONS = --platform=linux/amd64
|
||||
|
||||
@@ -12,4 +12,22 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
DOCKER_BUILD_PLATFORM_OPTIONS = --platform=linux/amd64
|
||||
PUSH_ON_BUILD ?= false
|
||||
ARCH ?= $(shell uname -m)
|
||||
DOCKER_BUILD_PLATFORM_OPTIONS = --platform=linux/$(ARCH)
|
||||
|
||||
ifeq ($(PUSH_ON_BUILD),true)
|
||||
DOCKER_BUILD_OPTIONS = --output=type=image,push=$(PUSH_ON_BUILD)
|
||||
$(BUILD_TARGETS): build-%: image-%
|
||||
$(DOCKER) push "$(IMAGE)"
|
||||
else
|
||||
$(BUILD_TARGETS): build-%: image-%
|
||||
endif
|
||||
|
||||
# For the default distribution we also retag the image.
|
||||
# Note: This needs to be updated for multi-arch images.
|
||||
ifeq ($(IMAGE_TAG),$(VERSION)-$(DIST))
|
||||
$(DEFAULT_PUSH_TARGET):
|
||||
$(DOCKER) image inspect $(IMAGE) > /dev/null || $(DOCKER) pull $(IMAGE)
|
||||
$(DOCKER) tag $(IMAGE) $(subst :$(IMAGE_TAG),:$(VERSION),$(IMAGE))
|
||||
endif
|
||||
|
||||
@@ -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.23.2
|
||||
FROM golang:1.24.0
|
||||
|
||||
WORKDIR /work
|
||||
COPY * .
|
||||
|
||||
@@ -1,60 +1,61 @@
|
||||
module github.com/NVIDIA/k8s-device-plugin/deployments/devel
|
||||
|
||||
go 1.23
|
||||
go 1.24
|
||||
|
||||
toolchain go1.23.1
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
github.com/golangci/golangci-lint v1.61.0
|
||||
github.com/matryer/moq v0.5.0
|
||||
github.com/golangci/golangci-lint v1.64.6
|
||||
github.com/matryer/moq v0.5.3
|
||||
)
|
||||
|
||||
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.1.1 // indirect
|
||||
github.com/Antonboom/errname v0.1.13 // indirect
|
||||
github.com/Antonboom/nilnil v0.1.9 // indirect
|
||||
github.com/Antonboom/testifylint v1.4.3 // indirect
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.2 // indirect
|
||||
github.com/4meepo/tagalign v1.4.2 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.3 // indirect
|
||||
github.com/Antonboom/errname v1.0.0 // indirect
|
||||
github.com/Antonboom/nilnil v1.0.1 // indirect
|
||||
github.com/Antonboom/testifylint v1.5.2 // indirect
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||
github.com/Crocmagnon/fatcontext v0.5.2 // indirect
|
||||
github.com/Crocmagnon/fatcontext v0.7.1 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // 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
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.5 // indirect
|
||||
github.com/alexkohler/prealloc v1.0.0 // indirect
|
||||
github.com/alingse/asasalint v0.0.11 // indirect
|
||||
github.com/alingse/nilnesserr v0.1.2 // indirect
|
||||
github.com/ashanbrown/forbidigo v1.6.0 // indirect
|
||||
github.com/ashanbrown/makezero v1.1.1 // indirect
|
||||
github.com/ashanbrown/makezero v1.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.3 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // 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
|
||||
github.com/butuzov/mirror v1.2.0 // indirect
|
||||
github.com/catenacyber/perfsprint v0.7.1 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.5.0 // indirect
|
||||
github.com/breml/bidichk v0.3.2 // indirect
|
||||
github.com/breml/errchkjson v0.4.0 // indirect
|
||||
github.com/butuzov/ireturn v0.3.1 // indirect
|
||||
github.com/butuzov/mirror v1.3.0 // indirect
|
||||
github.com/catenacyber/perfsprint v0.8.2 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charithe/durationcheck v0.0.10 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/ckaznocha/intrange v0.2.0 // indirect
|
||||
github.com/curioswitch/go-reassign v0.2.0 // indirect
|
||||
github.com/ckaznocha/intrange v0.3.0 // indirect
|
||||
github.com/curioswitch/go-reassign v0.3.0 // 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
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.5 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.6 // indirect
|
||||
github.com/go-critic/go-critic v0.11.4 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.9 // indirect
|
||||
github.com/go-critic/go-critic v0.12.0 // indirect
|
||||
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astequal v1.2.0 // indirect
|
||||
@@ -62,66 +63,68 @@ 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.1.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // 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-20240816233607-d8596aa466a9 // indirect
|
||||
github.com/golangci/go-printf-func-name v0.1.0 // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // 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
|
||||
github.com/golangci/revgrep v0.5.3 // indirect
|
||||
github.com/golangci/revgrep v0.8.0 // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.1.0 // indirect
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
||||
github.com/gostaticanalysis/comment v1.4.2 // indirect
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
|
||||
github.com/gostaticanalysis/comment v1.5.0 // indirect
|
||||
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
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.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
|
||||
github.com/kkHAIKE/contextcheck v1.1.5 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.4 // indirect
|
||||
github.com/julz/importas v0.2.0 // indirect
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect
|
||||
github.com/kisielk/errcheck v1.9.0 // indirect
|
||||
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
|
||||
github.com/kulti/thelper v0.6.3 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.10 // indirect
|
||||
github.com/kyoh86/exportloopref v0.1.11 // indirect
|
||||
github.com/lasiar/canonicalheader v1.1.1 // indirect
|
||||
github.com/ldez/gomoddirectives v0.2.4 // indirect
|
||||
github.com/ldez/tagliatelle v0.5.0 // indirect
|
||||
github.com/lasiar/canonicalheader v1.1.2 // indirect
|
||||
github.com/ldez/exptostd v0.4.2 // indirect
|
||||
github.com/ldez/gomoddirectives v0.6.1 // indirect
|
||||
github.com/ldez/grignotin v0.9.0 // indirect
|
||||
github.com/ldez/tagliatelle v0.7.1 // indirect
|
||||
github.com/ldez/usetesting v0.4.2 // indirect
|
||||
github.com/leonklingele/grouper v1.1.2 // indirect
|
||||
github.com/lufeee/execinquery v1.2.1 // indirect
|
||||
github.com/macabu/inamedparam v0.1.3 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/maratori/testableexamples v1.0.0 // indirect
|
||||
github.com/maratori/testpackage v1.1.1 // indirect
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/matoous/godox v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgechev/revive v1.3.9 // indirect
|
||||
github.com/mgechev/revive v1.7.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // 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/nunnatsa/ginkgolinter v0.19.1 // 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.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.6.0 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.7.1 // 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
|
||||
@@ -131,62 +134,64 @@ require (
|
||||
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/raeperd/recvcheck v0.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // 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/sanposhiho/wastedassign/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // 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/sashamelentyev/usestdlibvars v1.28.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
github.com/sivchari/tenv v1.10.0 // indirect
|
||||
github.com/sonatard/noctx v0.0.2 // indirect
|
||||
github.com/sivchari/tenv v1.12.1 // indirect
|
||||
github.com/sonatard/noctx v0.1.0 // indirect
|
||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/tdakkota/asciicheck v0.2.0 // 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.9.0 // indirect
|
||||
github.com/tdakkota/asciicheck v0.4.1 // indirect
|
||||
github.com/tetafro/godot v1.5.0 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 // indirect
|
||||
github.com/timonwong/loggercheck v0.10.1 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.10.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.3 // indirect
|
||||
github.com/ultraware/funlen v0.2.0 // indirect
|
||||
github.com/ultraware/whitespace v0.2.0 // indirect
|
||||
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||
github.com/uudashr/iface v1.3.1 // 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.2 // indirect
|
||||
go-simpler.org/musttag v0.13.0 // indirect
|
||||
go-simpler.org/sloglint v0.9.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // 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.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
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // 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.5.1 // indirect
|
||||
honnef.co/go/tools v0.6.0 // indirect
|
||||
mvdan.cc/gofumpt v0.7.0 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA=
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
|
||||
4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc=
|
||||
4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU=
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A=
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY=
|
||||
4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU=
|
||||
4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
@@ -35,79 +35,82 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
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.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.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck=
|
||||
github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA=
|
||||
github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E=
|
||||
github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
|
||||
github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
|
||||
github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
|
||||
github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA=
|
||||
github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI=
|
||||
github.com/Antonboom/nilnil v1.0.1 h1:C3Tkm0KUxgfO4Duk3PM+ztPncTFlOf0b2qadmS0s4xs=
|
||||
github.com/Antonboom/nilnil v1.0.1/go.mod h1:CH7pW2JsRNFgEh8B2UaPZTEPhCMuFowP/e8Udp9Nnb0=
|
||||
github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk=
|
||||
github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
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.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA=
|
||||
github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74=
|
||||
github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM=
|
||||
github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU=
|
||||
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.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg=
|
||||
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=
|
||||
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||
github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c=
|
||||
github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
|
||||
github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
|
||||
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
|
||||
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
|
||||
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
|
||||
github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo=
|
||||
github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
|
||||
github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY=
|
||||
github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
|
||||
github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s=
|
||||
github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
|
||||
github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU=
|
||||
github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY=
|
||||
github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM=
|
||||
github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w=
|
||||
github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
|
||||
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.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=
|
||||
github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U=
|
||||
github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0=
|
||||
github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA=
|
||||
github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs=
|
||||
github.com/butuzov/mirror v1.2.0/go.mod h1:DqZZDtzm42wIAIyHXeN8W/qb1EPlb9Qn/if9icBOpdQ=
|
||||
github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc=
|
||||
github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50=
|
||||
github.com/bombsimon/wsl/v4 v4.5.0 h1:iZRsEvDdyhd2La0FVi5k6tYehpOR/R7qIUjmKk7N74A=
|
||||
github.com/bombsimon/wsl/v4 v4.5.0/go.mod h1:NOQ3aLF4nD7N5YPXMruR6ZXDOAqLoM0GEpLwTdvmOSc=
|
||||
github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs=
|
||||
github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos=
|
||||
github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk=
|
||||
github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8=
|
||||
github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY=
|
||||
github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M=
|
||||
github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
|
||||
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
|
||||
github.com/catenacyber/perfsprint v0.8.2 h1:+o9zVmCSVa7M4MvabsWvESEhpsMkhfE7k0sHNGL95yw=
|
||||
github.com/catenacyber/perfsprint v0.8.2/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4=
|
||||
github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
|
||||
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
|
||||
@@ -115,13 +118,13 @@ 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.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs=
|
||||
github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE=
|
||||
github.com/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY=
|
||||
github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo=
|
||||
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.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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
|
||||
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
|
||||
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=
|
||||
@@ -129,14 +132,16 @@ 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=
|
||||
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
|
||||
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
|
||||
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
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=
|
||||
@@ -147,10 +152,10 @@ github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwV
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk=
|
||||
github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw=
|
||||
github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU=
|
||||
github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc=
|
||||
github.com/ghostiam/protogetter v0.3.9 h1:j+zlLLWzqLay22Cz/aYwTHKQ88GE2DQ6GkWSYFOI4lQ=
|
||||
github.com/ghostiam/protogetter v0.3.9/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
|
||||
github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w=
|
||||
github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -186,10 +191,10 @@ 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.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/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3/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.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
@@ -226,18 +231,18 @@ 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-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/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU=
|
||||
github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
|
||||
github.com/golangci/golangci-lint v1.64.6 h1:jOLaQN41IV7bMzXuNC4UnQGll7N1xY6eFDXkXEPGKAs=
|
||||
github.com/golangci/golangci-lint v1.64.6/go.mod h1:Wz9q+6EVuqGQ94GQ96RB2mjpcZYTOGhBhbt4O7REPu4=
|
||||
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=
|
||||
github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM=
|
||||
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
|
||||
github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc=
|
||||
github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs=
|
||||
github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
|
||||
github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s=
|
||||
github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs=
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -254,8 +259,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -266,8 +271,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-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/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=
|
||||
@@ -276,20 +281,27 @@ github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
|
||||
github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
|
||||
github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q=
|
||||
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak=
|
||||
github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8=
|
||||
github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
|
||||
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
|
||||
github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY=
|
||||
github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
|
||||
github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8=
|
||||
github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs=
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
@@ -301,10 +313,8 @@ github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5
|
||||
github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
|
||||
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.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk=
|
||||
github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA=
|
||||
github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc=
|
||||
github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk=
|
||||
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=
|
||||
@@ -314,15 +324,15 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY=
|
||||
github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
|
||||
github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos=
|
||||
github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k=
|
||||
github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0=
|
||||
github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
|
||||
github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ=
|
||||
github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY=
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI=
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.1/go.mod h1:nFmMlFNlClC2BPvNaHMdkirmTJxVCY0lhxBtlfOypMM=
|
||||
github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
|
||||
github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg=
|
||||
github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA=
|
||||
github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE=
|
||||
github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@@ -337,18 +347,20 @@ github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
|
||||
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
|
||||
github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs=
|
||||
github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
|
||||
github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ=
|
||||
github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
|
||||
github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I=
|
||||
github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0=
|
||||
github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg=
|
||||
github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g=
|
||||
github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo=
|
||||
github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4=
|
||||
github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
|
||||
github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
|
||||
github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs=
|
||||
github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
|
||||
github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc=
|
||||
github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs=
|
||||
github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow=
|
||||
github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk=
|
||||
github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk=
|
||||
github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I=
|
||||
github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA=
|
||||
github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ=
|
||||
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
|
||||
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
|
||||
github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM=
|
||||
github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
|
||||
github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk=
|
||||
github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
@@ -357,23 +369,23 @@ github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s
|
||||
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
|
||||
github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
|
||||
github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2srm/LN17lpybq15AryXIRcWYLE=
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4=
|
||||
github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs=
|
||||
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.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=
|
||||
github.com/matryer/moq v0.5.3 h1:4femQCFmBUwFPYs8VfM5ID7AI67/DTEDRBbTtSWy7GU=
|
||||
github.com/matryer/moq v0.5.3/go.mod h1:8288Qkw7gMZhUP3cIN86GG7g5p9jRuZH8biXLW4RXvQ=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
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.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A=
|
||||
github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU=
|
||||
github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY=
|
||||
github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4=
|
||||
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=
|
||||
@@ -393,14 +405,14 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
|
||||
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk=
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
|
||||
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.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/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
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=
|
||||
@@ -418,8 +430,8 @@ 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.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY=
|
||||
github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw=
|
||||
github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA=
|
||||
github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8=
|
||||
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=
|
||||
@@ -454,26 +466,29 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
|
||||
github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI=
|
||||
github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
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=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
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.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/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
|
||||
github.com/securego/gosec/v2 v2.22.1 h1:IcBt3TpI5Y9VN1YlwjSpM2cHu0i3Iw52QM+PQeg7jN8=
|
||||
github.com/securego/gosec/v2 v2.22.1/go.mod h1:4bb95X4Jz7VSEPdVjC0hD7C/yR6kdeUBvCPOy9gDQ0g=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -483,28 +498,29 @@ 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.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/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY=
|
||||
github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw=
|
||||
github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM=
|
||||
github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c=
|
||||
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
|
||||
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
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.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
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=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@@ -519,32 +535,34 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM=
|
||||
github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
|
||||
github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8=
|
||||
github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
|
||||
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.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.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
|
||||
github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw=
|
||||
github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
|
||||
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 h1:y4mJRFlM6fUyPhoXuFg/Yu02fg/nIPFMOY8tOqppoFg=
|
||||
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
|
||||
github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg=
|
||||
github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.10.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.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
|
||||
github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
|
||||
github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI=
|
||||
github.com/ultraware/funlen v0.2.0/go.mod h1:ZE0q4TsJ8T1SQcjmkhN/w+MceuatI6pBFSxxyteHIJA=
|
||||
github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSWoFa+g=
|
||||
github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
|
||||
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
|
||||
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
|
||||
github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U=
|
||||
github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg=
|
||||
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=
|
||||
@@ -564,10 +582,10 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
|
||||
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
|
||||
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.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY=
|
||||
go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo=
|
||||
go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE=
|
||||
go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM=
|
||||
go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE=
|
||||
go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww=
|
||||
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=
|
||||
@@ -575,8 +593,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
@@ -590,7 +608,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -601,12 +620,12 @@ 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-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
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=
|
||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -629,14 +648,15 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.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=
|
||||
@@ -671,12 +691,14 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
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 +718,10 @@ 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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.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=
|
||||
@@ -731,7 +755,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -745,20 +768,22 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.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=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -767,10 +792,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
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=
|
||||
@@ -780,7 +807,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
@@ -788,10 +814,8 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -822,20 +846,19 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
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.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.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
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=
|
||||
@@ -915,8 +938,8 @@ 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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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=
|
||||
@@ -943,8 +966,8 @@ 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.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
|
||||
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
|
||||
honnef.co/go/tools v0.6.0 h1:TAODvD3knlq75WCp2nyGJtT4LeRV/o7NN9nYPeVJXf8=
|
||||
honnef.co/go/tools v0.6.0/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||
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=
|
||||
|
||||
25
go.mod
25
go.mod
@@ -1,31 +1,32 @@
|
||||
module github.com/NVIDIA/nvidia-container-toolkit
|
||||
|
||||
go 1.20
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
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/NVIDIA/go-nvlib v0.7.1
|
||||
github.com/NVIDIA/go-nvml v0.12.4-1
|
||||
github.com/moby/sys/symlink v0.3.0
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/opencontainers/runc v1.2.5
|
||||
github.com/opencontainers/runtime-spec v1.2.1
|
||||
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.4
|
||||
golang.org/x/mod v0.20.0
|
||||
golang.org/x/sys v0.26.0
|
||||
tags.cncf.io/container-device-interface v0.8.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/sys v0.31.0
|
||||
tags.cncf.io/container-device-interface v0.8.1
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
|
||||
40
go.sum
40
go.sum
@@ -1,12 +1,14 @@
|
||||
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/NVIDIA/go-nvlib v0.7.1 h1:7HHPZxoCjSLm1NgaRRjuhI8ffMCpc5Vgpg5yxQYUff8=
|
||||
github.com/NVIDIA/go-nvlib v0.7.1/go.mod h1:2Kh2kYSP5IJ8EKf0/SYDzHiQKb9EJkwOf2LQzu6pXzY=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-1 h1:WKUvqshhWSNTfm47ETRhv0A0zJyr1ncCuHiXwoTrBEc=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-1/go.mod h1:8Llmj+1Rr+9VGGwZuRer5N/aCjxGuR5nPb/9ebBiIEQ=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
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=
|
||||
@@ -31,9 +33,11 @@ github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34
|
||||
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/runc v1.2.5 h1:8KAkq3Wrem8bApgOHyhRI/8IeLXIfmZ6Qaw6DNSLnA4=
|
||||
github.com/opencontainers/runc v1.2.5/go.mod h1:dOQeFo29xZKBNeRBI0B19mJtfHv68YgCTh1X+YphA+4=
|
||||
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=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
|
||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
@@ -55,13 +59,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
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/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
|
||||
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
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=
|
||||
@@ -71,13 +75,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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
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=
|
||||
@@ -88,7 +92,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
||||
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
tags.cncf.io/container-device-interface v0.8.1 h1:c0jN4Mt6781jD67NdPajmZlD1qrqQyov/Xfoab37lj0=
|
||||
tags.cncf.io/container-device-interface v0.8.1/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -34,29 +35,62 @@ type ContainerCLIConfig struct {
|
||||
NoPivot bool `toml:"no-pivot,omitempty"`
|
||||
NoCgroups bool `toml:"no-cgroups"`
|
||||
User string `toml:"user"`
|
||||
Ldconfig string `toml:"ldconfig"`
|
||||
// Ldconfig represents the path to the ldconfig binary to be used to update
|
||||
// the ldcache in a container as it is being created.
|
||||
// If this path starts with a '@' the path is relative to the host and if
|
||||
// not it is treated as a container path.
|
||||
//
|
||||
// Note that the use of container paths are disabled by default and if this
|
||||
// is required, the features.allow-ldconfig-from-container feature gate must
|
||||
// be enabled explicitly.
|
||||
Ldconfig ldconfigPath `toml:"ldconfig"`
|
||||
}
|
||||
|
||||
// NormalizeLDConfigPath returns the resolved path of the configured LDConfig binary.
|
||||
// This is only done for host LDConfigs and is required to handle systems where
|
||||
// /sbin/ldconfig is a wrapper around /sbin/ldconfig.real.
|
||||
func (c *ContainerCLIConfig) NormalizeLDConfigPath() string {
|
||||
return NormalizeLDConfigPath(c.Ldconfig)
|
||||
return string(c.Ldconfig.normalize())
|
||||
}
|
||||
|
||||
// An ldconfigPath is used to represent the path to ldconfig.
|
||||
type ldconfigPath string
|
||||
|
||||
func (p ldconfigPath) assertValid(allowContainerRelativePath bool) error {
|
||||
if p.isHostRelative() {
|
||||
return nil
|
||||
}
|
||||
if allowContainerRelativePath {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("nvidia-container-cli.ldconfig value %q is not host-relative (does not start with a '@')", p)
|
||||
}
|
||||
|
||||
func (p ldconfigPath) isHostRelative() bool {
|
||||
return strings.HasPrefix(string(p), "@")
|
||||
}
|
||||
|
||||
// normalize returns the resolved path of the configured LDConfig binary.
|
||||
// This is only done for host LDConfigs and is required to handle systems where
|
||||
// /sbin/ldconfig is a wrapper around /sbin/ldconfig.real.
|
||||
func (p ldconfigPath) normalize() ldconfigPath {
|
||||
if !p.isHostRelative() {
|
||||
return p
|
||||
}
|
||||
|
||||
path := string(p)
|
||||
trimmedPath := strings.TrimSuffix(strings.TrimPrefix(path, "@"), ".real")
|
||||
// If the .real path exists, we return that.
|
||||
if _, err := os.Stat(trimmedPath + ".real"); err == nil {
|
||||
return ldconfigPath("@" + trimmedPath + ".real")
|
||||
}
|
||||
// If the .real path does not exists (or cannot be read) we return the non-.real path.
|
||||
return ldconfigPath("@" + trimmedPath)
|
||||
}
|
||||
|
||||
// NormalizeLDConfigPath returns the resolved path of the configured LDConfig binary.
|
||||
// This is only done for host LDConfigs and is required to handle systems where
|
||||
// /sbin/ldconfig is a wrapper around /sbin/ldconfig.real.
|
||||
func NormalizeLDConfigPath(path string) string {
|
||||
if !strings.HasPrefix(path, "@") {
|
||||
return path
|
||||
}
|
||||
|
||||
trimmedPath := strings.TrimSuffix(strings.TrimPrefix(path, "@"), ".real")
|
||||
// If the .real path exists, we return that.
|
||||
if _, err := os.Stat(trimmedPath + ".real"); err == nil {
|
||||
return "@" + trimmedPath + ".real"
|
||||
}
|
||||
// If the .real path does not exists (or cannot be read) we return the non-.real path.
|
||||
return "@" + trimmedPath
|
||||
return string(ldconfigPath(path).normalize())
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestNormalizeLDConfigPath(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
ldconfig string
|
||||
ldconfig ldconfigPath
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
@@ -51,12 +51,12 @@ func TestNormalizeLDConfigPath(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "host .real file exists is returned",
|
||||
ldconfig: "@" + filepath.Join(testDir, "exists.real"),
|
||||
ldconfig: ldconfigPath("@" + filepath.Join(testDir, "exists.real")),
|
||||
expected: "@" + filepath.Join(testDir, "exists.real"),
|
||||
},
|
||||
{
|
||||
description: "host resolves .real file",
|
||||
ldconfig: "@" + filepath.Join(testDir, "exists"),
|
||||
ldconfig: ldconfigPath("@" + filepath.Join(testDir, "exists")),
|
||||
expected: "@" + filepath.Join(testDir, "exists.real"),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -51,6 +52,8 @@ var (
|
||||
NVIDIAContainerToolkitExecutable = "nvidia-container-toolkit"
|
||||
)
|
||||
|
||||
var errInvalidConfig = errors.New("invalid config value")
|
||||
|
||||
// Config represents the contents of the config.toml file for the NVIDIA Container Toolkit
|
||||
// Note: This is currently duplicated by the HookConfig in cmd/nvidia-container-toolkit/hook_config.go
|
||||
type Config struct {
|
||||
@@ -118,6 +121,9 @@ func GetDefault() (*Config, error) {
|
||||
AnnotationPrefixes: []string{cdi.AnnotationPrefix},
|
||||
SpecDirs: cdi.DefaultSpecDirs,
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -127,8 +133,20 @@ func GetDefault() (*Config, error) {
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
func getLdConfigPath() string {
|
||||
return NormalizeLDConfigPath("@/sbin/ldconfig")
|
||||
// assertValid checks for a valid config.
|
||||
func (c *Config) assertValid() error {
|
||||
err := c.NVIDIAContainerCLIConfig.Ldconfig.assertValid(c.Features.AllowLDConfigFromContainer.IsEnabled())
|
||||
if err != nil {
|
||||
return errors.Join(err, errInvalidConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getLdConfigPath allows us to override this function for testing.
|
||||
var getLdConfigPath = getLdConfigPathStub
|
||||
|
||||
func getLdConfigPathStub() ldconfigPath {
|
||||
return ldconfigPath("@/sbin/ldconfig").normalize()
|
||||
}
|
||||
|
||||
func getUserGroup() string {
|
||||
|
||||
@@ -44,23 +44,21 @@ func TestGetConfigWithCustomConfig(t *testing.T) {
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
contents []string
|
||||
expectedError error
|
||||
inspectLdconfig bool
|
||||
distIdsLike []string
|
||||
expectedConfig *Config
|
||||
description string
|
||||
contents []string
|
||||
expectedError error
|
||||
distIdsLike []string
|
||||
expectedConfig *Config
|
||||
}{
|
||||
{
|
||||
description: "empty config is default",
|
||||
inspectLdconfig: true,
|
||||
description: "empty config is default",
|
||||
expectedConfig: &Config{
|
||||
AcceptEnvvarUnprivileged: true,
|
||||
SupportedDriverCapabilities: "compat32,compute,display,graphics,ngx,utility,video",
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Root: "",
|
||||
LoadKmods: true,
|
||||
Ldconfig: "WAS_CHECKED",
|
||||
Ldconfig: "@/test/ld/config/path",
|
||||
},
|
||||
NVIDIAContainerRuntimeConfig: RuntimeConfig{
|
||||
DebugFilePath: "/dev/null",
|
||||
@@ -76,6 +74,9 @@ func TestGetConfig(t *testing.T) {
|
||||
AnnotationPrefixes: []string{"cdi.k8s.io/"},
|
||||
SpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -93,7 +94,7 @@ func TestGetConfig(t *testing.T) {
|
||||
"supported-driver-capabilities = \"compute,utility\"",
|
||||
"nvidia-container-cli.root = \"/bar/baz\"",
|
||||
"nvidia-container-cli.load-kmods = false",
|
||||
"nvidia-container-cli.ldconfig = \"/foo/bar/ldconfig\"",
|
||||
"nvidia-container-cli.ldconfig = \"@/foo/bar/ldconfig\"",
|
||||
"nvidia-container-cli.user = \"foo:bar\"",
|
||||
"nvidia-container-runtime.debug = \"/foo/bar\"",
|
||||
"nvidia-container-runtime.discover-mode = \"not-legacy\"",
|
||||
@@ -104,6 +105,7 @@ func TestGetConfig(t *testing.T) {
|
||||
"nvidia-container-runtime.modes.cdi.annotation-prefixes = [\"cdi.k8s.io/\", \"example.vendor.com/\",]",
|
||||
"nvidia-container-runtime.modes.cdi.spec-dirs = [\"/except/etc/cdi\", \"/not/var/run/cdi\",]",
|
||||
"nvidia-container-runtime.modes.csv.mount-spec-path = \"/not/etc/nvidia-container-runtime/host-files-for-container.d\"",
|
||||
"nvidia-container-runtime.modes.jit-cdi.load-kernel-modules = [\"foo\"]",
|
||||
"nvidia-container-runtime-hook.path = \"/foo/bar/nvidia-container-runtime-hook\"",
|
||||
"nvidia-ctk.path = \"/foo/bar/nvidia-ctk\"",
|
||||
},
|
||||
@@ -113,7 +115,7 @@ func TestGetConfig(t *testing.T) {
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Root: "/bar/baz",
|
||||
LoadKmods: false,
|
||||
Ldconfig: "/foo/bar/ldconfig",
|
||||
Ldconfig: "@/foo/bar/ldconfig",
|
||||
User: "foo:bar",
|
||||
},
|
||||
NVIDIAContainerRuntimeConfig: RuntimeConfig{
|
||||
@@ -136,6 +138,9 @@ func TestGetConfig(t *testing.T) {
|
||||
"/not/var/run/cdi",
|
||||
},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -146,6 +151,56 @@ func TestGetConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "feature allows ldconfig to be overridden",
|
||||
contents: []string{
|
||||
"[nvidia-container-cli]",
|
||||
"ldconfig = \"/foo/bar/ldconfig\"",
|
||||
"[features]",
|
||||
"allow-ldconfig-from-container = true",
|
||||
},
|
||||
expectedConfig: &Config{
|
||||
AcceptEnvvarUnprivileged: true,
|
||||
SupportedDriverCapabilities: "compat32,compute,display,graphics,ngx,utility,video",
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Ldconfig: "/foo/bar/ldconfig",
|
||||
LoadKmods: true,
|
||||
},
|
||||
NVIDIAContainerRuntimeConfig: RuntimeConfig{
|
||||
DebugFilePath: "/dev/null",
|
||||
LogLevel: "info",
|
||||
Runtimes: []string{"docker-runc", "runc", "crun"},
|
||||
Mode: "auto",
|
||||
Modes: modesConfig{
|
||||
CSV: csvModeConfig{
|
||||
MountSpecPath: "/etc/nvidia-container-runtime/host-files-for-container.d",
|
||||
},
|
||||
CDI: cdiModeConfig{
|
||||
DefaultKind: "nvidia.com/gpu",
|
||||
AnnotationPrefixes: []string{
|
||||
"cdi.k8s.io/",
|
||||
},
|
||||
SpecDirs: []string{
|
||||
"/etc/cdi",
|
||||
"/var/run/cdi",
|
||||
},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
Path: "nvidia-container-runtime-hook",
|
||||
},
|
||||
NVIDIACTKConfig: CTKConfig{
|
||||
Path: "nvidia-ctk",
|
||||
},
|
||||
Features: features{
|
||||
AllowLDConfigFromContainer: ptr(feature(true)),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "config options set in section",
|
||||
contents: []string{
|
||||
@@ -154,7 +209,7 @@ func TestGetConfig(t *testing.T) {
|
||||
"[nvidia-container-cli]",
|
||||
"root = \"/bar/baz\"",
|
||||
"load-kmods = false",
|
||||
"ldconfig = \"/foo/bar/ldconfig\"",
|
||||
"ldconfig = \"@/foo/bar/ldconfig\"",
|
||||
"user = \"foo:bar\"",
|
||||
"[nvidia-container-runtime]",
|
||||
"debug = \"/foo/bar\"",
|
||||
@@ -168,6 +223,8 @@ func TestGetConfig(t *testing.T) {
|
||||
"spec-dirs = [\"/except/etc/cdi\", \"/not/var/run/cdi\",]",
|
||||
"[nvidia-container-runtime.modes.csv]",
|
||||
"mount-spec-path = \"/not/etc/nvidia-container-runtime/host-files-for-container.d\"",
|
||||
"[nvidia-container-runtime.modes.jit-cdi]",
|
||||
"load-kernel-modules = [\"foo\"]",
|
||||
"[nvidia-container-runtime-hook]",
|
||||
"path = \"/foo/bar/nvidia-container-runtime-hook\"",
|
||||
"[nvidia-ctk]",
|
||||
@@ -179,7 +236,7 @@ func TestGetConfig(t *testing.T) {
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Root: "/bar/baz",
|
||||
LoadKmods: false,
|
||||
Ldconfig: "/foo/bar/ldconfig",
|
||||
Ldconfig: "@/foo/bar/ldconfig",
|
||||
User: "foo:bar",
|
||||
},
|
||||
NVIDIAContainerRuntimeConfig: RuntimeConfig{
|
||||
@@ -202,6 +259,9 @@ func TestGetConfig(t *testing.T) {
|
||||
"/not/var/run/cdi",
|
||||
},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -213,16 +273,15 @@ func TestGetConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "suse config",
|
||||
distIdsLike: []string{"suse", "opensuse"},
|
||||
inspectLdconfig: true,
|
||||
description: "suse config",
|
||||
distIdsLike: []string{"suse", "opensuse"},
|
||||
expectedConfig: &Config{
|
||||
AcceptEnvvarUnprivileged: true,
|
||||
SupportedDriverCapabilities: "compat32,compute,display,graphics,ngx,utility,video",
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Root: "",
|
||||
LoadKmods: true,
|
||||
Ldconfig: "WAS_CHECKED",
|
||||
Ldconfig: "@/test/ld/config/path",
|
||||
User: "root:video",
|
||||
},
|
||||
NVIDIAContainerRuntimeConfig: RuntimeConfig{
|
||||
@@ -239,6 +298,9 @@ func TestGetConfig(t *testing.T) {
|
||||
AnnotationPrefixes: []string{"cdi.k8s.io/"},
|
||||
SpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -250,9 +312,8 @@ func TestGetConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "suse config overrides user",
|
||||
distIdsLike: []string{"suse", "opensuse"},
|
||||
inspectLdconfig: true,
|
||||
description: "suse config overrides user",
|
||||
distIdsLike: []string{"suse", "opensuse"},
|
||||
contents: []string{
|
||||
"nvidia-container-cli.user = \"foo:bar\"",
|
||||
},
|
||||
@@ -262,7 +323,7 @@ func TestGetConfig(t *testing.T) {
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Root: "",
|
||||
LoadKmods: true,
|
||||
Ldconfig: "WAS_CHECKED",
|
||||
Ldconfig: "@/test/ld/config/path",
|
||||
User: "foo:bar",
|
||||
},
|
||||
NVIDIAContainerRuntimeConfig: RuntimeConfig{
|
||||
@@ -279,6 +340,9 @@ func TestGetConfig(t *testing.T) {
|
||||
AnnotationPrefixes: []string{"cdi.k8s.io/"},
|
||||
SpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
},
|
||||
JitCDI: jitCDIModeConfig{
|
||||
LoadKernelModules: []string{"nvidia", "nvidia-uvm", "nvidia-modeset"},
|
||||
},
|
||||
},
|
||||
},
|
||||
NVIDIAContainerRuntimeHookConfig: RuntimeHookConfig{
|
||||
@@ -293,6 +357,7 @@ func TestGetConfig(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
defer setGetLdConfigPathForTest()()
|
||||
defer setGetDistIDLikeForTest(tc.distIdsLike)()
|
||||
reader := strings.NewReader(strings.Join(tc.contents, "\n"))
|
||||
|
||||
@@ -305,21 +370,63 @@ func TestGetConfig(t *testing.T) {
|
||||
cfg, err := tomlCfg.Config()
|
||||
require.NoError(t, err)
|
||||
|
||||
// We first handle the ldconfig path since this is currently system-dependent.
|
||||
if tc.inspectLdconfig {
|
||||
ldconfig := cfg.NVIDIAContainerCLIConfig.Ldconfig
|
||||
require.True(t, strings.HasPrefix(ldconfig, "@/sbin/ldconfig"))
|
||||
remaining := strings.TrimPrefix(ldconfig, "@/sbin/ldconfig")
|
||||
require.True(t, remaining == ".real" || remaining == "")
|
||||
|
||||
cfg.NVIDIAContainerCLIConfig.Ldconfig = "WAS_CHECKED"
|
||||
}
|
||||
|
||||
require.EqualValues(t, tc.expectedConfig, cfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssertValid(t *testing.T) {
|
||||
defer setGetLdConfigPathForTest()()
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
config *Config
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "default is valid",
|
||||
config: func() *Config {
|
||||
config, _ := GetDefault()
|
||||
return config
|
||||
}(),
|
||||
},
|
||||
{
|
||||
description: "alternative host ldconfig path is valid",
|
||||
config: &Config{
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Ldconfig: "@/some/host/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "non-host path is invalid",
|
||||
config: &Config{
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Ldconfig: "/non/host/path",
|
||||
},
|
||||
},
|
||||
expectedError: errInvalidConfig,
|
||||
},
|
||||
{
|
||||
description: "feature flag allows non-host path",
|
||||
config: &Config{
|
||||
NVIDIAContainerCLIConfig: ContainerCLIConfig{
|
||||
Ldconfig: "/non/host/path",
|
||||
},
|
||||
Features: features{
|
||||
AllowLDConfigFromContainer: ptr(feature(true)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
require.ErrorIs(t, tc.config.assertValid(), tc.expectedError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// setGetDistIDsLikeForTest overrides the distribution IDs that would normally be read from the /etc/os-release file.
|
||||
func setGetDistIDLikeForTest(ids []string) func() {
|
||||
if ids == nil {
|
||||
@@ -335,3 +442,18 @@ func setGetDistIDLikeForTest(ids []string) func() {
|
||||
getDistIDLike = original
|
||||
}
|
||||
}
|
||||
|
||||
// prt returns a reference to whatever type is passed into it
|
||||
func ptr[T any](x T) *T {
|
||||
return &x
|
||||
}
|
||||
|
||||
func setGetLdConfigPathForTest() func() {
|
||||
previous := getLdConfigPath
|
||||
getLdConfigPath = func() ldconfigPath {
|
||||
return "@/test/ld/config/path"
|
||||
}
|
||||
return func() {
|
||||
getLdConfigPath = previous
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,35 @@ package config
|
||||
|
||||
// features specifies a set of named features.
|
||||
type features struct {
|
||||
// AllowCUDACompatLibsFromContainer allows CUDA compat libs from a container
|
||||
// to override certain driver library mounts from the host.
|
||||
AllowCUDACompatLibsFromContainer *feature `toml:"allow-cuda-compat-libs-from-container,omitempty"`
|
||||
// AllowLDConfigFromContainer allows non-host ldconfig paths to be used.
|
||||
// If this feature flag is not set to 'true' only host-rooted config paths
|
||||
// (i.e. paths starting with an '@' are considered valid)
|
||||
AllowLDConfigFromContainer *feature `toml:"allow-ldconfig-from-container,omitempty"`
|
||||
// DisableCUDACompatLibHook, when enabled skips the injection of a specific
|
||||
// hook to process CUDA compatibility libraries.
|
||||
//
|
||||
// Note: Since this mechanism replaces the logic in the `nvidia-container-cli`,
|
||||
// toggling this feature has no effect if `allow-cuda-compat-libs-from-container` is enabled.
|
||||
DisableCUDACompatLibHook *feature `toml:"disable-cuda-compat-lib-hook,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"`
|
||||
// IgnoreImexChannelRequests configures the NVIDIA Container Toolkit to
|
||||
// ignore IMEX channel requests through the NVIDIA_IMEX_CHANNELS envvar or
|
||||
// volume mounts.
|
||||
// This ensures that the NVIDIA Container Toolkit cannot be used to provide
|
||||
// access to an IMEX channel by simply specifying an environment variable,
|
||||
// possibly bypassing other checks by an orchestration system such as
|
||||
// kubernetes.
|
||||
IgnoreImexChannelRequests *feature `toml:"ignore-imex-channel-requests,omitempty"`
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
type feature bool
|
||||
|
||||
// IsEnabled checks whether a feature is explicitly enabled.
|
||||
//
|
||||
//nolint:unused
|
||||
func (f *feature) IsEnabled() bool {
|
||||
if f != nil {
|
||||
return bool(*f)
|
||||
|
||||
@@ -24,13 +24,3 @@ type RuntimeHookConfig struct {
|
||||
// SkipModeDetection disables the mode check for the runtime hook.
|
||||
SkipModeDetection bool `toml:"skip-mode-detection"`
|
||||
}
|
||||
|
||||
// GetDefaultRuntimeHookConfig defines the default values for the config
|
||||
func GetDefaultRuntimeHookConfig() (*RuntimeHookConfig, error) {
|
||||
cfg, err := GetDefault()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cfg.NVIDIAContainerRuntimeHookConfig, nil
|
||||
}
|
||||
|
||||
@@ -292,7 +292,11 @@ func (i CUDA) CDIDevicesFromMounts() []string {
|
||||
|
||||
// ImexChannelsFromEnvVar returns the list of IMEX channels requested for the image.
|
||||
func (i CUDA) ImexChannelsFromEnvVar() []string {
|
||||
return i.DevicesFromEnvvars(EnvVarNvidiaImexChannels).List()
|
||||
imexChannels := i.DevicesFromEnvvars(EnvVarNvidiaImexChannels).List()
|
||||
if len(imexChannels) == 1 && imexChannels[0] == "all" {
|
||||
return nil
|
||||
}
|
||||
return imexChannels
|
||||
}
|
||||
|
||||
// ImexChannelsFromMounts returns the list of IMEX channels requested for the image.
|
||||
|
||||
@@ -203,6 +203,37 @@ func TestGetVisibleDevicesFromMounts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImexChannelsFromEnvVar(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
env []string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
description: "no imex channels specified",
|
||||
},
|
||||
{
|
||||
description: "imex channel specified",
|
||||
env: []string{
|
||||
"NVIDIA_IMEX_CHANNELS=3,4",
|
||||
},
|
||||
expected: []string{"3", "4"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
for id, baseEnvvars := range map[string][]string{"": nil, "legacy": {"CUDA_VERSION=1.2.3"}} {
|
||||
t.Run(tc.description+id, func(t *testing.T) {
|
||||
i, err := NewCUDAImageFromEnv(append(baseEnvvars, tc.env...))
|
||||
require.NoError(t, err)
|
||||
|
||||
channels := i.ImexChannelsFromEnvVar()
|
||||
require.EqualValues(t, tc.expected, channels)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestMounts(paths ...string) []specs.Mount {
|
||||
var mounts []specs.Mount
|
||||
for _, path := range paths {
|
||||
|
||||
@@ -29,8 +29,9 @@ type RuntimeConfig struct {
|
||||
|
||||
// modesConfig defines (optional) per-mode configs
|
||||
type modesConfig struct {
|
||||
CSV csvModeConfig `toml:"csv"`
|
||||
CDI cdiModeConfig `toml:"cdi"`
|
||||
CSV csvModeConfig `toml:"csv"`
|
||||
CDI cdiModeConfig `toml:"cdi"`
|
||||
JitCDI jitCDIModeConfig `toml:"jit-cdi"`
|
||||
}
|
||||
|
||||
type cdiModeConfig struct {
|
||||
@@ -46,12 +47,10 @@ type csvModeConfig struct {
|
||||
MountSpecPath string `toml:"mount-spec-path"`
|
||||
}
|
||||
|
||||
// GetDefaultRuntimeConfig defines the default values for the config
|
||||
func GetDefaultRuntimeConfig() (*RuntimeConfig, error) {
|
||||
cfg, err := GetDefault()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cfg.NVIDIAContainerRuntimeConfig, nil
|
||||
type jitCDIModeConfig struct {
|
||||
// LoadKernelModules defines the names of the kernel modules that should be
|
||||
// loaded before generating a just-in-time CDI specification.
|
||||
// The module names must start with `nvidia` and if no modules are specified
|
||||
// no kernel modules are loaded.
|
||||
LoadKernelModules []string `toml:"load-kernel-modules"`
|
||||
}
|
||||
|
||||
@@ -108,6 +108,19 @@ func loadConfigTomlFrom(reader io.Reader) (*Toml, error) {
|
||||
|
||||
// Config returns the typed config associated with the toml tree.
|
||||
func (t *Toml) Config() (*Config, error) {
|
||||
cfg, err := t.configNoOverrides()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := cfg.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// configNoOverrides returns the typed config associated with the toml tree.
|
||||
// This config does not include feature-specific overrides.
|
||||
func (t *Toml) configNoOverrides() (*Config, error) {
|
||||
cfg, err := GetDefault()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -74,6 +74,9 @@ spec-dirs = ["/etc/cdi", "/var/run/cdi"]
|
||||
[nvidia-container-runtime.modes.csv]
|
||||
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
|
||||
|
||||
[nvidia-container-runtime.modes.jit-cdi]
|
||||
load-kernel-modules = ["nvidia", "nvidia-uvm", "nvidia-modeset"]
|
||||
|
||||
[nvidia-container-runtime-hook]
|
||||
path = "nvidia-container-runtime-hook"
|
||||
skip-mode-detection = false
|
||||
@@ -198,9 +201,12 @@ func TestTomlContents(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigFromToml(t *testing.T) {
|
||||
defer setGetLdConfigPathForTest()()
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
contents map[string]interface{}
|
||||
expectedError error
|
||||
expectedConfig *Config
|
||||
}{
|
||||
{
|
||||
@@ -226,13 +232,39 @@ func TestConfigFromToml(t *testing.T) {
|
||||
return c
|
||||
}(),
|
||||
},
|
||||
{
|
||||
description: "invalid ldconfig value raises error",
|
||||
contents: map[string]interface{}{
|
||||
"nvidia-container-cli": map[string]interface{}{
|
||||
"ldconfig": "/some/ldconfig/path",
|
||||
},
|
||||
},
|
||||
expectedError: errInvalidConfig,
|
||||
},
|
||||
{
|
||||
description: "feature allows ldconfig override",
|
||||
contents: map[string]interface{}{
|
||||
"nvidia-container-cli": map[string]interface{}{
|
||||
"ldconfig": "/some/ldconfig/path",
|
||||
},
|
||||
"features": map[string]interface{}{
|
||||
"allow-ldconfig-from-container": true,
|
||||
},
|
||||
},
|
||||
expectedConfig: func() *Config {
|
||||
c, _ := GetDefault()
|
||||
c.NVIDIAContainerCLIConfig.Ldconfig = "/some/ldconfig/path"
|
||||
c.Features.AllowLDConfigFromContainer = ptr(feature(true))
|
||||
return c
|
||||
}(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
tomlCfg := fromMap(tc.contents)
|
||||
config, err := tomlCfg.Config()
|
||||
require.NoError(t, err)
|
||||
require.ErrorIs(t, err, tc.expectedError)
|
||||
require.EqualValues(t, tc.expectedConfig, config)
|
||||
})
|
||||
}
|
||||
|
||||
24
internal/discover/compat_libs.go
Normal file
24
internal/discover/compat_libs.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package discover
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
)
|
||||
|
||||
// NewCUDACompatHookDiscoverer creates a discoverer for a enable-cuda-compat hook.
|
||||
// This hook is responsible for setting up CUDA compatibility in the container and depends on the host driver version.
|
||||
func NewCUDACompatHookDiscoverer(logger logger.Interface, nvidiaCDIHookPath string, driver *root.Driver) Discover {
|
||||
_, cudaVersionPattern := getCUDALibRootAndVersionPattern(logger, driver)
|
||||
var args []string
|
||||
if !strings.Contains(cudaVersionPattern, "*") {
|
||||
args = append(args, "--host-driver-version="+cudaVersionPattern)
|
||||
}
|
||||
|
||||
return CreateNvidiaCDIHook(
|
||||
nvidiaCDIHookPath,
|
||||
"enable-cuda-compat",
|
||||
args...,
|
||||
)
|
||||
}
|
||||
@@ -28,9 +28,15 @@ const (
|
||||
// NewCharDeviceLocator creates a Locator that can be used to find char devices at the specified root. A logger is
|
||||
// also specified.
|
||||
func NewCharDeviceLocator(opts ...Option) Locator {
|
||||
filter := assertCharDevice
|
||||
// TODO: We should have a better way to inject this logic than this envvar.
|
||||
if os.Getenv("__NVCT_TESTING_DEVICES_ARE_FILES") == "true" {
|
||||
filter = assertFile
|
||||
}
|
||||
|
||||
opts = append(opts,
|
||||
WithSearchPaths("", devRoot),
|
||||
WithFilter(assertCharDevice),
|
||||
WithFilter(filter),
|
||||
)
|
||||
return NewFileLocator(
|
||||
opts...,
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/system/nvmodules"
|
||||
)
|
||||
|
||||
// Driver represents a filesystem in which a set of drivers or devices is defined.
|
||||
@@ -125,3 +128,20 @@ func xdgDataDirs() []string {
|
||||
|
||||
return []string{"/usr/local/share", "/usr/share"}
|
||||
}
|
||||
|
||||
// LoadKmods loads the specified kernel modules in the driver root.
|
||||
// Errors in loading a module do not prevent other modules from being attempted.
|
||||
func (r *Driver) LoadKernelModules(moduleNames ...string) error {
|
||||
modules := nvmodules.New(
|
||||
nvmodules.WithLogger(r.logger),
|
||||
nvmodules.WithRoot(r.Root),
|
||||
)
|
||||
|
||||
var errs error
|
||||
for _, moduleName := range moduleNames {
|
||||
if err := modules.Load(moduleName); err != nil {
|
||||
errs = errors.Join(errs, fmt.Errorf("failed to load kernel module %q: %w", moduleName, err))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -33,3 +33,18 @@ func Resolve(filename string) (string, error) {
|
||||
|
||||
return os.Readlink(filename)
|
||||
}
|
||||
|
||||
// ForceCreate creates a specified symlink.
|
||||
// If a file (or empty directory) exists at the path it is removed.
|
||||
func ForceCreate(target string, link string) error {
|
||||
_, err := os.Lstat(link)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to get file info: %w", err)
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
if err := os.Remove(link); err != nil {
|
||||
return fmt.Errorf("failed to remove existing file: %w", err)
|
||||
}
|
||||
}
|
||||
return os.Symlink(target, link)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/modifier/cdi"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
|
||||
@@ -34,7 +35,7 @@ import (
|
||||
// NewCDIModifier creates an OCI spec modifier that determines the modifications to make based on the
|
||||
// CDI specifications available on the system. The NVIDIA_VISIBLE_DEVICES environment variable is
|
||||
// used to select the devices to include.
|
||||
func NewCDIModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Spec) (oci.SpecModifier, error) {
|
||||
func NewCDIModifier(logger logger.Interface, cfg *config.Config, driver *root.Driver, ociSpec oci.Spec) (oci.SpecModifier, error) {
|
||||
devices, err := getDevicesFromSpec(logger, ociSpec, cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get required devices from OCI specification: %v", err)
|
||||
@@ -50,7 +51,7 @@ func NewCDIModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Spe
|
||||
return nil, fmt.Errorf("requesting a CDI device with vendor 'runtime.nvidia.com' is not supported when requesting other CDI devices")
|
||||
}
|
||||
if len(automaticDevices) > 0 {
|
||||
automaticModifier, err := newAutomaticCDISpecModifier(logger, cfg, automaticDevices)
|
||||
automaticModifier, err := newAutomaticCDISpecModifier(logger, cfg, driver, automaticDevices)
|
||||
if err == nil {
|
||||
return automaticModifier, nil
|
||||
}
|
||||
@@ -163,9 +164,9 @@ func filterAutomaticDevices(devices []string) []string {
|
||||
return automatic
|
||||
}
|
||||
|
||||
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, devices []string) (oci.SpecModifier, error) {
|
||||
func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, driver *root.Driver, devices []string) (oci.SpecModifier, error) {
|
||||
logger.Debugf("Generating in-memory CDI specs for devices %v", devices)
|
||||
spec, err := generateAutomaticCDISpec(logger, cfg, devices)
|
||||
spec, err := generateAutomaticCDISpec(logger, cfg, driver, devices)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate CDI spec: %w", err)
|
||||
}
|
||||
@@ -180,7 +181,7 @@ func newAutomaticCDISpecModifier(logger logger.Interface, cfg *config.Config, de
|
||||
return cdiModifier, nil
|
||||
}
|
||||
|
||||
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devices []string) (spec.Interface, error) {
|
||||
func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, driver *root.Driver, devices []string) (spec.Interface, error) {
|
||||
cdilib, err := nvcdi.New(
|
||||
nvcdi.WithLogger(logger),
|
||||
nvcdi.WithNVIDIACDIHookPath(cfg.NVIDIACTKConfig.Path),
|
||||
@@ -192,6 +193,11 @@ func generateAutomaticCDISpec(logger logger.Interface, cfg *config.Config, devic
|
||||
return nil, fmt.Errorf("failed to construct CDI library: %w", err)
|
||||
}
|
||||
|
||||
// TODO: Consider moving this into the nvcdi API.
|
||||
if err := driver.LoadKernelModules(cfg.NVIDIAContainerRuntimeConfig.Modes.JitCDI.LoadKernelModules...); err != nil {
|
||||
logger.Warningf("Ignoring error(s) loading kernel modules: %v", err)
|
||||
}
|
||||
|
||||
identifiers := []string{}
|
||||
for _, device := range devices {
|
||||
_, _, id := parser.ParseDevice(device)
|
||||
|
||||
@@ -68,20 +68,10 @@ func NewCSVModifier(logger logger.Interface, cfg *config.Config, container image
|
||||
return nil, fmt.Errorf("failed to get CDI spec: %v", err)
|
||||
}
|
||||
|
||||
cdiModifier, err := cdi.New(
|
||||
return cdi.New(
|
||||
cdi.WithLogger(logger),
|
||||
cdi.WithSpec(spec.Raw()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct CDI modifier: %v", err)
|
||||
}
|
||||
|
||||
modifiers := Merge(
|
||||
nvidiaContainerRuntimeHookRemover{logger},
|
||||
cdiModifier,
|
||||
)
|
||||
|
||||
return modifiers, nil
|
||||
}
|
||||
|
||||
func checkRequirements(logger logger.Interface, image image.CUDA) error {
|
||||
|
||||
@@ -19,7 +19,6 @@ package modifier
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
testlog "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -74,66 +73,3 @@ func TestNewCSVModifier(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSVModifierRemovesHook(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
spec *specs.Spec
|
||||
expectedError error
|
||||
expectedSpec *specs.Spec
|
||||
}{
|
||||
{
|
||||
description: "modification removes existing nvidia-container-runtime-hook",
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-runtime-hook",
|
||||
Args: []string{"/path/to/nvidia-container-runtime-hook", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "modification removes existing nvidia-container-toolkit",
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-toolkit",
|
||||
Args: []string{"/path/to/nvidia-container-toolkit", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
m := nvidiaContainerRuntimeHookRemover{logger: logger}
|
||||
|
||||
err := m.Modify(tc.spec)
|
||||
if tc.expectedError != nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.Empty(t, tc.spec.Hooks.Prestart)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
@@ -35,7 +36,7 @@ import (
|
||||
// NVIDIA_GDRCOPY=enabled
|
||||
//
|
||||
// If not devices are selected, no changes are made.
|
||||
func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image image.CUDA) (oci.SpecModifier, error) {
|
||||
func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image image.CUDA, driver *root.Driver) (oci.SpecModifier, error) {
|
||||
if devices := image.VisibleDevicesFromEnvVar(); len(devices) == 0 {
|
||||
logger.Infof("No modification required; no devices requested")
|
||||
return nil, nil
|
||||
@@ -78,5 +79,24 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
|
||||
discoverers = append(discoverers, d)
|
||||
}
|
||||
|
||||
if !cfg.Features.AllowCUDACompatLibsFromContainer.IsEnabled() && !cfg.Features.DisableCUDACompatLibHook.IsEnabled() {
|
||||
compatLibHookDiscoverer := discover.NewCUDACompatHookDiscoverer(logger, cfg.NVIDIACTKConfig.Path, driver)
|
||||
discoverers = append(discoverers, compatLibHookDiscoverer)
|
||||
// For legacy mode, we also need to inject a hook to update the LDCache
|
||||
// after we have modifed the configuration.
|
||||
if cfg.NVIDIAContainerRuntimeConfig.Mode == "legacy" {
|
||||
ldcacheUpdateHookDiscoverer, err := discover.NewLDCacheUpdateHook(
|
||||
logger,
|
||||
discover.None{},
|
||||
cfg.NVIDIACTKConfig.Path,
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct ldcache update discoverer: %w", err)
|
||||
}
|
||||
discoverers = append(discoverers, ldcacheUpdateHookDiscoverer)
|
||||
}
|
||||
}
|
||||
|
||||
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...))
|
||||
}
|
||||
|
||||
@@ -33,6 +33,13 @@ type nvidiaContainerRuntimeHookRemover struct {
|
||||
|
||||
var _ oci.SpecModifier = (*nvidiaContainerRuntimeHookRemover)(nil)
|
||||
|
||||
// NewNvidiaContainerRuntimeHookRemover creates a modifier that removes any NVIDIA Container Runtime hooks from the provided spec.
|
||||
func NewNvidiaContainerRuntimeHookRemover(logger logger.Interface) oci.SpecModifier {
|
||||
return nvidiaContainerRuntimeHookRemover{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Modify removes any NVIDIA Container Runtime hooks from the provided spec
|
||||
func (m nvidiaContainerRuntimeHookRemover) Modify(spec *specs.Spec) error {
|
||||
if spec == nil {
|
||||
|
||||
@@ -22,14 +22,12 @@ import (
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
)
|
||||
|
||||
type list struct {
|
||||
modifiers []oci.SpecModifier
|
||||
}
|
||||
type List []oci.SpecModifier
|
||||
|
||||
// Merge merges a set of OCI specification modifiers as a list.
|
||||
// This can be used to compose modifiers.
|
||||
func Merge(modifiers ...oci.SpecModifier) oci.SpecModifier {
|
||||
var filteredModifiers []oci.SpecModifier
|
||||
var filteredModifiers List
|
||||
for _, m := range modifiers {
|
||||
if m == nil {
|
||||
continue
|
||||
@@ -37,19 +35,19 @@ func Merge(modifiers ...oci.SpecModifier) oci.SpecModifier {
|
||||
filteredModifiers = append(filteredModifiers, m)
|
||||
}
|
||||
|
||||
return list{
|
||||
modifiers: filteredModifiers,
|
||||
}
|
||||
return filteredModifiers
|
||||
}
|
||||
|
||||
// Modify applies a list of modifiers in sequence and returns on any errors encountered.
|
||||
func (m list) Modify(spec *specs.Spec) error {
|
||||
for _, mm := range m.modifiers {
|
||||
func (m List) Modify(spec *specs.Spec) error {
|
||||
for _, mm := range m {
|
||||
if mm == nil {
|
||||
continue
|
||||
}
|
||||
err := mm.Modify(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@ func TestMaintainSpec(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
inputSpecPath := filepath.Join(moduleRoot, "test/input", f)
|
||||
inputSpecPath := filepath.Join(moduleRoot, "tests/input", f)
|
||||
|
||||
spec := NewFileSpec(inputSpecPath).(*fileSpec)
|
||||
|
||||
_, err := spec.Load()
|
||||
require.NoError(t, err)
|
||||
|
||||
outputSpecPath := filepath.Join(moduleRoot, "test/output", f)
|
||||
outputSpecPath := filepath.Join(moduleRoot, "tests/output", f)
|
||||
spec.path = outputSpecPath
|
||||
spec.Flush()
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestGetFileList(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
description: "returns list of CSV files",
|
||||
root: "test/input/csv_samples/",
|
||||
root: "tests/input/csv_samples/",
|
||||
files: []string{
|
||||
"jetson.csv",
|
||||
"simple_wrong.csv",
|
||||
@@ -46,15 +46,15 @@ func TestGetFileList(t *testing.T) {
|
||||
},
|
||||
{
|
||||
description: "handles empty folder",
|
||||
root: "test/input/csv_samples/empty",
|
||||
root: "tests/input/csv_samples/empty",
|
||||
},
|
||||
{
|
||||
description: "handles non-existent folder",
|
||||
root: "test/input/csv_samples/NONEXISTENT",
|
||||
root: "tests/input/csv_samples/NONEXISTENT",
|
||||
},
|
||||
{
|
||||
description: "handles non-existent folder root",
|
||||
root: "/NONEXISTENT/test/input/csv_samples/",
|
||||
root: "/NONEXISTENT/tests/input/csv_samples/",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -75,42 +75,61 @@ func newSpecModifier(logger logger.Interface, cfg *config.Config, ociSpec oci.Sp
|
||||
}
|
||||
|
||||
mode := info.ResolveAutoMode(logger, cfg.NVIDIAContainerRuntimeConfig.Mode, image)
|
||||
modeModifier, err := newModeModifier(logger, mode, cfg, ociSpec, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// For CDI mode we make no additional modifications.
|
||||
if mode == "cdi" {
|
||||
return modeModifier, nil
|
||||
}
|
||||
|
||||
graphicsModifier, err := modifier.NewGraphicsModifier(logger, cfg, image, driver)
|
||||
// We update the mode here so that we can continue passing just the config to other functions.
|
||||
cfg.NVIDIAContainerRuntimeConfig.Mode = mode
|
||||
modeModifier, err := newModeModifier(logger, mode, cfg, driver, ociSpec, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
featureModifier, err := modifier.NewFeatureGatedModifier(logger, cfg, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var modifiers modifier.List
|
||||
for _, modifierType := range supportedModifierTypes(mode) {
|
||||
switch modifierType {
|
||||
case "mode":
|
||||
modifiers = append(modifiers, modeModifier)
|
||||
case "nvidia-hook-remover":
|
||||
modifiers = append(modifiers, modifier.NewNvidiaContainerRuntimeHookRemover(logger))
|
||||
case "graphics":
|
||||
graphicsModifier, err := modifier.NewGraphicsModifier(logger, cfg, image, driver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modifiers = append(modifiers, graphicsModifier)
|
||||
case "feature-gated":
|
||||
featureGatedModifier, err := modifier.NewFeatureGatedModifier(logger, cfg, image, driver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modifiers = append(modifiers, featureGatedModifier)
|
||||
}
|
||||
}
|
||||
|
||||
modifiers := modifier.Merge(
|
||||
modeModifier,
|
||||
graphicsModifier,
|
||||
featureModifier,
|
||||
)
|
||||
return modifiers, nil
|
||||
}
|
||||
|
||||
func newModeModifier(logger logger.Interface, mode string, cfg *config.Config, ociSpec oci.Spec, image image.CUDA) (oci.SpecModifier, error) {
|
||||
func newModeModifier(logger logger.Interface, mode string, cfg *config.Config, driver *root.Driver, ociSpec oci.Spec, image image.CUDA) (oci.SpecModifier, error) {
|
||||
switch mode {
|
||||
case "legacy":
|
||||
return modifier.NewStableRuntimeModifier(logger, cfg.NVIDIAContainerRuntimeHookConfig.Path), nil
|
||||
case "csv":
|
||||
return modifier.NewCSVModifier(logger, cfg, image)
|
||||
case "cdi":
|
||||
return modifier.NewCDIModifier(logger, cfg, ociSpec)
|
||||
return modifier.NewCDIModifier(logger, cfg, driver, ociSpec)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid runtime mode: %v", cfg.NVIDIAContainerRuntimeConfig.Mode)
|
||||
}
|
||||
|
||||
// supportedModifierTypes returns the modifiers supported for a specific runtime mode.
|
||||
func supportedModifierTypes(mode string) []string {
|
||||
switch mode {
|
||||
case "cdi":
|
||||
// For CDI mode we make no additional modifications.
|
||||
return []string{"nvidia-hook-remover", "mode"}
|
||||
case "csv":
|
||||
// For CSV mode we support mode and feature-gated modification.
|
||||
return []string{"nvidia-hook-remover", "feature-gated", "mode"}
|
||||
default:
|
||||
return []string{"feature-gated", "graphics", "mode"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/config"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/test"
|
||||
)
|
||||
|
||||
@@ -45,7 +46,7 @@ func TestMain(m *testing.M) {
|
||||
if err != nil {
|
||||
log.Fatalf("error in test setup: could not get module root: %v", err)
|
||||
}
|
||||
testBinPath := filepath.Join(moduleRoot, "test", "bin")
|
||||
testBinPath := filepath.Join(moduleRoot, "tests", "bin")
|
||||
|
||||
// Set the environment variables for the test
|
||||
os.Setenv("PATH", test.PrependToPath(testBinPath, moduleRoot))
|
||||
@@ -165,3 +166,181 @@ func TestFactoryMethod(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSpecModifier(t *testing.T) {
|
||||
logger, _ := testlog.NewNullLogger()
|
||||
driver := root.New(
|
||||
root.WithDriverRoot("/nvidia/driver/root"),
|
||||
)
|
||||
testCases := []struct {
|
||||
description string
|
||||
config *config.Config
|
||||
spec *specs.Spec
|
||||
expectedSpec *specs.Spec
|
||||
}{
|
||||
{
|
||||
description: "csv mode removes nvidia-container-runtime-hook",
|
||||
config: &config.Config{
|
||||
NVIDIAContainerRuntimeConfig: config.RuntimeConfig{
|
||||
Mode: "csv",
|
||||
},
|
||||
},
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-runtime-hook",
|
||||
Args: []string{"/path/to/nvidia-container-runtime-hook", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "csv mode removes nvidia-container-toolkit",
|
||||
config: &config.Config{
|
||||
NVIDIAContainerRuntimeConfig: config.RuntimeConfig{
|
||||
Mode: "csv",
|
||||
},
|
||||
},
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-toolkit",
|
||||
Args: []string{"/path/to/nvidia-container-toolkit", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "cdi mode removes nvidia-container-runtime-hook",
|
||||
config: &config.Config{
|
||||
NVIDIAContainerRuntimeConfig: config.RuntimeConfig{
|
||||
Mode: "cdi",
|
||||
},
|
||||
},
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-runtime-hook",
|
||||
Args: []string{"/path/to/nvidia-container-runtime-hook", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "cdi mode removes nvidia-container-toolkit",
|
||||
config: &config.Config{
|
||||
NVIDIAContainerRuntimeConfig: config.RuntimeConfig{
|
||||
Mode: "cdi",
|
||||
},
|
||||
},
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-toolkit",
|
||||
Args: []string{"/path/to/nvidia-container-toolkit", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "legacy mode keeps nvidia-container-runtime-hook",
|
||||
config: &config.Config{
|
||||
NVIDIAContainerRuntimeConfig: config.RuntimeConfig{
|
||||
Mode: "legacy",
|
||||
},
|
||||
},
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-runtime-hook",
|
||||
Args: []string{"/path/to/nvidia-container-runtime-hook", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-runtime-hook",
|
||||
Args: []string{"/path/to/nvidia-container-runtime-hook", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "legacy mode keeps nvidia-container-toolkit",
|
||||
config: &config.Config{
|
||||
NVIDIAContainerRuntimeConfig: config.RuntimeConfig{
|
||||
Mode: "legacy",
|
||||
},
|
||||
},
|
||||
spec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-toolkit",
|
||||
Args: []string{"/path/to/nvidia-container-toolkit", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSpec: &specs.Spec{
|
||||
Hooks: &specs.Hooks{
|
||||
Prestart: []specs.Hook{
|
||||
{
|
||||
Path: "/path/to/nvidia-container-toolkit",
|
||||
Args: []string{"/path/to/nvidia-container-toolkit", "prestart"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
spec := &oci.SpecMock{
|
||||
LoadFunc: func() (*specs.Spec, error) {
|
||||
return tc.spec, nil
|
||||
},
|
||||
}
|
||||
m, err := newSpecModifier(logger, tc.config, spec, driver)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = m.Modify(tc.spec)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, tc.expectedSpec, tc.spec)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package nvdevices
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -66,7 +65,7 @@ func New(opts ...Option) (*Interface, error) {
|
||||
if i.dryRun {
|
||||
i.mknoder = &mknodLogger{i.logger}
|
||||
} else {
|
||||
i.mknoder = &mknodUnix{}
|
||||
i.mknoder = &mknodUnix{i.logger}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
@@ -107,13 +106,6 @@ func (m *Interface) CreateNVIDIADevice(node string) error {
|
||||
// If a devRoot is configured, this is prepended to the path.
|
||||
func (m *Interface) createDeviceNode(path string, major int, minor int) error {
|
||||
path = filepath.Join(m.devRoot, path)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
m.logger.Infof("Skipping: %s already exists", path)
|
||||
return nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to stat %s: %v", path, err)
|
||||
}
|
||||
|
||||
return m.Mknode(path, major, minor)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package nvdevices
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
|
||||
@@ -36,9 +39,19 @@ func (m *mknodLogger) Mknode(path string, major, minor int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mknodUnix struct{}
|
||||
type mknodUnix struct {
|
||||
logger logger.Interface
|
||||
}
|
||||
|
||||
func (m *mknodUnix) Mknode(path string, major, minor int) error {
|
||||
// TODO: Ensure that the existing device node has the correct properties.
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
m.logger.Infof("Skipping: %s already exists", path)
|
||||
return nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to stat %s: %v", path, err)
|
||||
}
|
||||
|
||||
err := unix.Mknod(path, unix.S_IFCHR, int(unix.Mkdev(uint32(major), uint32(minor))))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -18,12 +18,13 @@ package engine
|
||||
|
||||
// Interface defines the API for a runtime config updater.
|
||||
type Interface interface {
|
||||
DefaultRuntime() string
|
||||
AddRuntime(string, string, bool) error
|
||||
Set(string, interface{})
|
||||
DefaultRuntime() string
|
||||
EnableCDI()
|
||||
GetRuntimeConfig(string) (RuntimeConfig, error)
|
||||
RemoveRuntime(string) error
|
||||
Save(string) (int64, error)
|
||||
GetRuntimeConfig(string) (RuntimeConfig, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
// RuntimeConfig defines the interface to query container runtime handler configuration
|
||||
|
||||
@@ -30,40 +30,40 @@ func (c *Config) AddRuntime(name string, path string, setAsDefault bool) error {
|
||||
}
|
||||
config := *c.Tree
|
||||
|
||||
config.Set("version", int64(2))
|
||||
config.Set("version", c.Version)
|
||||
|
||||
runtimeNamesForConfig := engine.GetLowLevelRuntimes(c)
|
||||
for _, r := range runtimeNamesForConfig {
|
||||
options := config.GetSubtreeByPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", r})
|
||||
options := config.GetSubtreeByPath([]string{"plugins", c.CRIRuntimePluginName, "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())
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name}, options.Copy())
|
||||
break
|
||||
}
|
||||
|
||||
if config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}) == nil {
|
||||
if config.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name}) == nil {
|
||||
c.Logger.Warningf("could not infer options from runtimes %v; using defaults", runtimeNamesForConfig)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "runtime_type"}, c.RuntimeType)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "runtime_root"}, "")
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "runtime_engine"}, "")
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "privileged_without_host_devices"}, false)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "runtime_type"}, c.RuntimeType)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "runtime_root"}, "")
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "runtime_engine"}, "")
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "privileged_without_host_devices"}, false)
|
||||
}
|
||||
|
||||
if len(c.ContainerAnnotations) > 0 {
|
||||
annotations, err := c.getRuntimeAnnotations([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "container_annotations"})
|
||||
annotations, err := c.getRuntimeAnnotations([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "container_annotations"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annotations = append(c.ContainerAnnotations, annotations...)
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "container_annotations"}, annotations)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "container_annotations"}, annotations)
|
||||
}
|
||||
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name, "options", "BinaryName"}, path)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name, "options", "BinaryName"}, path)
|
||||
|
||||
if setAsDefault {
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}, name)
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}, name)
|
||||
}
|
||||
|
||||
*c.Tree = config
|
||||
@@ -96,21 +96,21 @@ func (c *Config) getRuntimeAnnotations(path []string) ([]string, error) {
|
||||
return annotations, nil
|
||||
}
|
||||
|
||||
// Set sets the specified containerd option.
|
||||
func (c *Config) Set(key string, value interface{}) {
|
||||
config := *c.Tree
|
||||
config.SetPath([]string{"plugins", "io.containerd.grpc.v1.cri", key}, value)
|
||||
*c.Tree = config
|
||||
}
|
||||
|
||||
// DefaultRuntime returns the default runtime for the cri-o config
|
||||
func (c Config) DefaultRuntime() string {
|
||||
if runtime, ok := c.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}).(string); ok {
|
||||
if runtime, ok := c.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}).(string); ok {
|
||||
return runtime
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// EnableCDI sets the enable_cdi field in the Containerd config to true.
|
||||
func (c *Config) EnableCDI() {
|
||||
config := *c.Tree
|
||||
config.SetPath([]string{"plugins", c.CRIRuntimePluginName, "enable_cdi"}, true)
|
||||
*c.Tree = config
|
||||
}
|
||||
|
||||
// RemoveRuntime removes a runtime from the docker config
|
||||
func (c *Config) RemoveRuntime(name string) error {
|
||||
if c == nil || c.Tree == nil {
|
||||
@@ -119,14 +119,14 @@ func (c *Config) RemoveRuntime(name string) error {
|
||||
|
||||
config := *c.Tree
|
||||
|
||||
config.DeletePath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name})
|
||||
if runtime, ok := config.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"}).(string); ok {
|
||||
config.DeletePath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name})
|
||||
if runtime, ok := config.GetPath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"}).(string); ok {
|
||||
if runtime == name {
|
||||
config.DeletePath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "default_runtime_name"})
|
||||
config.DeletePath([]string{"plugins", c.CRIRuntimePluginName, "containerd", "default_runtime_name"})
|
||||
}
|
||||
}
|
||||
|
||||
runtimePath := []string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "runtimes", name}
|
||||
runtimePath := []string{"plugins", c.CRIRuntimePluginName, "containerd", "runtimes", name}
|
||||
for i := 0; i < len(runtimePath); i++ {
|
||||
if runtimes, ok := config.GetPath(runtimePath[:len(runtimePath)-i]).(*toml.Tree); ok {
|
||||
if len(runtimes.Keys()) == 0 {
|
||||
@@ -46,7 +46,7 @@ func TestAddRuntime(t *testing.T) {
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
`,
|
||||
@@ -195,24 +195,85 @@ func TestAddRuntime(t *testing.T) {
|
||||
SystemdCgroup = false
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: "empty v3 spec is supported",
|
||||
config: `
|
||||
version = 3
|
||||
`,
|
||||
expectedConfig: `
|
||||
version = 3
|
||||
[plugins]
|
||||
[plugins."io.containerd.cri.v1.runtime"]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test]
|
||||
privileged_without_host_devices = false
|
||||
runtime_engine = ""
|
||||
runtime_root = ""
|
||||
runtime_type = "io.containerd.runc.v2"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
`,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "v3 spec is supported",
|
||||
config: `
|
||||
version = 3
|
||||
[plugins]
|
||||
[plugins."io.containerd.cri.v1.runtime"]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options]
|
||||
BinaryName = "/usr/bin/runc"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
expectedConfig: `
|
||||
version = 3
|
||||
[plugins]
|
||||
[plugins."io.containerd.cri.v1.runtime"]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes]
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options]
|
||||
BinaryName = "/usr/bin/runc"
|
||||
SystemdCgroup = true
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test]
|
||||
privileged_without_host_devices = true
|
||||
runtime_engine = "engine"
|
||||
runtime_root = "root"
|
||||
runtime_type = "type"
|
||||
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.test.options]
|
||||
BinaryName = "/usr/bin/test"
|
||||
SystemdCgroup = true
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
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: cfg,
|
||||
}
|
||||
c, err := New(
|
||||
WithLogger(logger),
|
||||
WithConfigSource(toml.FromString(tc.config)),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = c.AddRuntime("test", "/usr/bin/test", tc.setAsDefault)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, expectedConfig.String(), cfg.String())
|
||||
require.EqualValues(t, expectedConfig.String(), c.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -299,13 +360,13 @@ func TestGetRuntimeConfig(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg, err := toml.Load(config)
|
||||
|
||||
c, err := New(
|
||||
WithLogger(logger),
|
||||
WithConfigSource(toml.FromString(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