Handle case where symlink already exists in create-symlinks hook

Signed-off-by: Christopher Desiniotis <cdesiniotis@nvidia.com>
This commit is contained in:
Christopher Desiniotis 2024-10-16 15:36:10 -07:00
parent 23f1ba3e93
commit 92779e71b3
No known key found for this signature in database
GPG Key ID: 603C8E544D789A89
2 changed files with 75 additions and 2 deletions

View File

@ -17,6 +17,7 @@
package symlinks package symlinks
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -25,6 +26,7 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger" "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci" "github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
) )
@ -121,8 +123,17 @@ func (m command) run(c *cli.Context, cfg *config) error {
func (m command) createLink(containerRoot string, targetPath string, link string) error { func (m command) createLink(containerRoot string, targetPath string, link string) error {
linkPath := filepath.Join(containerRoot, link) linkPath := filepath.Join(containerRoot, link)
exists, err := doesLinkExist(targetPath, linkPath)
if err != nil {
return fmt.Errorf("failed to check if link exists: %w", err)
}
if exists {
m.logger.Debugf("Link %s already exists", linkPath)
return nil
}
m.logger.Infof("Symlinking %v to %v", linkPath, targetPath) m.logger.Infof("Symlinking %v to %v", linkPath, targetPath)
err := os.MkdirAll(filepath.Dir(linkPath), 0755) err = os.MkdirAll(filepath.Dir(linkPath), 0755)
if err != nil { if err != nil {
return fmt.Errorf("failed to create directory: %v", err) return fmt.Errorf("failed to create directory: %v", err)
} }
@ -133,3 +144,19 @@ func (m command) createLink(containerRoot string, targetPath string, link string
return nil 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) {
currentTarget, err := symlinks.Resolve(link)
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("failed to resolve existing symlink %s: %w", link, err)
}
if currentTarget == target {
return true, nil
}
return true, fmt.Errorf("unexpected link target: %s", currentTarget)
}

View File

@ -12,6 +12,36 @@ import (
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks" "github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
) )
func TestDoesLinkExist(t *testing.T) {
tmpDir := t.TempDir()
require.NoError(
t,
makeFs(tmpDir,
dirOrLink{path: "/a/b/c", target: "d"},
dirOrLink{path: "/a/b/e", target: "/a/b/f"},
),
)
exists, err := doesLinkExist("d", filepath.Join(tmpDir, "/a/b/c"))
require.NoError(t, err)
require.True(t, exists)
exists, err = doesLinkExist("/a/b/f", filepath.Join(tmpDir, "/a/b/e"))
require.NoError(t, err)
require.True(t, exists)
_, err = doesLinkExist("different-target", filepath.Join(tmpDir, "/a/b/c"))
require.Error(t, err)
_, err = doesLinkExist("/a/b/d", filepath.Join(tmpDir, "/a/b/c"))
require.Error(t, err)
exists, err = doesLinkExist("foo", filepath.Join(tmpDir, "/a/b/does-not-exist"))
require.NoError(t, err)
require.False(t, exists)
}
func TestCreateLinkRelativePath(t *testing.T) { func TestCreateLinkRelativePath(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
hostRoot := filepath.Join(tmpDir, "/host-root/") hostRoot := filepath.Join(tmpDir, "/host-root/")
@ -56,12 +86,28 @@ func TestCreateLinkAlreadyExists(t *testing.T) {
// nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so // nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so
err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so") err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so")
require.Error(t, err) require.NoError(t, err)
target, err := symlinks.Resolve(filepath.Join(containerRoot, "lib/libfoo.so")) target, err := symlinks.Resolve(filepath.Join(containerRoot, "lib/libfoo.so"))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "libfoo.so.1", target) 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)
}
type dirOrLink struct { type dirOrLink struct {
path string path string
target string target string