Merge pull request #77516 from gnufied/implement-resize-secrets

Add a new field for storing volume expansion secrets
This commit is contained in:
Kubernetes Prow Robot
2019-05-09 17:51:58 -07:00
committed by GitHub
15 changed files with 1798 additions and 1535 deletions

View File

@@ -28,6 +28,23 @@ func DropDisabledFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.Persist
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && !volumeModeInUse(oldPVSpec) {
pvSpec.VolumeMode = nil
}
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandCSIVolumes) && !hasExpansionSecrets(oldPVSpec) {
if pvSpec.CSI != nil {
pvSpec.CSI.ControllerExpandSecretRef = nil
}
}
}
func hasExpansionSecrets(oldPVSpec *api.PersistentVolumeSpec) bool {
if oldPVSpec == nil || oldPVSpec.CSI == nil {
return false
}
if oldPVSpec.CSI.ControllerExpandSecretRef != nil {
return true
}
return false
}
func volumeModeInUse(oldPVSpec *api.PersistentVolumeSpec) bool {

View File

@@ -32,14 +32,20 @@ func TestDropDisabledFields(t *testing.T) {
return &api.PersistentVolumeSpec{VolumeMode: mode}
}
secretRef := &api.SecretReference{
Name: "expansion-secret",
Namespace: "default",
}
modeBlock := api.PersistentVolumeBlock
tests := map[string]struct {
oldSpec *api.PersistentVolumeSpec
newSpec *api.PersistentVolumeSpec
expectOldSpec *api.PersistentVolumeSpec
expectNewSpec *api.PersistentVolumeSpec
blockEnabled bool
oldSpec *api.PersistentVolumeSpec
newSpec *api.PersistentVolumeSpec
expectOldSpec *api.PersistentVolumeSpec
expectNewSpec *api.PersistentVolumeSpec
blockEnabled bool
csiExpansionEnabled bool
}{
"disabled block clears new": {
blockEnabled: false,
@@ -84,11 +90,47 @@ func TestDropDisabledFields(t *testing.T) {
oldSpec: specWithMode(&modeBlock),
expectOldSpec: specWithMode(&modeBlock),
},
"disabled csi expansion clears secrets": {
csiExpansionEnabled: false,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(nil),
oldSpec: nil,
expectOldSpec: nil,
},
"enabled csi expansion preserve secrets": {
csiExpansionEnabled: true,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: nil,
expectOldSpec: nil,
},
"enabled csi expansion preserve secrets when both old and new have it": {
csiExpansionEnabled: true,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: specWithCSISecrets(secretRef),
expectOldSpec: specWithCSISecrets(secretRef),
},
"disabled csi expansion old pv had secrets": {
csiExpansionEnabled: false,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: specWithCSISecrets(secretRef),
expectOldSpec: specWithCSISecrets(secretRef),
},
"enabled csi expansion preserves secrets when old pv did not had secrets": {
csiExpansionEnabled: true,
newSpec: specWithCSISecrets(secretRef),
expectNewSpec: specWithCSISecrets(secretRef),
oldSpec: specWithCSISecrets(nil),
expectOldSpec: specWithCSISecrets(nil),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, tc.blockEnabled)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandCSIVolumes, tc.csiExpansionEnabled)()
DropDisabledFields(tc.newSpec, tc.oldSpec)
if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
@@ -100,3 +142,19 @@ func TestDropDisabledFields(t *testing.T) {
})
}
}
func specWithCSISecrets(secret *api.SecretReference) *api.PersistentVolumeSpec {
pvSpec := &api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
CSI: &api.CSIPersistentVolumeSource{
Driver: "com.google.gcepd",
VolumeHandle: "foobar",
},
},
}
if secret != nil {
pvSpec.CSI.ControllerExpandSecretRef = secret
}
return pvSpec
}

View File

@@ -119,6 +119,12 @@ func VisitPVSecretNames(pv *corev1.PersistentVolume, visitor Visitor) bool {
return false
}
}
if source.CSI.ControllerExpandSecretRef != nil {
if !visitor(source.CSI.ControllerExpandSecretRef.Namespace, source.CSI.ControllerExpandSecretRef.Name, false /* kubeletVisible */) {
return false
}
}
if source.CSI.NodePublishSecretRef != nil {
if !visitor(source.CSI.NodePublishSecretRef.Namespace, source.CSI.NodePublishSecretRef.Name, true /* kubeletVisible */) {
return false

View File

@@ -142,9 +142,17 @@ func TestPVSecrets(t *testing.T) {
NodeStageSecretRef: &corev1.SecretReference{
Name: "Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
Namespace: "csi"}}}}},
{Spec: corev1.PersistentVolumeSpec{
ClaimRef: &corev1.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: corev1.PersistentVolumeSource{
CSI: &corev1.CSIPersistentVolumeSource{
ControllerExpandSecretRef: &corev1.SecretReference{
Name: "Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
Namespace: "csi"}}}}},
}
extractedNames := sets.NewString()
extractedNamesWithNamespace := sets.NewString()
for _, pv := range pvs {
VisitPVSecretNames(pv, func(namespace, name string, kubeletVisible bool) bool {
extractedNames.Insert(name)
@@ -172,6 +180,7 @@ func TestPVSecrets(t *testing.T) {
"Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
"Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
"Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
"Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
)
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{}))
secretPaths = secretPaths.Difference(excludedSecretPaths)
@@ -219,6 +228,7 @@ func TestPVSecrets(t *testing.T) {
"csi/Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
"csi/Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
"csi/Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
"csi/Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
)
if missingNames := expectedNamespacedNames.Difference(extractedNamesWithNamespace); len(missingNames) > 0 {
t.Logf("Missing expected namespaced names:\n%s", strings.Join(missingNames.List(), "\n"))

View File

@@ -1601,6 +1601,15 @@ type CSIPersistentVolumeSource struct {
// secret object contains more than one secret, all secrets are passed.
// +optional
NodePublishSecretRef *SecretReference
// ControllerExpandSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// ControllerExpandVolume call.
// This is an alpha field and requires enabling ExpandCSIVolumes feature gate.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
ControllerExpandSecretRef *SecretReference
}
// Represents a source location of a volume to mount, managed by an external CSI driver

View File

@@ -2310,6 +2310,7 @@ func autoConvert_v1_CSIPersistentVolumeSource_To_core_CSIPersistentVolumeSource(
out.ControllerPublishSecretRef = (*core.SecretReference)(unsafe.Pointer(in.ControllerPublishSecretRef))
out.NodeStageSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodeStageSecretRef))
out.NodePublishSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodePublishSecretRef))
out.ControllerExpandSecretRef = (*core.SecretReference)(unsafe.Pointer(in.ControllerExpandSecretRef))
return nil
}
@@ -2327,6 +2328,7 @@ func autoConvert_core_CSIPersistentVolumeSource_To_v1_CSIPersistentVolumeSource(
out.ControllerPublishSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.ControllerPublishSecretRef))
out.NodeStageSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodeStageSecretRef))
out.NodePublishSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodePublishSecretRef))
out.ControllerExpandSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.ControllerExpandSecretRef))
return nil
}

View File

@@ -1479,6 +1479,19 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
}
}
if csi.ControllerExpandSecretRef != nil {
if len(csi.ControllerExpandSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("controllerExpandSecretRef", "name"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerExpandSecretRef.Name, fldPath.Child("name"))...)
}
if len(csi.ControllerExpandSecretRef.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("controllerExpandSecretRef", "namespace"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerExpandSecretRef.Namespace, fldPath.Child("namespace"))...)
}
}
if csi.NodePublishSecretRef != nil {
if len(csi.NodePublishSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "name"), ""))
@@ -1772,12 +1785,17 @@ func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
allErrs := ValidatePersistentVolume(newPv)
// if oldPV does not have ControllerExpandSecretRef then allow it to be set
if (oldPv.Spec.CSI != nil && oldPv.Spec.CSI.ControllerExpandSecretRef == nil) &&
(newPv.Spec.CSI != nil && newPv.Spec.CSI.ControllerExpandSecretRef != nil) {
newPv = newPv.DeepCopy()
newPv.Spec.CSI.ControllerExpandSecretRef = nil
}
// PersistentVolumeSource should be immutable after creation.
if !apiequality.Semantic.DeepEqual(newPv.Spec.PersistentVolumeSource, oldPv.Spec.PersistentVolumeSource) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "persistentvolumesource"), "is immutable after creation"))
}
newPv.Status = oldPv.Status
allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...)
// Allow setting NodeAffinity if oldPv NodeAffinity was not set

View File

@@ -455,10 +455,31 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
Type: newHostPathType(string(core.HostPathDirectory)),
},
}
validCSIVolume := testVolume("csi-volume", "", core.PersistentVolumeSpec{
Capacity: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("1G"),
},
AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
PersistentVolumeSource: core.PersistentVolumeSource{
CSI: &core.CSIPersistentVolumeSource{
Driver: "come.google.gcepd",
VolumeHandle: "foobar",
},
},
StorageClassName: "gp2",
})
expandSecretRef := &core.SecretReference{
Name: "expansion-secret",
Namespace: "default",
}
scenarios := map[string]struct {
isExpectedFailure bool
oldVolume *core.PersistentVolume
newVolume *core.PersistentVolume
isExpectedFailure bool
csiExpansionEnabled bool
oldVolume *core.PersistentVolume
newVolume *core.PersistentVolume
}{
"condition-no-update": {
isExpectedFailure: false,
@@ -475,6 +496,21 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
oldVolume: validVolume,
newVolume: invalidPvSourceUpdateDeep,
},
"csi-expansion-enabled-with-pv-secret": {
csiExpansionEnabled: true,
isExpectedFailure: false,
oldVolume: validCSIVolume,
newVolume: getCSIVolumeWithSecret(validCSIVolume, expandSecretRef),
},
"csi-expansion-enabled-with-old-pv-secret": {
csiExpansionEnabled: true,
isExpectedFailure: true,
oldVolume: getCSIVolumeWithSecret(validCSIVolume, expandSecretRef),
newVolume: getCSIVolumeWithSecret(validCSIVolume, &core.SecretReference{
Name: "foo-secret",
Namespace: "default",
}),
},
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolumeUpdate(scenario.newVolume, scenario.oldVolume)
@@ -487,6 +523,14 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
}
}
func getCSIVolumeWithSecret(pv *core.PersistentVolume, secret *core.SecretReference) *core.PersistentVolume {
pvCopy := pv.DeepCopy()
if secret != nil {
pvCopy.Spec.CSI.ControllerExpandSecretRef = secret
}
return pvCopy
}
func testLocalVolume(path string, affinity *core.VolumeNodeAffinity) core.PersistentVolumeSpec {
return core.PersistentVolumeSpec{
Capacity: core.ResourceList{
@@ -1834,6 +1878,22 @@ func TestValidateCSIVolumeSource(t *testing.T) {
errtype: field.ErrorTypeInvalid,
errfield: "driver",
},
{
name: "controllerExpandSecretRef: invalid name missing",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Namespace: "default"}},
errtype: field.ErrorTypeRequired,
errfield: "controllerExpandSecretRef.name",
},
{
name: "controllerExpandSecretRef: invalid namespace missing",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar"}},
errtype: field.ErrorTypeRequired,
errfield: "controllerExpandSecretRef.namespace",
},
{
name: "valid controllerExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
},
}
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, true)()

View File

@@ -237,6 +237,11 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
*out = new(SecretReference)
**out = **in
}
if in.ControllerExpandSecretRef != nil {
in, out := &in.ControllerExpandSecretRef, &out.ControllerExpandSecretRef
*out = new(SecretReference)
**out = **in
}
return
}