github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/eventhandler_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 "fmt" 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 29 "github.com/stretchr/testify/assert" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/client-go/kubernetes/scheme" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 ndmFakeClientset "sigs.k8s.io/controller-runtime/pkg/client/fake" 34 ) 35 36 var ( 37 ignoreDiskDevPath = "/dev/sdZ" 38 fakeHostName = "node-name" 39 fakeModel = "fake-disk-model" 40 fakeSerial = "fake-disk-serial" 41 fakeVendor = "fake-disk-vendor" 42 fakeWWN = "fake-WWN" 43 fakeBDType = "blockdevice" 44 ) 45 46 var ( 47 fakeBD1 = blockdevice.BlockDevice{ 48 Identifier: blockdevice.Identifier{ 49 DevPath: "/dev/sdX", 50 }, 51 DeviceAttributes: blockdevice.DeviceAttribute{ 52 WWN: fakeWWN, 53 Serial: fakeSerial, 54 }, 55 } 56 fakeBD2 = blockdevice.BlockDevice{ 57 Identifier: blockdevice.Identifier{ 58 DevPath: ignoreDiskDevPath, 59 }, 60 DeviceAttributes: blockdevice.DeviceAttribute{ 61 WWN: fakeWWN, 62 }, 63 } 64 ) 65 66 var ( 67 fakeBD1Uuid, _ = generateUUID(fakeBD1) 68 fakeBD2Uuid, _ = generateUUID(fakeBD2) 69 ) 70 71 func mockEmptyBlockDeviceCr() apis.BlockDevice { 72 fakeBDr := apis.BlockDevice{} 73 fakeObjectMeta := metav1.ObjectMeta{ 74 Labels: make(map[string]string), 75 Name: fakeBD1Uuid, 76 } 77 fakeTypeMeta := metav1.TypeMeta{ 78 Kind: controller.NDMBlockDeviceKind, 79 APIVersion: controller.NDMVersion, 80 } 81 fakeBDr.ObjectMeta = fakeObjectMeta 82 fakeBDr.TypeMeta = fakeTypeMeta 83 fakeBDr.Status.State = controller.NDMActive 84 fakeBDr.Status.ClaimState = apis.BlockDeviceUnclaimed 85 fakeBDr.Spec.DevLinks = make([]apis.DeviceDevLink, 0) 86 return fakeBDr 87 } 88 89 func CreateFakeClient(t *testing.T) client.Client { 90 91 deviceR := &apis.BlockDevice{ 92 ObjectMeta: metav1.ObjectMeta{ 93 Labels: make(map[string]string), 94 Name: "dummy-blockdevice", 95 }, 96 } 97 98 deviceList := &apis.BlockDeviceList{ 99 TypeMeta: metav1.TypeMeta{ 100 Kind: "BlockDevice", 101 APIVersion: "", 102 }, 103 } 104 105 s := scheme.Scheme 106 s.AddKnownTypes(apis.GroupVersion, deviceR) 107 s.AddKnownTypes(apis.GroupVersion, deviceList) 108 109 fakeNdmClient := ndmFakeClientset.NewFakeClient() 110 if fakeNdmClient == nil { 111 fmt.Println("NDMClient is not created") 112 } 113 return fakeNdmClient 114 } 115 116 type fakeFilter struct{} 117 118 func (nf *fakeFilter) Start() {} 119 120 func (nf *fakeFilter) Include(fakeDiskInfo *blockdevice.BlockDevice) bool { 121 return true 122 } 123 124 func (nf *fakeFilter) Exclude(fakeDiskInfo *blockdevice.BlockDevice) bool { 125 return fakeDiskInfo.DevPath != ignoreDiskDevPath 126 } 127 128 func TestAddBlockDeviceEvent(t *testing.T) { 129 fakeNdmClient := CreateFakeClient(t) 130 nodeAttributes := make(map[string]string) 131 nodeAttributes[controller.HostNameKey] = fakeHostName 132 fakeController := &controller.Controller{ 133 Clientset: fakeNdmClient, 134 Mutex: &sync.Mutex{}, 135 Filters: make([]*controller.Filter, 0), 136 Probes: make([]*controller.Probe, 0), 137 NodeAttributes: nodeAttributes, 138 BDHierarchy: make(blockdevice.Hierarchy), 139 } 140 //add one filter 141 filter := &fakeFilter{} 142 filter1 := &controller.Filter{ 143 Name: "filter1", 144 State: true, 145 Interface: filter, 146 } 147 fakeController.AddNewFilter(filter1) 148 // add one probe 149 testProbe := &fakeProbe{} 150 probe1 := &controller.Probe{ 151 Name: "probe1", 152 State: true, 153 Interface: testProbe, 154 } 155 fakeController.AddNewProbe(probe1) 156 157 probeEvent := &ProbeEvent{ 158 Controller: fakeController, 159 } 160 // blockdevice-1 details 161 eventmsg := make([]*blockdevice.BlockDevice, 0) 162 eventmsg = append(eventmsg, &fakeBD1) 163 // blockdevice-2 details 164 eventmsg = append(eventmsg, &fakeBD2) 165 // Creating one event message 166 eventDetails := controller.EventMessage{ 167 Action: libudevwrapper.UDEV_ACTION_ADD, 168 Devices: eventmsg, 169 } 170 probeEvent.addBlockDeviceEvent(eventDetails) 171 // Retrieve disk resource 172 cdr1, err1 := fakeController.GetBlockDevice(fakeBD1Uuid) 173 174 // Retrieve disk resource 175 cdr2, _ := fakeController.GetBlockDevice(fakeBD2Uuid) 176 if cdr2 != nil { 177 t.Error("resource with ignoreDiskUuid should not be present in etcd") 178 } 179 // Create one fake disk resource 180 fakeDr := mockEmptyBlockDeviceCr() 181 fakeDr.ObjectMeta.Labels[controller.KubernetesHostNameLabel] = fakeController.NodeAttributes[controller.HostNameKey] 182 fakeDr.ObjectMeta.Labels[controller.NDMDeviceTypeKey] = fakeBDType 183 fakeDr.ObjectMeta.Labels[controller.NDMManagedKey] = controller.TrueString 184 fakeDr.Spec.Details.Model = fakeModel 185 fakeDr.Spec.Details.Serial = fakeSerial 186 fakeDr.Spec.Details.Vendor = fakeVendor 187 fakeDr.Spec.Partitioned = controller.NDMNotPartitioned 188 fakeDr.Spec.Path = "/dev/sdX" 189 190 tests := map[string]struct { 191 actualDisk apis.BlockDevice 192 expectedDisk apis.BlockDevice 193 actualError error 194 expectedError error 195 }{ 196 "resource with 'fake-disk-uid' uuid for create resource": {actualDisk: *cdr1, expectedDisk: fakeDr, actualError: err1, expectedError: nil}, 197 } 198 for name, test := range tests { 199 t.Run(name, func(t *testing.T) { 200 compareBlockDevice(t, test.expectedDisk, test.actualDisk) 201 assert.Equal(t, test.expectedError, test.actualError) 202 }) 203 } 204 } 205 206 func TestDeleteDiskEvent(t *testing.T) { 207 fakeNdmClient := CreateFakeClient(t) 208 probes := make([]*controller.Probe, 0) 209 nodeAttributes := make(map[string]string) 210 nodeAttributes[controller.HostNameKey] = fakeHostName 211 mutex := &sync.Mutex{} 212 fakeController := &controller.Controller{ 213 Clientset: fakeNdmClient, 214 Probes: probes, 215 Mutex: mutex, 216 NodeAttributes: nodeAttributes, 217 BDHierarchy: blockdevice.Hierarchy{ 218 "/dev/sdX": fakeBD1, 219 }, 220 } 221 222 // Create one fake block device resource 223 fakeBDr := mockEmptyBlockDeviceCr() 224 fakeBDr.ObjectMeta.Labels[controller.KubernetesHostNameLabel] = fakeController.NodeAttributes[controller.HostNameKey] 225 fakeBDr.ObjectMeta.Labels[controller.NDMDeviceTypeKey] = fakeBDType 226 fakeBDr.ObjectMeta.Labels[controller.NDMManagedKey] = controller.TrueString 227 fakeController.CreateBlockDevice(fakeBDr) 228 229 probeEvent := &ProbeEvent{ 230 Controller: fakeController, 231 } 232 eventmsg := make([]*blockdevice.BlockDevice, 0) 233 eventmsg = append(eventmsg, &fakeBD1) 234 eventDetails := controller.EventMessage{ 235 Action: libudevwrapper.UDEV_ACTION_REMOVE, 236 Devices: eventmsg, 237 } 238 probeEvent.deleteBlockDeviceEvent(eventDetails) 239 240 // Retrieve resources 241 bdR1, err1 := fakeController.GetBlockDevice(fakeBD1Uuid) 242 243 fakeBDr.Status.State = controller.NDMInactive 244 tests := map[string]struct { 245 actualBD apis.BlockDevice 246 expectedBD apis.BlockDevice 247 actualError error 248 expectedError error 249 }{ 250 "remove resource with 'fake-disk-uid' uuid": {actualBD: *bdR1, expectedBD: fakeBDr, actualError: err1, expectedError: nil}, 251 } 252 for name, test := range tests { 253 t.Run(name, func(t *testing.T) { 254 compareBlockDevice(t, test.expectedBD, test.actualBD) 255 assert.Equal(t, test.expectedError, test.actualError) 256 }) 257 } 258 } 259 260 // compareBlockDevice is the custom blockdevice comparison function. Only those values that need to be checked 261 // for equality will be checked here. Resource version field will not be checked as it 262 // will be updated on every write. Refer https://github.com/kubernetes-sigs/controller-runtime/pull/620 263 func compareBlockDevice(t *testing.T, bd1, bd2 apis.BlockDevice) { 264 assert.Equal(t, bd1.Name, bd2.Name) 265 assert.Equal(t, bd1.Labels, bd2.Labels) 266 // devlinks will be compared separately 267 assert.Equal(t, len(bd1.Spec.DevLinks), len(bd2.Spec.DevLinks)) 268 if len(bd1.Spec.DevLinks) != len(bd2.Spec.DevLinks) { 269 assert.Fail(t, "Devlinks, expected: %+v \n actual: %+v", bd1.Spec.DevLinks, bd2.Spec.DevLinks) 270 return 271 } 272 // compare each set of devlinks 273 for i := 0; i < len(bd1.Spec.DevLinks); i++ { 274 assert.True(t, unorderedEqual(bd1.Spec.DevLinks[i].Links, bd2.Spec.DevLinks[i].Links)) 275 } 276 // links will be made nil since they are already compared 277 bd1.Spec.DevLinks = nil 278 bd2.Spec.DevLinks = nil 279 280 assert.Equal(t, bd1.Spec, bd2.Spec) 281 assert.Equal(t, bd1.Status, bd2.Status) 282 283 } 284 285 // compareBlockDeviceList is the custom comparison function for blockdevice list 286 func compareBlockDeviceList(t *testing.T, bdList1, bdList2 apis.BlockDeviceList) { 287 assert.Equal(t, len(bdList1.Items), len(bdList2.Items)) 288 for i := 0; i < len(bdList2.Items); i++ { 289 compareBlockDevice(t, bdList1.Items[i], bdList2.Items[i]) 290 } 291 } 292 293 func TestIsParentOrSlaveDevice(t *testing.T) { 294 tests := map[string]struct { 295 bd blockdevice.BlockDevice 296 erroredDevices []string 297 want bool 298 }{ 299 "no devices in errored state": { 300 bd: blockdevice.BlockDevice{ 301 Identifier: blockdevice.Identifier{ 302 DevPath: "/dev/sda1", 303 }, 304 DependentDevices: blockdevice.DependentBlockDevices{ 305 Parent: "/dev/sda", 306 Partitions: nil, 307 Holders: nil, 308 Slaves: nil, 309 }, 310 }, 311 erroredDevices: nil, 312 want: false, 313 }, 314 "multiple devices in errored state with no matching BD": { 315 bd: blockdevice.BlockDevice{ 316 Identifier: blockdevice.Identifier{ 317 DevPath: "/dev/sda1", 318 }, 319 DependentDevices: blockdevice.DependentBlockDevices{ 320 Parent: "/dev/sda", 321 Partitions: nil, 322 Holders: nil, 323 Slaves: nil, 324 }, 325 }, 326 erroredDevices: []string{"/dev/sdb", "/dev/sdc"}, 327 want: false, 328 }, 329 "one device in errored state that is the parent of the given BD": { 330 bd: blockdevice.BlockDevice{ 331 Identifier: blockdevice.Identifier{ 332 DevPath: "/dev/sda1", 333 }, 334 DependentDevices: blockdevice.DependentBlockDevices{ 335 Parent: "/dev/sda", 336 Partitions: nil, 337 Holders: nil, 338 Slaves: nil, 339 }, 340 }, 341 erroredDevices: []string{"/dev/sda"}, 342 want: true, 343 }, 344 "one device in errored state that that the BD is a slave to": { 345 bd: blockdevice.BlockDevice{ 346 Identifier: blockdevice.Identifier{ 347 DevPath: "/dev/dm-0", 348 }, 349 DependentDevices: blockdevice.DependentBlockDevices{ 350 Parent: "", 351 Partitions: nil, 352 Holders: nil, 353 Slaves: []string{"/dev/sda", "/dev/sdb"}, 354 }, 355 }, 356 erroredDevices: []string{"/dev/sda", "/dev/sdc"}, 357 want: true, 358 }, 359 } 360 for name, tt := range tests { 361 t.Run(name, func(t *testing.T) { 362 got := isParentOrSlaveDevice(tt.bd, tt.erroredDevices) 363 assert.Equal(t, tt.want, got) 364 }) 365 } 366 }