github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/udevprobe_test.go (about) 1 /* 2 Copyright 2018 OpenEBS Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package probe 18 19 import ( 20 "errors" 21 "sync" 22 "testing" 23 24 apis "github.com/openebs/node-disk-manager/api/v1alpha1" 25 "github.com/openebs/node-disk-manager/blockdevice" 26 "github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller" 27 libudevwrapper "github.com/openebs/node-disk-manager/pkg/udev" 28 "github.com/stretchr/testify/assert" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 ) 31 32 type alwaysTrueFilter struct{} 33 34 func (nf *alwaysTrueFilter) Start() {} 35 36 func (nf *alwaysTrueFilter) Include(fakeDiskInfo *blockdevice.BlockDevice) bool { 37 return true 38 } 39 40 func (nf *alwaysTrueFilter) Exclude(fakeDiskInfo *blockdevice.BlockDevice) bool { 41 return true 42 } 43 44 func mockOsDiskToAPI() (apis.BlockDevice, error) { 45 mockOsDiskDetails, err := libudevwrapper.MockDiskDetails() 46 if err != nil { 47 return apis.BlockDevice{}, err 48 } 49 fakeDetails := apis.DeviceDetails{ 50 Model: mockOsDiskDetails.Model, 51 Serial: mockOsDiskDetails.Serial, 52 Vendor: mockOsDiskDetails.Vendor, 53 } 54 fakeObj := apis.DeviceSpec{ 55 Path: mockOsDiskDetails.DevNode, 56 Details: fakeDetails, 57 Partitioned: controller.NDMNotPartitioned, 58 } 59 60 devLinks := make([]apis.DeviceDevLink, 0) 61 if len(mockOsDiskDetails.ByIdDevLinks) != 0 { 62 byIdLinks := apis.DeviceDevLink{ 63 Kind: "by-id", 64 Links: mockOsDiskDetails.ByIdDevLinks, 65 } 66 devLinks = append(devLinks, byIdLinks) 67 } 68 if len(mockOsDiskDetails.ByPathDevLinks) != 0 { 69 byPathLinks := apis.DeviceDevLink{ 70 Kind: "by-path", 71 Links: mockOsDiskDetails.ByPathDevLinks, 72 } 73 devLinks = append(devLinks, byPathLinks) 74 } 75 fakeObj.DevLinks = devLinks 76 77 fakeTypeMeta := metav1.TypeMeta{ 78 Kind: controller.NDMBlockDeviceKind, 79 APIVersion: controller.NDMVersion, 80 } 81 fakeObjectMeta := metav1.ObjectMeta{ 82 Labels: make(map[string]string), 83 Name: mockOsDiskDetails.Uid, 84 } 85 fakeDiskStatus := apis.DeviceStatus{ 86 State: controller.NDMActive, 87 ClaimState: apis.BlockDeviceUnclaimed, 88 } 89 fakeDr := apis.BlockDevice{ 90 TypeMeta: fakeTypeMeta, 91 ObjectMeta: fakeObjectMeta, 92 Spec: fakeObj, 93 Status: fakeDiskStatus, 94 } 95 return fakeDr, nil 96 } 97 98 func TestFillDiskDetails(t *testing.T) { 99 mockOsDiskDetails, err := libudevwrapper.MockDiskDetails() 100 if err != nil { 101 t.Fatal(err) 102 } 103 uProbe := udevProbe{} 104 actualDiskInfo := &blockdevice.BlockDevice{} 105 actualDiskInfo.SysPath = mockOsDiskDetails.SysPath 106 uProbe.FillBlockDeviceDetails(actualDiskInfo) 107 expectedDiskInfo := &blockdevice.BlockDevice{} 108 expectedDiskInfo.SysPath = mockOsDiskDetails.SysPath 109 expectedDiskInfo.DevPath = mockOsDiskDetails.DevNode 110 expectedDiskInfo.DeviceAttributes.Model = mockOsDiskDetails.Model 111 expectedDiskInfo.DeviceAttributes.Serial = mockOsDiskDetails.Serial 112 expectedDiskInfo.DeviceAttributes.Vendor = mockOsDiskDetails.Vendor 113 expectedDiskInfo.DeviceAttributes.WWN = mockOsDiskDetails.Wwn 114 expectedDiskInfo.PartitionInfo.PartitionTableType = mockOsDiskDetails.PartTableType 115 expectedDiskInfo.DeviceAttributes.IDType = mockOsDiskDetails.IdType 116 if len(mockOsDiskDetails.ByIdDevLinks) > 0 { 117 expectedDiskInfo.DevLinks = append(expectedDiskInfo.DevLinks, blockdevice.DevLink{ 118 Kind: libudevwrapper.BY_ID_LINK, 119 Links: mockOsDiskDetails.ByIdDevLinks, 120 }) 121 } 122 if len(mockOsDiskDetails.ByPathDevLinks) > 0 { 123 expectedDiskInfo.DevLinks = append(expectedDiskInfo.DevLinks, blockdevice.DevLink{ 124 Kind: libudevwrapper.BY_PATH_LINK, 125 Links: mockOsDiskDetails.ByPathDevLinks, 126 }) 127 } 128 if len(mockOsDiskDetails.SymLinks) > 0 { 129 expectedDiskInfo.DevLinks = append(expectedDiskInfo.DevLinks, blockdevice.DevLink{ 130 Kind: libudevwrapper.SYMLINK, 131 Links: mockOsDiskDetails.SymLinks, 132 }) 133 } 134 135 // The devlinks are compared separately as the ordering of devlinks can be different in some systems 136 // eg: ubuntu 20.04 in github actions 137 assert.True(t, compareDevLinks(expectedDiskInfo.DevLinks, actualDiskInfo.DevLinks)) 138 139 // The devlinks are made nil since they are already compared 140 expectedDiskInfo.DevLinks = nil 141 actualDiskInfo.DevLinks = nil 142 143 assert.Equal(t, expectedDiskInfo, actualDiskInfo) 144 } 145 146 func TestUdevProbe(t *testing.T) { 147 mockOsDiskDetails, err := libudevwrapper.MockDiskDetails() 148 if err != nil { 149 t.Fatal(err) 150 } 151 fakeHostName := "node-name" 152 fakeNdmClient := CreateFakeClient(t) 153 probes := make([]*controller.Probe, 0) 154 filters := make([]*controller.Filter, 0) 155 nodeAttributes := make(map[string]string) 156 nodeAttributes[controller.HostNameKey] = fakeHostName 157 mutex := &sync.Mutex{} 158 fakeController := &controller.Controller{ 159 Clientset: fakeNdmClient, 160 Mutex: mutex, 161 Probes: probes, 162 Filters: filters, 163 NodeAttributes: nodeAttributes, 164 BDHierarchy: make(blockdevice.Hierarchy), 165 } 166 udevprobe := newUdevProbe(fakeController) 167 var pi controller.ProbeInterface = udevprobe 168 newRegisterProbe := ®isterProbe{ 169 priority: 1, 170 name: "udev probe", 171 state: true, 172 pi: pi, 173 controller: fakeController, 174 } 175 176 newRegisterProbe.register() 177 178 // Add one filter 179 filter := &alwaysTrueFilter{} 180 filter1 := &controller.Filter{ 181 Name: "filter1", 182 State: true, 183 Interface: filter, 184 } 185 fakeController.AddNewFilter(filter1) 186 probeEvent := &ProbeEvent{ 187 Controller: fakeController, 188 } 189 eventmsg := make([]*blockdevice.BlockDevice, 0) 190 deviceDetails := &blockdevice.BlockDevice{} 191 deviceDetails.SysPath = mockOsDiskDetails.SysPath 192 eventmsg = append(eventmsg, deviceDetails) 193 eventDetails := controller.EventMessage{ 194 Action: libudevwrapper.UDEV_ACTION_ADD, 195 Devices: eventmsg, 196 } 197 probeEvent.addBlockDeviceEvent(eventDetails) 198 // Retrieve disk resource 199 uuid, ok := generateUUID(*deviceDetails) 200 cdr1, err1 := fakeController.GetBlockDevice(uuid) 201 fakeDr, err := mockOsDiskToAPI() 202 if err != nil { 203 t.Fatal(err) 204 } 205 fakeDr.Name = uuid 206 fakeDr.ObjectMeta.Labels[controller.KubernetesHostNameLabel] = fakeController.NodeAttributes[controller.HostNameKey] 207 fakeDr.ObjectMeta.Labels[controller.NDMDeviceTypeKey] = "blockdevice" 208 fakeDr.ObjectMeta.Labels[controller.NDMManagedKey] = controller.TrueString 209 tests := map[string]struct { 210 actualDisk *apis.BlockDevice 211 expectedDisk apis.BlockDevice 212 actualError error 213 expectedError error 214 }{ 215 "add event for resource with 'fake-disk-uid' uuid": {actualDisk: cdr1, expectedDisk: fakeDr, actualError: err1, expectedError: nil}, 216 } 217 for name, test := range tests { 218 t.Run(name, func(t *testing.T) { 219 if !ok { 220 assert.Nil(t, cdr1) 221 } else { 222 compareBlockDevice(t, test.expectedDisk, *test.actualDisk) 223 assert.Equal(t, test.expectedError, test.actualError) 224 } 225 }) 226 } 227 } 228 229 func TestNewUdevProbeForFillDiskDetails(t *testing.T) { 230 // Creating the actual udev probe struct 231 mockDisk, err := libudevwrapper.MockDiskDetails() 232 if err != nil { 233 t.Fatal(err) 234 } 235 sysPath := mockDisk.SysPath 236 udev, err := libudevwrapper.NewUdev() 237 if err != nil { 238 t.Fatal(err) 239 } 240 actualUdevProbe := &udevProbe{ 241 udev: udev, 242 } 243 actualUdevProbe.udevDevice, err = actualUdevProbe.udev.NewDeviceFromSysPath(sysPath) 244 if err != nil { 245 t.Fatal(err) 246 } 247 udevProbeError := errors.New("unable to create Udevice object for null struct struct_udev_device") 248 249 // expected cases 250 expectedUdevProbe1, expectedError1 := newUdevProbeForFillDiskDetails(sysPath) 251 expectedUdevProbe2, expectedError2 := newUdevProbeForFillDiskDetails("") 252 tests := map[string]struct { 253 actualUdevProbe *udevProbe 254 expectedUdevProbe *udevProbe 255 actualError error 256 expectedError error 257 }{ 258 "udev probe with correct syspath": {actualUdevProbe: actualUdevProbe, expectedUdevProbe: expectedUdevProbe1, actualError: nil, expectedError: expectedError1}, 259 "udev probe with empty syspath": {actualUdevProbe: nil, expectedUdevProbe: expectedUdevProbe2, actualError: udevProbeError, expectedError: expectedError2}, 260 } 261 for name, test := range tests { 262 t.Run(name, func(t *testing.T) { 263 assert.Equal(t, test.expectedUdevProbe, test.actualUdevProbe) 264 assert.Equal(t, test.expectedError, test.actualError) 265 }) 266 } 267 } 268 269 func compareDevLinks(devLink1, devLink2 []blockdevice.DevLink) bool { 270 if len(devLink1) != len(devLink2) { 271 return false 272 } 273 cmp := true 274 for i := 0; i < len(devLink1); i++ { 275 cmp = cmp && unorderedEqual(devLink1[0].Links, devLink2[0].Links) 276 } 277 return cmp 278 } 279 280 func unorderedEqual(first, second []string) bool { 281 if len(first) != len(second) { 282 return false 283 } 284 exists := make(map[string]bool) 285 for _, value := range first { 286 exists[value] = true 287 } 288 for _, value := range second { 289 if !exists[value] { 290 return false 291 } 292 } 293 return true 294 }