Skip to content

Commit 30dc068

Browse files
fix drive name of volume at startup if needed (#873)
When a node-server starts, it syncs the drive states like drive name, capacity, make etc. This is because, the drive order wouldv'e changed during node restarts. In such scenarios, We need to also make sure to fix the drive name in corresponding volumes. Without this fix, the `kubectl directpv list volumes` command will show wrong drive names (if the node has restarted)
1 parent 42db733 commit 30dc068

File tree

3 files changed

+204
-1
lines changed

3 files changed

+204
-1
lines changed

pkg/device/sync_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// This file is part of MinIO DirectPV
2+
// Copyright (c) 2023 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package device
18+
19+
import (
20+
"testing"
21+
22+
directpvtypes "github.com/minio/directpv/pkg/apis/directpv.min.io/types"
23+
"github.com/minio/directpv/pkg/types"
24+
)
25+
26+
func newDrive(name string, totalCapacity int64, make, volume string) *types.Drive {
27+
drive := types.NewDrive(
28+
directpvtypes.DriveID(name+"-id"),
29+
types.DriveStatus{
30+
TotalCapacity: totalCapacity,
31+
Make: make,
32+
},
33+
directpvtypes.NodeID("nodeId"),
34+
directpvtypes.DriveName(name),
35+
directpvtypes.AccessTierDefault,
36+
)
37+
drive.AddVolumeFinalizer(volume)
38+
return drive
39+
}
40+
41+
func newTestDevice(name string, totalCapacity int64, dmname string) device {
42+
return device{
43+
TotalCapacity: totalCapacity,
44+
Device: Device{
45+
Name: name,
46+
DMName: dmname,
47+
},
48+
}
49+
}
50+
51+
func TestSyncDrive(t *testing.T) {
52+
testCases := []struct {
53+
drive *types.Drive
54+
device device
55+
updated bool
56+
expectedDriveName directpvtypes.DriveName
57+
expectedDriveCapacity int64
58+
expectedMake string
59+
}{
60+
{
61+
drive: newDrive("sda", 100, "dmname", "volume-1"),
62+
device: newTestDevice("sda", 100, "dmname"),
63+
updated: false,
64+
expectedDriveName: "sda",
65+
expectedDriveCapacity: 100,
66+
expectedMake: "dmname",
67+
},
68+
{
69+
drive: newDrive("sda", 100, "dmname", "volume-1"),
70+
device: newTestDevice("sda", 200, "dmname"),
71+
updated: true,
72+
expectedDriveName: "sda",
73+
expectedDriveCapacity: 200,
74+
expectedMake: "dmname",
75+
},
76+
{
77+
drive: newDrive("sda", 100, "dmname", "volume-1"),
78+
device: newTestDevice("sda", 100, "dmname-new"),
79+
updated: true,
80+
expectedDriveName: "sda",
81+
expectedDriveCapacity: 100,
82+
expectedMake: "dmname-new",
83+
},
84+
{
85+
drive: newDrive("sda", 100, "dmname", "volume-1"),
86+
device: newTestDevice("sdb", 100, "dmname"),
87+
updated: true,
88+
expectedDriveName: "sdb",
89+
expectedDriveCapacity: 100,
90+
expectedMake: "dmname",
91+
},
92+
{
93+
drive: newDrive("sda", 100, "dmname", "volume-1"),
94+
device: newTestDevice("sda", 100, "dmname"),
95+
updated: false,
96+
expectedDriveName: "sda",
97+
expectedDriveCapacity: 100,
98+
expectedMake: "dmname",
99+
},
100+
}
101+
102+
for _, testCase := range testCases {
103+
updated := syncDrive(testCase.drive, testCase.device)
104+
if updated != testCase.updated {
105+
t.Errorf("expected updated value: %v; but got %v", testCase.updated, updated)
106+
}
107+
if testCase.drive.GetDriveName() != testCase.expectedDriveName {
108+
t.Errorf("expected drive name: %v; but got %v", testCase.expectedDriveName, testCase.drive.GetDriveName())
109+
}
110+
if testCase.drive.Status.TotalCapacity != testCase.expectedDriveCapacity {
111+
t.Errorf("expected drive capacity: %v; but got %v", testCase.expectedDriveCapacity, testCase.drive.Status.TotalCapacity)
112+
}
113+
if testCase.drive.Status.Make != testCase.expectedMake {
114+
t.Errorf("expected drive make: %v; but got %v", testCase.expectedMake, testCase.drive.Status.Make)
115+
}
116+
}
117+
}

pkg/volume/event.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/minio/directpv/pkg/sys"
3131
"github.com/minio/directpv/pkg/types"
3232
"github.com/minio/directpv/pkg/xfs"
33+
apierrors "k8s.io/apimachinery/pkg/api/errors"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/runtime"
3536
"k8s.io/client-go/tools/cache"
@@ -77,15 +78,37 @@ func (handler *volumeEventHandler) ObjectType() runtime.Object {
7778
return &types.Volume{}
7879
}
7980

80-
func (handler *volumeEventHandler) Handle(ctx context.Context, _ controller.EventType, object runtime.Object) error {
81+
func (handler *volumeEventHandler) Handle(ctx context.Context, eventType controller.EventType, object runtime.Object) error {
8182
volume := object.(*types.Volume)
8283
if !volume.GetDeletionTimestamp().IsZero() {
8384
return handler.delete(ctx, volume)
8485
}
8586

87+
if eventType == controller.AddEvent {
88+
return sync(ctx, volume)
89+
}
90+
8691
return nil
8792
}
8893

94+
func sync(ctx context.Context, volume *types.Volume) error {
95+
drive, err := client.DriveClient().Get(ctx, string(volume.GetDriveID()), metav1.GetOptions{})
96+
if err != nil {
97+
if !apierrors.IsNotFound(err) {
98+
return err
99+
}
100+
return nil
101+
}
102+
driveName := drive.GetDriveName()
103+
if volume.GetDriveName() != driveName {
104+
volume.SetDriveName(driveName)
105+
_, err = client.VolumeClient().Update(ctx, volume, metav1.UpdateOptions{
106+
TypeMeta: types.NewVolumeTypeMeta(),
107+
})
108+
}
109+
return err
110+
}
111+
89112
func (handler *volumeEventHandler) delete(ctx context.Context, volume *types.Volume) error {
90113
if !volume.IsReleased() {
91114
return fmt.Errorf("volume %v must be released before cleaning up", volume.Name)

pkg/volume/event_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,66 @@ func TestAbnormalDeleteEventHandle(t *testing.T) {
188188
t.Errorf("[%s] DeleteVolumeHandle expected to fail but succeeded", newObj.Name)
189189
}
190190
}
191+
192+
func TestSync(t *testing.T) {
193+
newDrive := func(name, driveName, volume string) *types.Drive {
194+
drive := types.NewDrive(
195+
directpvtypes.DriveID(name),
196+
types.DriveStatus{
197+
TotalCapacity: 100,
198+
Make: "make",
199+
},
200+
directpvtypes.NodeID("nodeId"),
201+
directpvtypes.DriveName(driveName),
202+
directpvtypes.AccessTierDefault,
203+
)
204+
drive.AddVolumeFinalizer(volume)
205+
return drive
206+
}
207+
208+
newVolume := func(name, driveID, driveName string) *types.Volume {
209+
volume := types.NewVolume(
210+
name,
211+
"fsuuid",
212+
"nodeId",
213+
directpvtypes.DriveID(driveID),
214+
directpvtypes.DriveName(driveName),
215+
100,
216+
)
217+
volume.Status.DataPath = "datapath"
218+
return volume
219+
}
220+
221+
drive := newDrive("drive-1", "sda", "volume-1")
222+
volume := newVolume("volume-1", "drive-1", "sdb")
223+
objects := []runtime.Object{
224+
drive,
225+
volume,
226+
}
227+
clientset := types.NewExtFakeClientset(clientsetfake.NewSimpleClientset(objects...))
228+
client.SetDriveInterface(clientset.DirectpvLatest().DirectPVDrives())
229+
client.SetVolumeInterface(clientset.DirectpvLatest().DirectPVVolumes())
230+
231+
volume, err := client.VolumeClient().Get(context.TODO(), volume.Name, metav1.GetOptions{
232+
TypeMeta: types.NewVolumeTypeMeta(),
233+
})
234+
if err != nil {
235+
t.Fatalf("Volume (%s) not found; %v", volume.Name, err)
236+
}
237+
238+
err = sync(context.TODO(), volume)
239+
if err != nil {
240+
t.Fatalf("unable to sync; %v", err)
241+
}
242+
243+
volume, err = client.VolumeClient().Get(context.TODO(), volume.Name, metav1.GetOptions{
244+
TypeMeta: types.NewVolumeTypeMeta(),
245+
})
246+
if err != nil {
247+
t.Fatalf("Volume (%s) not found after sync; %v", volume.Name, err)
248+
}
249+
250+
if volume.GetDriveName() != drive.GetDriveName() {
251+
t.Fatalf("expected drive name: %v; but got %v", drive.GetDriveName(), volume.GetDriveName())
252+
}
253+
}

0 commit comments

Comments
 (0)