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

Add a new field for storing volume expansion secrets
k3s-v1.15.3
Kubernetes Prow Robot 2019-05-09 17:51:58 -07:00 committed by GitHub
commit 3e7fa617b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1798 additions and 1535 deletions

View File

@ -6055,6 +6055,10 @@
"io.k8s.api.core.v1.CSIPersistentVolumeSource": {
"description": "Represents storage that is managed by an external CSI volume driver (Beta feature)",
"properties": {
"controllerExpandSecretRef": {
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference",
"description": "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."
},
"controllerPublishSecretRef": {
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference",
"description": "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. 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."

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
}

File diff suppressed because it is too large Load Diff

View File

@ -218,6 +218,15 @@ message CSIPersistentVolumeSource {
// secret object contains more than one secret, all secrets are passed.
// +optional
optional SecretReference nodePublishSecretRef = 8;
// 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
optional SecretReference controllerExpandSecretRef = 9;
}
// Represents a source location of a volume to mount, managed by an external CSI driver

View File

@ -1692,6 +1692,15 @@ type CSIPersistentVolumeSource struct {
// secret object contains more than one secret, all secrets are passed.
// +optional
NodePublishSecretRef *SecretReference `json:"nodePublishSecretRef,omitempty" protobuf:"bytes,8,opt,name=nodePublishSecretRef"`
// 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 `json:"controllerExpandSecretRef,omitempty" protobuf:"bytes,9,opt,name=controllerExpandSecretRef"`
}
// Represents a source location of a volume to mount, managed by an external CSI driver

View File

@ -126,6 +126,7 @@ var map_CSIPersistentVolumeSource = map[string]string{
"controllerPublishSecretRef": "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. 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.",
"nodeStageSecretRef": "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. 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.",
"nodePublishSecretRef": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. 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.",
"controllerExpandSecretRef": "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.",
}
func (CSIPersistentVolumeSource) SwaggerDoc() map[string]string {

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
}