Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1961942: use /disk/by-id name as symlink name #235

Merged
merged 1 commit into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
139 changes: 77 additions & 62 deletions pkg/diskmaker/diskmaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,77 @@ func (d *DiskMaker) Run(stop <-chan struct{}) {
}
}

func (d *DiskMaker) createSymLink(deviceNameLocation DiskLocation, symLinkDirPath string) {

var symLinkSource, symLinkTarget string
var isSymLinkedByDeviceName bool
if deviceNameLocation.diskID != "" {
symLinkSource = deviceNameLocation.diskID
symLinkTarget = getSymlinkTarget(deviceNameLocation.diskID, symLinkDirPath)
Comment on lines +132 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when someone updates to a new LSO version with symlinks already present from the old LSO? Looking at the code, does it create new symlinks, ignoring the old ones? What happens to already existing PVs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think new symlinks will be created. Old ones will still be used. We are checking for existing symlinks in line 155 below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this scenario. Updated the results in the PR description. Looks good.

} else {
symLinkSource = deviceNameLocation.diskNamePath
symLinkTarget = getSymlinkTarget(deviceNameLocation.diskNamePath, symLinkDirPath)
isSymLinkedByDeviceName = true
}

// get PV creation lock which checks for existing symlinks to this device
pvLock, pvLocked, existingSymlinks, err := internal.GetPVCreationLock(
symLinkSource,
d.symlinkLocation,
)

unlockFunc := func() {
err := pvLock.Unlock()
if err != nil {
klog.Errorf("failed to unlock device: %+v", err)
}
}

defer unlockFunc()

if len(existingSymlinks) > 0 { // already claimed, fail silently
return
} else if err != nil || !pvLocked { // locking failed for some other reasion
klog.Errorf("not symlinking, could not get lock: %v", err)
return
}

err = os.MkdirAll(symLinkDirPath, 0755)
if err != nil {
msg := fmt.Sprintf("error creating symlink dir %s: %v", symLinkDirPath, err)
e := NewEvent(ErrorFindingMatchingDisk, msg, symLinkTarget)
d.eventSync.Report(e, d.localVolume)
klog.Errorf(msg)
return
}

if fileExists(symLinkTarget) {
klog.V(4).Infof("symlink %s already exists", symLinkTarget)
return
}

err = os.Symlink(symLinkSource, symLinkTarget)
if err != nil {
msg := fmt.Sprintf("error creating symlink %s: %v", symLinkTarget, err)
e := NewEvent(ErrorFindingMatchingDisk, msg, symLinkSource)
d.eventSync.Report(e, d.localVolume)
klog.Errorf(msg)
return
}

if isSymLinkedByDeviceName {
msg := fmt.Sprintf("created symlink on device name %s with no disk/by-id. device name might not persist on reboot", symLinkSource)
e := NewEvent(SymLinkedOnDeviceName, msg, symLinkSource)
d.eventSync.Report(e, d.localVolume)
klog.Warningf(msg)
return
}

successMsg := fmt.Sprintf("found matching disk %s with id %s", deviceNameLocation.diskNamePath, deviceNameLocation.diskID)
e := NewSuccessEvent(FoundMatchingDisk, successMsg, deviceNameLocation.diskNamePath)
d.eventSync.Report(e, d.localVolume)
}

func (d *DiskMaker) symLinkDisks(diskConfig *DiskConfig) {
// run command lsblk --all --noheadings --pairs --output "KNAME,PKNAME,TYPE,MOUNTPOINT"
// the reason we are using KNAME instead of NAME is because for lvm disks(and may be others)
Expand Down Expand Up @@ -192,69 +263,8 @@ func (d *DiskMaker) symLinkDisks(diskConfig *DiskConfig) {

for storageClass, deviceArray := range deviceMap {
for _, deviceNameLocation := range deviceArray {
//
symLinkDirPath := path.Join(d.symlinkLocation, storageClass)
baseDeviceName := filepath.Base(deviceNameLocation.diskNamePath)
symLinkPath := path.Join(symLinkDirPath, baseDeviceName)

// get PV creation lock which checks for existing symlinks to this device
pvLock, pvLocked, existingSymlinks, err := internal.GetPVCreationLock(
deviceNameLocation.diskNamePath,
d.symlinkLocation,
)
unlockFunc := func() {
err := pvLock.Unlock()
if err != nil {
klog.Errorf("failed to unlock device: %+v", err)
}
}
defer unlockFunc()
if len(existingSymlinks) > 0 { // already claimed, fail silently
e := NewEvent(DeviceSymlinkExists, "this device is already matched by another LocalVolume or LocalVolumeSet", symLinkPath)
d.eventSync.Report(e, d.localVolume)
unlockFunc()
continue
} else if err != nil || !pvLocked { // locking failed for some other reasion
klog.Errorf("not symlinking, could not get lock: %v", err)
unlockFunc()
continue
}

err = os.MkdirAll(symLinkDirPath, 0755)
if err != nil {
msg := fmt.Sprintf("error creating symlink dir %s: %v", symLinkDirPath, err)
e := NewEvent(ErrorFindingMatchingDisk, msg, symLinkPath)
d.eventSync.Report(e, d.localVolume)
unlockFunc()
klog.Errorf(msg)
continue
}

if fileExists(symLinkPath) {
unlockFunc()
klog.V(4).Infof("symlink %s already exists", symLinkPath)
continue
}

var symLinkErr error
if deviceNameLocation.diskID != "" {
klog.V(3).Infof("symlinking to %s to %s", deviceNameLocation.diskID, symLinkPath)
symLinkErr = os.Symlink(deviceNameLocation.diskID, symLinkPath)
} else {
klog.V(3).Infof("symlinking to %s to %s", deviceNameLocation.diskNamePath, symLinkPath)
symLinkErr = os.Symlink(deviceNameLocation.diskNamePath, symLinkPath)
}
if symLinkErr != nil {
msg := fmt.Sprintf("error creating symlink %s: %v", symLinkPath, symLinkErr)
e := NewEvent(ErrorFindingMatchingDisk, msg, deviceNameLocation.diskNamePath)
d.eventSync.Report(e, d.localVolume)
klog.Errorf(msg)
}
// unlock before proceeding
unlockFunc()
successMsg := fmt.Sprintf("found matching disk %s", baseDeviceName)
e := NewSuccessEvent(FoundMatchingDisk, successMsg, deviceNameLocation.diskNamePath)
d.eventSync.Report(e, d.localVolume)
d.createSymLink(deviceNameLocation, symLinkDirPath)
}
}

Expand Down Expand Up @@ -387,3 +397,8 @@ func fileExists(filename string) bool {
}
return true
}

func getSymlinkTarget(device, symLinkDirPath string) string {
baseDeviceName := filepath.Base(device)
return path.Join(symLinkDirPath, baseDeviceName)
}
69 changes: 64 additions & 5 deletions pkg/diskmaker/diskmaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"

localv1 "github.com/openshift/local-storage-operator/pkg/apis/local/v1"
)

Expand Down Expand Up @@ -33,11 +36,7 @@ func TestFindMatchingDisk(t *testing.T) {
}

func TestLoadConfig(t *testing.T) {
tempDir, err := ioutil.TempDir("", "diskmaker")
if err != nil {
t.Fatalf("error creating temp directory : %v", err)
}

tempDir := createTmpDir(t, "", "diskmaker")
defer os.RemoveAll(tempDir)
diskConfig := &DiskConfig{
Disks: map[string]*Disks{
Expand Down Expand Up @@ -78,6 +77,37 @@ func TestLoadConfig(t *testing.T) {
}
}

func TestCreateSymLinkByDeviceID(t *testing.T) {
tmpSymLinkTargetDir := createTmpDir(t, "", "target")
fakeDisk := createTmpFile(t, "", "diskName")
fakeDiskByID := createTmpFile(t, "", "diskID")
defer os.RemoveAll(tmpSymLinkTargetDir)
defer os.Remove(fakeDisk.Name())
defer os.Remove(fakeDiskByID.Name())

d := getFakeDiskMaker("", tmpSymLinkTargetDir)
diskLocation := DiskLocation{fakeDisk.Name(), fakeDiskByID.Name()}

d.createSymLink(diskLocation, tmpSymLinkTargetDir)

// assert that target symlink is created for disk ID when both disk name and disk by-id are available
assert.Truef(t, hasFile(t, tmpSymLinkTargetDir, "diskID"), "failed to find symlink with disk ID in %s directory", tmpSymLinkTargetDir)
}

func TestCreateSymLinkByDeviceName(t *testing.T) {
tmpSymLinkTargetDir := createTmpDir(t, "", "target")
fakeDisk := createTmpFile(t, "", "diskName")
defer os.Remove(fakeDisk.Name())
defer os.RemoveAll(tmpSymLinkTargetDir)

d := getFakeDiskMaker("", tmpSymLinkTargetDir)
diskLocation := DiskLocation{fakeDisk.Name(), ""}
d.createSymLink(diskLocation, tmpSymLinkTargetDir)

// assert that target symlink is created for disk name when no disk ID is available
assert.Truef(t, hasFile(t, tmpSymLinkTargetDir, "diskName"), "failed to find symlink with disk name in %s directory", tmpSymLinkTargetDir)
}

func getFakeDiskMaker(configLocation, symlinkLocation string) *DiskMaker {
d := &DiskMaker{configLocation: configLocation, symlinkLocation: symlinkLocation}
d.apiClient = &MockAPIUpdater{}
Expand All @@ -90,3 +120,32 @@ func getDeiveIDs() []string {
"/dev/disk/by-id/xyz",
}
}

func createTmpDir(t *testing.T, dir, prefix string) string {
tmpDir, err := ioutil.TempDir(dir, prefix)
if err != nil {
t.Fatalf("error creating temp directory : %v", err)
}
return tmpDir
}

func createTmpFile(t *testing.T, dir, pattern string) *os.File {
tmpFile, err := ioutil.TempFile(dir, pattern)
if err != nil {
t.Fatalf("error creating tmp file: %v", err)
}
return tmpFile
}

func hasFile(t *testing.T, dir, file string) bool {
files, err := ioutil.ReadDir(dir)
if err != nil {
t.Fatalf("error reading directory %s : %v", dir, err)
}
for _, f := range files {
if strings.Contains(f.Name(), file) {
return true
}
}
return false
}
1 change: 1 addition & 0 deletions pkg/diskmaker/event_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
ErrorListingDeviceID = "ErrorListingDeviceID"
ErrorFindingMatchingDisk = "ErrorFindingMatchingDisk"
ErrorCreatingSymLink = "ErrorCreatingSymLink"
SymLinkedOnDeviceName = "SymlinkedOnDeivceName"

FoundMatchingDisk = "FoundMatchingDisk"
DeviceSymlinkExists = "DeviceSymlinkExists"
Expand Down