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 := &registerProbe{
   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  }