Skip to content

Commit 74df227

Browse files
author
Cheng Pan
committed
Implement CSI migration logic for volume resize
* Using PVC annotation for sychronication between in-tree resizer and extternal resizer * Modify enqueue condition for resizer migration * Add unit tests
1 parent 7493e6d commit 74df227

File tree

17 files changed

+1384
-18
lines changed

17 files changed

+1384
-18
lines changed

Gopkg.lock

+20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/controller/controller.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,27 @@ func (ctrl *resizeController) updatePVC(oldObj, newObj interface{}) {
123123
newSize := newPVC.Spec.Resources.Requests[v1.ResourceStorage]
124124
oldSize := oldPVC.Spec.Resources.Requests[v1.ResourceStorage]
125125

126+
newResizerName := newPVC.Annotations[util.VolumeResizerKey]
127+
oldResizerName := oldPVC.Annotations[util.VolumeResizerKey]
128+
126129
// We perform additional checks to avoid double processing of PVCs, as we will also receive Update event when:
127130
// 1. Administrator or users may introduce other changes(such as add labels, modify annotations, etc.)
128131
// unrelated to volume resize.
129132
// 2. Informer will resync and send Update event periodically without any changes.
130-
if newSize.Cmp(oldSize) > 0 {
133+
//
134+
// We add the PVC into work queue when the new size is larger then the old size
135+
// or when the resizer name changes. This is needed for CSI migration for the follow two cases:
136+
//
137+
// 1. First time a migrated PVC is expanded:
138+
// It does not yet have the annotation because annotation is only added by in-tree resizer when it receives a volume
139+
// expansion request. So first update event that will be received by external-resizer will be ignored because it won't
140+
// know how to support resizing of a "un-annotated" in-tree PVC. When in-tree resizer does add the annotation, a second
141+
// update even will be received and we add the pvc to workqueue. If annotation matches the registered driver name in
142+
// csi_resizer object, we proceeds with expansion internally or we discard the PVC.
143+
// 2. An already expanded in-tree PVC:
144+
// An in-tree PVC is resized with in-tree resizer. And later, CSI migration is turned on and resizer name is updated from
145+
// in-tree resizer name to CSI driver name.
146+
if newSize.Cmp(oldSize) > 0 || newResizerName != oldResizerName {
131147
ctrl.addPVC(newObj)
132148
}
133149
}
@@ -249,7 +265,7 @@ func (ctrl *resizeController) pvcNeedResize(pvc *v1.PersistentVolumeClaim) bool
249265

250266
// pvNeedResize returns true if a pv supports and also requests resize.
251267
func (ctrl *resizeController) pvNeedResize(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) bool {
252-
if !ctrl.resizer.CanSupport(pv) {
268+
if !ctrl.resizer.CanSupport(pv, pvc) {
253269
klog.V(4).Infof("Resizer %q doesn't support PV %q", ctrl.name, pv.Name)
254270
return false
255271
}

pkg/controller/controller_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func TestController(t *testing.T) {
6565
NodeResize: true,
6666
},
6767
} {
68-
client := csi.NewMockClient(test.NodeResize, true, true)
68+
client := csi.NewMockClient("mock", test.NodeResize, true, true)
6969
driverName, _ := client.GetDriverName(context.TODO())
7070

7171
initialObjects := []runtime.Object{}

pkg/csi/mock_client.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package csi
33
import "context"
44

55
func NewMockClient(
6+
name string,
67
supportsNodeResize bool,
78
supportsControllerResize bool,
89
supportsPluginControllerService bool) *MockClient {
910
return &MockClient{
10-
name: "mock",
11+
name: name,
1112
supportsNodeResize: supportsNodeResize,
1213
supportsControllerResize: supportsControllerResize,
1314
supportsPluginControllerService: supportsPluginControllerService,

pkg/resizer/csi_resizer.go

+43-6
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ import (
2323
"time"
2424

2525
"github.com/kubernetes-csi/external-resizer/pkg/csi"
26+
"github.com/kubernetes-csi/external-resizer/pkg/util"
2627

2728
"k8s.io/api/core/v1"
2829
"k8s.io/apimachinery/pkg/api/resource"
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/client-go/informers"
3132
"k8s.io/client-go/kubernetes"
33+
34+
csitranslationlib "k8s.io/csi-translation-lib"
3235
"k8s.io/klog"
3336
)
3437

@@ -107,7 +110,18 @@ func (r *csiResizer) Name() string {
107110
return r.name
108111
}
109112

110-
func (r *csiResizer) CanSupport(pv *v1.PersistentVolume) bool {
113+
// CanSupport returns whether the PV is supported by resizer
114+
// Resizer will resize the volume if it is CSI volume or is migration enabled in-tree volume
115+
func (r *csiResizer) CanSupport(pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) bool {
116+
resizerName := pvc.Annotations[util.VolumeResizerKey]
117+
// resizerName will be CSI driver name when CSI migration is enabled
118+
// otherwise, it will be in-tree plugin name
119+
// r.name is the CSI driver name, return true only when they match
120+
// and the CSI driver is migrated
121+
if csitranslationlib.IsMigratedCSIDriverByName(r.name) && resizerName == r.name {
122+
return true
123+
}
124+
111125
source := pv.Spec.CSI
112126
if source == nil {
113127
klog.V(4).Infof("PV %s is not a CSI volume, skip it", pv.Name)
@@ -120,14 +134,32 @@ func (r *csiResizer) CanSupport(pv *v1.PersistentVolume) bool {
120134
return true
121135
}
122136

137+
// Resize resizes the persistence volume given request size
138+
// It supports both CSI volume and migrated in-tree volume
123139
func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quantity) (resource.Quantity, bool, error) {
124140
oldSize := pv.Spec.Capacity[v1.ResourceStorage]
125141

126-
source := pv.Spec.CSI
127-
if source == nil {
128-
return oldSize, false, errors.New("not a CSI volume")
142+
var volumeID string
143+
var source *v1.CSIPersistentVolumeSource
144+
if pv.Spec.CSI != nil {
145+
// handle CSI volume
146+
source = pv.Spec.CSI
147+
volumeID = source.VolumeHandle
148+
} else {
149+
if csitranslationlib.IsMigratedCSIDriverByName(r.name) {
150+
// handle migrated in-tree volume
151+
csiPV, err := csitranslationlib.TranslateInTreePVToCSI(pv)
152+
if err != nil {
153+
return oldSize, false, fmt.Errorf("failed to translate persistent volume: %v", err)
154+
}
155+
source = csiPV.Spec.CSI
156+
volumeID = source.VolumeHandle
157+
} else {
158+
// non-migrated in-tree volume
159+
return oldSize, false, fmt.Errorf("volume %v is not migrated to CSI", pv.Name)
160+
}
129161
}
130-
volumeID := source.VolumeHandle
162+
131163
if len(volumeID) == 0 {
132164
return oldSize, false, errors.New("empty volume handle")
133165
}
@@ -148,7 +180,12 @@ func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quanti
148180
if err != nil {
149181
return oldSize, nodeResizeRequired, err
150182
}
151-
return *resource.NewQuantity(newSizeBytes, resource.BinarySI), nodeResizeRequired, err
183+
184+
if pv.Spec.VolumeMode == nil || *pv.Spec.VolumeMode == v1.PersistentVolumeFilesystem {
185+
return *resource.NewQuantity(newSizeBytes, resource.BinarySI), nodeResizeRequired, err
186+
}
187+
188+
return *resource.NewQuantity(newSizeBytes, resource.BinarySI), false, err
152189
}
153190

154191
func getDriverName(client csi.Client, timeout time.Duration) (string, error) {

0 commit comments

Comments
 (0)