Skip to content

Commit 3e7fa61

Browse files
authored
Merge pull request kubernetes#77516 from gnufied/implement-resize-secrets
Add a new field for storing volume expansion secrets
2 parents 88eea7e + 6939329 commit 3e7fa61

File tree

15 files changed

+1560
-1297
lines changed

15 files changed

+1560
-1297
lines changed

api/openapi-spec/swagger.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/persistentvolume/util.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,23 @@ func DropDisabledFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.Persist
2828
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && !volumeModeInUse(oldPVSpec) {
2929
pvSpec.VolumeMode = nil
3030
}
31+
32+
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandCSIVolumes) && !hasExpansionSecrets(oldPVSpec) {
33+
if pvSpec.CSI != nil {
34+
pvSpec.CSI.ControllerExpandSecretRef = nil
35+
}
36+
}
37+
}
38+
39+
func hasExpansionSecrets(oldPVSpec *api.PersistentVolumeSpec) bool {
40+
if oldPVSpec == nil || oldPVSpec.CSI == nil {
41+
return false
42+
}
43+
44+
if oldPVSpec.CSI.ControllerExpandSecretRef != nil {
45+
return true
46+
}
47+
return false
3148
}
3249

3350
func volumeModeInUse(oldPVSpec *api.PersistentVolumeSpec) bool {

pkg/api/persistentvolume/util_test.go

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ func TestDropDisabledFields(t *testing.T) {
3232
return &api.PersistentVolumeSpec{VolumeMode: mode}
3333
}
3434

35+
secretRef := &api.SecretReference{
36+
Name: "expansion-secret",
37+
Namespace: "default",
38+
}
39+
3540
modeBlock := api.PersistentVolumeBlock
3641

3742
tests := map[string]struct {
38-
oldSpec *api.PersistentVolumeSpec
39-
newSpec *api.PersistentVolumeSpec
40-
expectOldSpec *api.PersistentVolumeSpec
41-
expectNewSpec *api.PersistentVolumeSpec
42-
blockEnabled bool
43+
oldSpec *api.PersistentVolumeSpec
44+
newSpec *api.PersistentVolumeSpec
45+
expectOldSpec *api.PersistentVolumeSpec
46+
expectNewSpec *api.PersistentVolumeSpec
47+
blockEnabled bool
48+
csiExpansionEnabled bool
4349
}{
4450
"disabled block clears new": {
4551
blockEnabled: false,
@@ -84,11 +90,47 @@ func TestDropDisabledFields(t *testing.T) {
8490
oldSpec: specWithMode(&modeBlock),
8591
expectOldSpec: specWithMode(&modeBlock),
8692
},
93+
"disabled csi expansion clears secrets": {
94+
csiExpansionEnabled: false,
95+
newSpec: specWithCSISecrets(secretRef),
96+
expectNewSpec: specWithCSISecrets(nil),
97+
oldSpec: nil,
98+
expectOldSpec: nil,
99+
},
100+
"enabled csi expansion preserve secrets": {
101+
csiExpansionEnabled: true,
102+
newSpec: specWithCSISecrets(secretRef),
103+
expectNewSpec: specWithCSISecrets(secretRef),
104+
oldSpec: nil,
105+
expectOldSpec: nil,
106+
},
107+
"enabled csi expansion preserve secrets when both old and new have it": {
108+
csiExpansionEnabled: true,
109+
newSpec: specWithCSISecrets(secretRef),
110+
expectNewSpec: specWithCSISecrets(secretRef),
111+
oldSpec: specWithCSISecrets(secretRef),
112+
expectOldSpec: specWithCSISecrets(secretRef),
113+
},
114+
"disabled csi expansion old pv had secrets": {
115+
csiExpansionEnabled: false,
116+
newSpec: specWithCSISecrets(secretRef),
117+
expectNewSpec: specWithCSISecrets(secretRef),
118+
oldSpec: specWithCSISecrets(secretRef),
119+
expectOldSpec: specWithCSISecrets(secretRef),
120+
},
121+
"enabled csi expansion preserves secrets when old pv did not had secrets": {
122+
csiExpansionEnabled: true,
123+
newSpec: specWithCSISecrets(secretRef),
124+
expectNewSpec: specWithCSISecrets(secretRef),
125+
oldSpec: specWithCSISecrets(nil),
126+
expectOldSpec: specWithCSISecrets(nil),
127+
},
87128
}
88129

89130
for name, tc := range tests {
90131
t.Run(name, func(t *testing.T) {
91132
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, tc.blockEnabled)()
133+
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandCSIVolumes, tc.csiExpansionEnabled)()
92134

93135
DropDisabledFields(tc.newSpec, tc.oldSpec)
94136
if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
@@ -100,3 +142,19 @@ func TestDropDisabledFields(t *testing.T) {
100142
})
101143
}
102144
}
145+
146+
func specWithCSISecrets(secret *api.SecretReference) *api.PersistentVolumeSpec {
147+
pvSpec := &api.PersistentVolumeSpec{
148+
PersistentVolumeSource: api.PersistentVolumeSource{
149+
CSI: &api.CSIPersistentVolumeSource{
150+
Driver: "com.google.gcepd",
151+
VolumeHandle: "foobar",
152+
},
153+
},
154+
}
155+
156+
if secret != nil {
157+
pvSpec.CSI.ControllerExpandSecretRef = secret
158+
}
159+
return pvSpec
160+
}

pkg/api/v1/persistentvolume/util.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ func VisitPVSecretNames(pv *corev1.PersistentVolume, visitor Visitor) bool {
119119
return false
120120
}
121121
}
122+
if source.CSI.ControllerExpandSecretRef != nil {
123+
if !visitor(source.CSI.ControllerExpandSecretRef.Namespace, source.CSI.ControllerExpandSecretRef.Name, false /* kubeletVisible */) {
124+
return false
125+
}
126+
}
127+
122128
if source.CSI.NodePublishSecretRef != nil {
123129
if !visitor(source.CSI.NodePublishSecretRef.Namespace, source.CSI.NodePublishSecretRef.Name, true /* kubeletVisible */) {
124130
return false

pkg/api/v1/persistentvolume/util_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,17 @@ func TestPVSecrets(t *testing.T) {
142142
NodeStageSecretRef: &corev1.SecretReference{
143143
Name: "Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
144144
Namespace: "csi"}}}}},
145+
{Spec: corev1.PersistentVolumeSpec{
146+
ClaimRef: &corev1.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
147+
PersistentVolumeSource: corev1.PersistentVolumeSource{
148+
CSI: &corev1.CSIPersistentVolumeSource{
149+
ControllerExpandSecretRef: &corev1.SecretReference{
150+
Name: "Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
151+
Namespace: "csi"}}}}},
145152
}
146153
extractedNames := sets.NewString()
147154
extractedNamesWithNamespace := sets.NewString()
155+
148156
for _, pv := range pvs {
149157
VisitPVSecretNames(pv, func(namespace, name string, kubeletVisible bool) bool {
150158
extractedNames.Insert(name)
@@ -172,6 +180,7 @@ func TestPVSecrets(t *testing.T) {
172180
"Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
173181
"Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
174182
"Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
183+
"Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
175184
)
176185
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{}))
177186
secretPaths = secretPaths.Difference(excludedSecretPaths)
@@ -219,6 +228,7 @@ func TestPVSecrets(t *testing.T) {
219228
"csi/Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
220229
"csi/Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
221230
"csi/Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
231+
"csi/Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
222232
)
223233
if missingNames := expectedNamespacedNames.Difference(extractedNamesWithNamespace); len(missingNames) > 0 {
224234
t.Logf("Missing expected namespaced names:\n%s", strings.Join(missingNames.List(), "\n"))

pkg/apis/core/types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,15 @@ type CSIPersistentVolumeSource struct {
16011601
// secret object contains more than one secret, all secrets are passed.
16021602
// +optional
16031603
NodePublishSecretRef *SecretReference
1604+
1605+
// ControllerExpandSecretRef is a reference to the secret object containing
1606+
// sensitive information to pass to the CSI driver to complete the CSI
1607+
// ControllerExpandVolume call.
1608+
// This is an alpha field and requires enabling ExpandCSIVolumes feature gate.
1609+
// This field is optional, and may be empty if no secret is required. If the
1610+
// secret object contains more than one secret, all secrets are passed.
1611+
// +optional
1612+
ControllerExpandSecretRef *SecretReference
16041613
}
16051614

16061615
// Represents a source location of a volume to mount, managed by an external CSI driver

pkg/apis/core/v1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/core/validation/validation.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,19 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
14791479
}
14801480
}
14811481

1482+
if csi.ControllerExpandSecretRef != nil {
1483+
if len(csi.ControllerExpandSecretRef.Name) == 0 {
1484+
allErrs = append(allErrs, field.Required(fldPath.Child("controllerExpandSecretRef", "name"), ""))
1485+
} else {
1486+
allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerExpandSecretRef.Name, fldPath.Child("name"))...)
1487+
}
1488+
if len(csi.ControllerExpandSecretRef.Namespace) == 0 {
1489+
allErrs = append(allErrs, field.Required(fldPath.Child("controllerExpandSecretRef", "namespace"), ""))
1490+
} else {
1491+
allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerExpandSecretRef.Namespace, fldPath.Child("namespace"))...)
1492+
}
1493+
}
1494+
14821495
if csi.NodePublishSecretRef != nil {
14831496
if len(csi.NodePublishSecretRef.Name) == 0 {
14841497
allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "name"), ""))
@@ -1772,12 +1785,17 @@ func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
17721785
func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
17731786
allErrs := ValidatePersistentVolume(newPv)
17741787

1788+
// if oldPV does not have ControllerExpandSecretRef then allow it to be set
1789+
if (oldPv.Spec.CSI != nil && oldPv.Spec.CSI.ControllerExpandSecretRef == nil) &&
1790+
(newPv.Spec.CSI != nil && newPv.Spec.CSI.ControllerExpandSecretRef != nil) {
1791+
newPv = newPv.DeepCopy()
1792+
newPv.Spec.CSI.ControllerExpandSecretRef = nil
1793+
}
1794+
17751795
// PersistentVolumeSource should be immutable after creation.
17761796
if !apiequality.Semantic.DeepEqual(newPv.Spec.PersistentVolumeSource, oldPv.Spec.PersistentVolumeSource) {
17771797
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "persistentvolumesource"), "is immutable after creation"))
17781798
}
1779-
newPv.Status = oldPv.Status
1780-
17811799
allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...)
17821800

17831801
// Allow setting NodeAffinity if oldPv NodeAffinity was not set

pkg/apis/core/validation/validation_test.go

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,31 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
455455
Type: newHostPathType(string(core.HostPathDirectory)),
456456
},
457457
}
458+
459+
validCSIVolume := testVolume("csi-volume", "", core.PersistentVolumeSpec{
460+
Capacity: core.ResourceList{
461+
core.ResourceName(core.ResourceStorage): resource.MustParse("1G"),
462+
},
463+
AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
464+
PersistentVolumeSource: core.PersistentVolumeSource{
465+
CSI: &core.CSIPersistentVolumeSource{
466+
Driver: "come.google.gcepd",
467+
VolumeHandle: "foobar",
468+
},
469+
},
470+
StorageClassName: "gp2",
471+
})
472+
473+
expandSecretRef := &core.SecretReference{
474+
Name: "expansion-secret",
475+
Namespace: "default",
476+
}
477+
458478
scenarios := map[string]struct {
459-
isExpectedFailure bool
460-
oldVolume *core.PersistentVolume
461-
newVolume *core.PersistentVolume
479+
isExpectedFailure bool
480+
csiExpansionEnabled bool
481+
oldVolume *core.PersistentVolume
482+
newVolume *core.PersistentVolume
462483
}{
463484
"condition-no-update": {
464485
isExpectedFailure: false,
@@ -475,6 +496,21 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
475496
oldVolume: validVolume,
476497
newVolume: invalidPvSourceUpdateDeep,
477498
},
499+
"csi-expansion-enabled-with-pv-secret": {
500+
csiExpansionEnabled: true,
501+
isExpectedFailure: false,
502+
oldVolume: validCSIVolume,
503+
newVolume: getCSIVolumeWithSecret(validCSIVolume, expandSecretRef),
504+
},
505+
"csi-expansion-enabled-with-old-pv-secret": {
506+
csiExpansionEnabled: true,
507+
isExpectedFailure: true,
508+
oldVolume: getCSIVolumeWithSecret(validCSIVolume, expandSecretRef),
509+
newVolume: getCSIVolumeWithSecret(validCSIVolume, &core.SecretReference{
510+
Name: "foo-secret",
511+
Namespace: "default",
512+
}),
513+
},
478514
}
479515
for name, scenario := range scenarios {
480516
errs := ValidatePersistentVolumeUpdate(scenario.newVolume, scenario.oldVolume)
@@ -487,6 +523,14 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
487523
}
488524
}
489525

526+
func getCSIVolumeWithSecret(pv *core.PersistentVolume, secret *core.SecretReference) *core.PersistentVolume {
527+
pvCopy := pv.DeepCopy()
528+
if secret != nil {
529+
pvCopy.Spec.CSI.ControllerExpandSecretRef = secret
530+
}
531+
return pvCopy
532+
}
533+
490534
func testLocalVolume(path string, affinity *core.VolumeNodeAffinity) core.PersistentVolumeSpec {
491535
return core.PersistentVolumeSpec{
492536
Capacity: core.ResourceList{
@@ -1834,6 +1878,22 @@ func TestValidateCSIVolumeSource(t *testing.T) {
18341878
errtype: field.ErrorTypeInvalid,
18351879
errfield: "driver",
18361880
},
1881+
{
1882+
name: "controllerExpandSecretRef: invalid name missing",
1883+
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Namespace: "default"}},
1884+
errtype: field.ErrorTypeRequired,
1885+
errfield: "controllerExpandSecretRef.name",
1886+
},
1887+
{
1888+
name: "controllerExpandSecretRef: invalid namespace missing",
1889+
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar"}},
1890+
errtype: field.ErrorTypeRequired,
1891+
errfield: "controllerExpandSecretRef.namespace",
1892+
},
1893+
{
1894+
name: "valid controllerExpandSecretRef",
1895+
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
1896+
},
18371897
}
18381898

18391899
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, true)()

pkg/apis/core/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)