diff --git a/pkg/driver/nri_hooks.go b/pkg/driver/nri_hooks.go index 12ed6f8a..d82b54aa 100644 --- a/pkg/driver/nri_hooks.go +++ b/pkg/driver/nri_hooks.go @@ -30,6 +30,7 @@ import ( metav1apply "k8s.io/client-go/applyconfigurations/meta/v1" resourceapply "k8s.io/client-go/applyconfigurations/resource/v1beta1" "k8s.io/klog/v2" + "k8s.io/utils/set" ) // NRI hooks into the container runtime, the lifecycle of the Pod seen here is local to the runtime @@ -66,9 +67,15 @@ func (np *NetworkDriver) CreateContainer(_ context.Context, pod *api.PodSandbox, return nil, nil, nil } // Containers only cares about the RDMA char devices + devPaths := set.Set[string]{} adjust := &api.ContainerAdjustment{} for _, config := range podConfig { for _, dev := range config.RDMADevice.DevChars { + // do not insert the same path multiple times + if devPaths.Has(dev.Path) { + continue + } + devPaths.Insert(dev.Path) // TODO check the file permissions and uid and gid fields adjust.AddDevice(&api.LinuxDevice{ Path: dev.Path, diff --git a/pkg/driver/nri_hooks_test.go b/pkg/driver/nri_hooks_test.go new file mode 100644 index 00000000..0467e462 --- /dev/null +++ b/pkg/driver/nri_hooks_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2024 Google LLC + +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 + + https://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 driver + +import ( + "context" + "testing" + + "github.com/containerd/nri/pkg/api" + "k8s.io/apimachinery/pkg/types" +) + +func TestCreateContainerNoDuplicateDevices(t *testing.T) { + np := &NetworkDriver{ + podConfigStore: NewPodConfigStore(), + } + + podUID := types.UID("test-pod") + pod := &api.PodSandbox{ + Uid: string(podUID), + Name: "test-pod", + Namespace: "test-ns", + } + ctr := &api.Container{ + Name: "test-container", + } + + // Setup pod config with duplicate RDMA devices + rdmaDevChars := []LinuxDevice{ + {Path: "/dev/infiniband/uverbs0", Type: "c", Major: 231, Minor: 192}, + } + podConfig := PodConfig{ + RDMADevice: RDMAConfig{ + DevChars: rdmaDevChars, + }, + } + np.podConfigStore.Set(podUID, "eth0", podConfig) + np.podConfigStore.Set(podUID, "eth1", podConfig) + + adjust, _, err := np.CreateContainer(context.Background(), pod, ctr) + if err != nil { + t.Fatalf("CreateContainer failed: %v", err) + } + + if len(adjust.Linux.Devices) != 1 { + t.Errorf("CreateContainer should not adjust the same device multiple times\n%v", adjust.Linux.Devices) + } +} diff --git a/pkg/driver/pod_device_config_test.go b/pkg/driver/pod_device_config_test.go index ce8669c4..5f615341 100644 --- a/pkg/driver/pod_device_config_test.go +++ b/pkg/driver/pod_device_config_test.go @@ -292,3 +292,57 @@ func TestPodConfigStore_DeleteClaim(t *testing.T) { }) } } + +func TestPodConfigStore_NoDuplicateDevices(t *testing.T) { + store := NewPodConfigStore() + podUID := types.UID("test-pod-uid-1") + deviceName1 := "eth0" + config1 := PodConfig{ + Network: apis.NetworkConfig{ + Interface: apis.InterfaceConfig{Name: "eth0-pod"}, + }, + RDMADevice: RDMAConfig{ + LinkDev: "mlx5_0", + DevChars: []LinuxDevice{{ + Path: "/dev/infiniband/rdma_cm", + }, { + Path: "/dev/infiniband/uverbs1", + }}, + }, + } + deviceName2 := "eth1" + config2 := PodConfig{ + Network: apis.NetworkConfig{ + Interface: apis.InterfaceConfig{Name: "eth2-pod"}, + }, + RDMADevice: RDMAConfig{ + LinkDev: "mlx5_1", + DevChars: []LinuxDevice{{ + Path: "/dev/infiniband/rdma_cm", + }, { + Path: "/dev/infiniband/uverbs2", + }}, + }, + } + + // Set the same device config multiple times + store.Set(podUID, deviceName1, config1) + store.Set(podUID, deviceName2, config2) + store.Set(podUID, deviceName1, config1) + + podConfigs, found := store.GetPodConfigs(podUID) + if !found { + t.Fatalf("GetPodConfigs() did not find configs for podUID, expected found") + } + + if len(podConfigs) != 2 { + t.Errorf("Expected 2 device config, but got %d", len(podConfigs)) + } + + if _, ok := podConfigs[deviceName1]; !ok { + t.Errorf("Device %s not found in pod configs", deviceName2) + } + if _, ok := podConfigs[deviceName2]; !ok { + t.Errorf("Device %s not found in pod configs", deviceName2) + } +}