Merge pull request #105963 from zhucan/bugfix-95367
csi: add nodeExpandSecret support for CSI client & add unit test
This commit is contained in:
commit
1aec0c81b1
6
api/openapi-spec/swagger.json
generated
6
api/openapi-spec/swagger.json
generated
@ -4942,7 +4942,7 @@
|
||||
"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."
|
||||
"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 beta 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",
|
||||
@ -4956,6 +4956,10 @@
|
||||
"description": "fsType to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".",
|
||||
"type": "string"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference",
|
||||
"description": "nodeExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeExpandVolume call. This is an alpha field and requires enabling CSINodeExpandSecret feature gate. This field is optional, may be omitted if no secret is required. If the secret object contains more than one secret, all secrets are passed."
|
||||
},
|
||||
"nodePublishSecretRef": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference",
|
||||
"description": "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."
|
||||
|
@ -422,7 +422,7 @@
|
||||
"$ref": "#/components/schemas/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."
|
||||
"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 beta 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": {
|
||||
"allOf": [
|
||||
@ -441,6 +441,14 @@
|
||||
"description": "fsType to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".",
|
||||
"type": "string"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/io.k8s.api.core.v1.SecretReference"
|
||||
}
|
||||
],
|
||||
"description": "nodeExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeExpandVolume call. This is an alpha field and requires enabling CSINodeExpandSecret feature gate. This field is optional, may be omitted if no secret is required. If the secret object contains more than one secret, all secrets are passed."
|
||||
},
|
||||
"nodePublishSecretRef": {
|
||||
"allOf": [
|
||||
{
|
||||
|
@ -101,7 +101,7 @@
|
||||
"$ref": "#/components/schemas/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."
|
||||
"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 beta 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": {
|
||||
"allOf": [
|
||||
@ -120,6 +120,14 @@
|
||||
"description": "fsType to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".",
|
||||
"type": "string"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/io.k8s.api.core.v1.SecretReference"
|
||||
}
|
||||
],
|
||||
"description": "nodeExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeExpandVolume call. This is an alpha field and requires enabling CSINodeExpandSecret feature gate. This field is optional, may be omitted if no secret is required. If the secret object contains more than one secret, all secrets are passed."
|
||||
},
|
||||
"nodePublishSecretRef": {
|
||||
"allOf": [
|
||||
{
|
||||
|
44
pkg/api/persistentvolume/util.go
Normal file
44
pkg/api/persistentvolume/util.go
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package persistentvolume
|
||||
|
||||
import (
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
// DropDisabledFields removes disabled fields from the pv spec.
|
||||
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pv spec.
|
||||
func DropDisabledFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.PersistentVolumeSpec) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.CSINodeExpandSecret) && !hasNodeExpansionSecrets(oldPVSpec) {
|
||||
if pvSpec.CSI != nil {
|
||||
pvSpec.CSI.NodeExpandSecretRef = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hasNodeExpansionSecrets(oldPVSpec *api.PersistentVolumeSpec) bool {
|
||||
if oldPVSpec == nil || oldPVSpec.CSI == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if oldPVSpec.CSI.NodeExpandSecretRef != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
110
pkg/api/persistentvolume/util_test.go
Normal file
110
pkg/api/persistentvolume/util_test.go
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package persistentvolume
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
func TestDropDisabledFields(t *testing.T) {
|
||||
secretRef := &api.SecretReference{
|
||||
Name: "expansion-secret",
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
tests := map[string]struct {
|
||||
oldSpec *api.PersistentVolumeSpec
|
||||
newSpec *api.PersistentVolumeSpec
|
||||
expectOldSpec *api.PersistentVolumeSpec
|
||||
expectNewSpec *api.PersistentVolumeSpec
|
||||
csiExpansionEnabled bool
|
||||
}{
|
||||
"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.CSINodeExpandSecret, tc.csiExpansionEnabled)()
|
||||
|
||||
DropDisabledFields(tc.newSpec, tc.oldSpec)
|
||||
if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
|
||||
t.Error(cmp.Diff(tc.newSpec, tc.expectNewSpec))
|
||||
}
|
||||
if !reflect.DeepEqual(tc.oldSpec, tc.expectOldSpec) {
|
||||
t.Error(cmp.Diff(tc.oldSpec, tc.expectOldSpec))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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.NodeExpandSecretRef = secret
|
||||
}
|
||||
return pvSpec
|
||||
}
|
@ -147,6 +147,11 @@ func VisitPVSecretNames(pv *corev1.PersistentVolume, visitor Visitor) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if source.CSI.NodeExpandSecretRef != nil {
|
||||
if !visitor(source.CSI.NodeExpandSecretRef.Namespace, source.CSI.NodeExpandSecretRef.Name, true /* kubeletVisible */) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -149,6 +149,13 @@ func TestPVSecrets(t *testing.T) {
|
||||
ControllerExpandSecretRef: &corev1.SecretReference{
|
||||
Name: "Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
|
||||
Namespace: "csi"}}}}},
|
||||
{Spec: corev1.PersistentVolumeSpec{
|
||||
ClaimRef: &corev1.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
|
||||
PersistentVolumeSource: corev1.PersistentVolumeSource{
|
||||
CSI: &corev1.CSIPersistentVolumeSource{
|
||||
NodeExpandSecretRef: &corev1.SecretReference{
|
||||
Name: "Spec.PersistentVolumeSource.CSI.NodeExpandSecretRef",
|
||||
Namespace: "csi"}}}}},
|
||||
}
|
||||
extractedNames := sets.NewString()
|
||||
extractedNamesWithNamespace := sets.NewString()
|
||||
@ -181,6 +188,7 @@ func TestPVSecrets(t *testing.T) {
|
||||
"Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
|
||||
"Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
|
||||
"Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
|
||||
"Spec.PersistentVolumeSource.CSI.NodeExpandSecretRef",
|
||||
)
|
||||
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{}))
|
||||
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||
@ -229,6 +237,7 @@ func TestPVSecrets(t *testing.T) {
|
||||
"csi/Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
|
||||
"csi/Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
|
||||
"csi/Spec.PersistentVolumeSource.CSI.ControllerExpandSecretRef",
|
||||
"csi/Spec.PersistentVolumeSource.CSI.NodeExpandSecretRef",
|
||||
)
|
||||
if missingNames := expectedNamespacedNames.Difference(extractedNamesWithNamespace); len(missingNames) > 0 {
|
||||
t.Logf("Missing expected namespaced names:\n%s", strings.Join(missingNames.List(), "\n"))
|
||||
|
@ -1716,11 +1716,20 @@ type CSIPersistentVolumeSource struct {
|
||||
// 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 is an beta 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
|
||||
|
||||
// NodeExpandSecretRef is a reference to the secret object containing
|
||||
// sensitive information to pass to the CSI driver to complete the CSI
|
||||
// NodeExpandVolume call.
|
||||
// This is an alpha field and requires enabling CSINodeExpandSecret feature gate.
|
||||
// This field is optional, may be omitted if no secret is required. If the
|
||||
// secret object contains more than one secret, all secrets are passed.
|
||||
// +optional
|
||||
NodeExpandSecretRef *SecretReference
|
||||
}
|
||||
|
||||
// CSIVolumeSource represents a source location of a volume to mount, managed by an external CSI driver
|
||||
|
2
pkg/apis/core/v1/zz_generated.conversion.go
generated
2
pkg/apis/core/v1/zz_generated.conversion.go
generated
@ -2423,6 +2423,7 @@ func autoConvert_v1_CSIPersistentVolumeSource_To_core_CSIPersistentVolumeSource(
|
||||
out.NodeStageSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodeStageSecretRef))
|
||||
out.NodePublishSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodePublishSecretRef))
|
||||
out.ControllerExpandSecretRef = (*core.SecretReference)(unsafe.Pointer(in.ControllerExpandSecretRef))
|
||||
out.NodeExpandSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodeExpandSecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -2441,6 +2442,7 @@ func autoConvert_core_CSIPersistentVolumeSource_To_v1_CSIPersistentVolumeSource(
|
||||
out.NodeStageSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodeStageSecretRef))
|
||||
out.NodePublishSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodePublishSecretRef))
|
||||
out.ControllerExpandSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.ControllerExpandSecretRef))
|
||||
out.NodeExpandSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodeExpandSecretRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1595,6 +1595,18 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
|
||||
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodePublishSecretRef.Namespace, fldPath.Child("namespace"))...)
|
||||
}
|
||||
}
|
||||
if csi.NodeExpandSecretRef != nil {
|
||||
if len(csi.NodeExpandSecretRef.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("nodeExpandSecretRef", "name"), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidateDNS1123Subdomain(csi.NodeExpandSecretRef.Name, fldPath.Child("name"))...)
|
||||
}
|
||||
if len(csi.NodeExpandSecretRef.Namespace) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("nodeExpandSecretRef", "namespace"), ""))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodeExpandSecretRef.Namespace, fldPath.Child("namespace"))...)
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
@ -2898,6 +2898,22 @@ func TestValidateCSIPersistentVolumeSource(t *testing.T) {
|
||||
errtype: field.ErrorTypeRequired,
|
||||
errfield: "nodePublishSecretRef.namespace",
|
||||
},
|
||||
{
|
||||
name: "nodeExpandSecretRef: invalid name missing",
|
||||
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Namespace: "default"}},
|
||||
errtype: field.ErrorTypeRequired,
|
||||
errfield: "nodeExpandSecretRef.name",
|
||||
},
|
||||
{
|
||||
name: "nodeExpandSecretRef: invalid namespace missing",
|
||||
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: "foobar"}},
|
||||
errtype: field.ErrorTypeRequired,
|
||||
errfield: "nodeExpandSecretRef.namespace",
|
||||
},
|
||||
{
|
||||
name: "valid nodeExpandSecretRef",
|
||||
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
|
5
pkg/apis/core/zz_generated.deepcopy.go
generated
5
pkg/apis/core/zz_generated.deepcopy.go
generated
@ -243,6 +243,11 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
|
||||
*out = new(SecretReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.NodeExpandSecretRef != nil {
|
||||
in, out := &in.NodeExpandSecretRef, &out.NodeExpandSecretRef
|
||||
*out = new(SecretReference)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -882,6 +882,13 @@ const (
|
||||
//
|
||||
// Enables support for time zones in CronJobs.
|
||||
CronJobTimeZone featuregate.Feature = "CronJobTimeZone"
|
||||
|
||||
// owner: @humblec, @zhucan
|
||||
// kep: http://kep.k8s.io/3171
|
||||
// alpha: v1.24
|
||||
//
|
||||
// Enables SecretRef field in CSI NodeExpandVolume request.
|
||||
CSINodeExpandSecret featuregate.Feature = "CSINodeExpandSecret"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -990,6 +997,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
ReadWriteOncePod: {Default: false, PreRelease: featuregate.Alpha},
|
||||
CSRDuration: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26
|
||||
DelegateFSGroupToCSIDriver: {Default: true, PreRelease: featuregate.Beta},
|
||||
CSINodeExpandSecret: {Default: false, PreRelease: featuregate.Alpha},
|
||||
KubeletInUserNamespace: {Default: false, PreRelease: featuregate.Alpha},
|
||||
MemoryQoS: {Default: false, PreRelease: featuregate.Alpha},
|
||||
CPUManagerPolicyOptions: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
8
pkg/generated/openapi/zz_generated.openapi.go
generated
8
pkg/generated/openapi/zz_generated.openapi.go
generated
@ -14818,7 +14818,13 @@ func schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref common.ReferenceCall
|
||||
},
|
||||
"controllerExpandSecretRef": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
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.",
|
||||
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 beta 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.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretReference"),
|
||||
},
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "nodeExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeExpandVolume call. This is an alpha field and requires enabling CSINodeExpandSecret feature gate. This field is optional, may be omitted if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
|
||||
Ref: ref("k8s.io/api/core/v1.SecretReference"),
|
||||
},
|
||||
},
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
pvutil "k8s.io/kubernetes/pkg/api/persistentvolume"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
volumevalidation "k8s.io/kubernetes/pkg/volume/validation"
|
||||
@ -62,6 +63,10 @@ func (persistentvolumeStrategy) GetResetFields() map[fieldpath.APIVersion]*field
|
||||
|
||||
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
|
||||
func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
pv := obj.(*api.PersistentVolume)
|
||||
pv.Status = api.PersistentVolumeStatus{}
|
||||
|
||||
pvutil.DropDisabledFields(&pv.Spec, nil)
|
||||
}
|
||||
|
||||
func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
@ -89,6 +94,8 @@ func (persistentvolumeStrategy) PrepareForUpdate(ctx context.Context, obj, old r
|
||||
newPv := obj.(*api.PersistentVolume)
|
||||
oldPv := old.(*api.PersistentVolume)
|
||||
newPv.Status = oldPv.Status
|
||||
|
||||
pvutil.DropDisabledFields(&newPv.Spec, &oldPv.Spec)
|
||||
}
|
||||
|
||||
func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
|
@ -120,6 +120,7 @@ type csiResizeOptions struct {
|
||||
accessMode api.PersistentVolumeAccessMode
|
||||
newSize resource.Quantity
|
||||
mountOptions []string
|
||||
secrets map[string]string
|
||||
}
|
||||
|
||||
var _ csiClient = &csiDriverClient{}
|
||||
@ -320,6 +321,7 @@ func (c *csiDriverClient) NodeExpandVolume(ctx context.Context, opts csiResizeOp
|
||||
Mode: accessModeMapper(opts.accessMode),
|
||||
},
|
||||
},
|
||||
Secrets: opts.secrets,
|
||||
}
|
||||
|
||||
// not all CSI drivers support NodeStageUnstage and hence the StagingTargetPath
|
||||
|
@ -23,7 +23,9 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
api "k8s.io/api/core/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
@ -77,6 +79,17 @@ func (c *csiPlugin) nodeExpandWithClient(
|
||||
if pv == nil {
|
||||
return false, fmt.Errorf("Expander.NodeExpand failed to find associated PersistentVolume for plugin %s", c.GetPluginName())
|
||||
}
|
||||
nodeExpandSecrets := map[string]string{}
|
||||
expandClient := c.host.GetKubeClient()
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CSINodeExpandSecret) {
|
||||
if csiSource.NodeExpandSecretRef != nil {
|
||||
nodeExpandSecrets, err = getCredentialsFromSecret(expandClient, csiSource.NodeExpandSecretRef)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("expander.NodeExpand failed to get NodeExpandSecretRef %s/%s: %v",
|
||||
csiSource.NodeExpandSecretRef.Namespace, csiSource.NodeExpandSecretRef.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opts := csiResizeOptions{
|
||||
volumePath: resizeOptions.DeviceMountPath,
|
||||
@ -86,6 +99,7 @@ func (c *csiPlugin) nodeExpandWithClient(
|
||||
fsType: csiSource.FSType,
|
||||
accessMode: api.ReadWriteOnce,
|
||||
mountOptions: pv.Spec.MountOptions,
|
||||
secrets: nodeExpandSecrets,
|
||||
}
|
||||
|
||||
if !fsVolume {
|
||||
|
@ -17,26 +17,34 @@ limitations under the License.
|
||||
package csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
api "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
)
|
||||
|
||||
func TestNodeExpand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nodeExpansion bool
|
||||
nodeStageSet bool
|
||||
success bool
|
||||
fsVolume bool
|
||||
grpcError error
|
||||
hasVolumeInUseError bool
|
||||
deviceStagePath string
|
||||
name string
|
||||
nodeExpansion bool
|
||||
nodeStageSet bool
|
||||
success bool
|
||||
fsVolume bool
|
||||
grpcError error
|
||||
hasVolumeInUseError bool
|
||||
deviceStagePath string
|
||||
enableCSINodeExpandSecret bool
|
||||
secret *api.Secret
|
||||
}{
|
||||
{
|
||||
name: "when node expansion is not set",
|
||||
@ -87,13 +95,39 @@ func TestNodeExpand(t *testing.T) {
|
||||
grpcError: status.Error(codes.InvalidArgument, "invalid-argument"),
|
||||
hasVolumeInUseError: false,
|
||||
},
|
||||
{
|
||||
name: "when nodeExpansion=on, nodeStage=on, volumePhase=staged",
|
||||
nodeExpansion: true,
|
||||
nodeStageSet: true,
|
||||
success: true,
|
||||
fsVolume: true,
|
||||
deviceStagePath: "/foo/bar",
|
||||
enableCSINodeExpandSecret: true,
|
||||
secret: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "expand-secret",
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"apiUsername": []byte("csiusername"),
|
||||
"apiPassword": []byte("csipassword"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSINodeExpandSecret, tc.enableCSINodeExpandSecret)()
|
||||
plug, tmpDir := newTestPlugin(t, nil)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
spec := volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "expandable", "test-vol"), false)
|
||||
if tc.enableCSINodeExpandSecret {
|
||||
spec.PersistentVolume.Spec.CSI.NodeExpandSecretRef = &api.SecretReference{
|
||||
Name: tc.secret.Name,
|
||||
Namespace: tc.secret.Namespace,
|
||||
}
|
||||
}
|
||||
|
||||
newSize, _ := resource.ParseQuantity("20Gi")
|
||||
|
||||
@ -110,6 +144,13 @@ func TestNodeExpand(t *testing.T) {
|
||||
fakeCSIClient, _ := csClient.(*fakeCsiDriverClient)
|
||||
fakeNodeClient := fakeCSIClient.nodeClient
|
||||
|
||||
if tc.enableCSINodeExpandSecret {
|
||||
_, err := plug.host.GetKubeClient().CoreV1().Secrets(tc.secret.Namespace).Create(context.TODO(), tc.secret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if tc.grpcError != nil {
|
||||
fakeNodeClient.SetNextError(tc.grpcError)
|
||||
}
|
||||
|
1840
staging/src/k8s.io/api/core/v1/generated.pb.go
generated
1840
staging/src/k8s.io/api/core/v1/generated.pb.go
generated
File diff suppressed because it is too large
Load Diff
@ -220,11 +220,20 @@ message CSIPersistentVolumeSource {
|
||||
// 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 is an beta 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;
|
||||
|
||||
// nodeExpandSecretRef is a reference to the secret object containing
|
||||
// sensitive information to pass to the CSI driver to complete the CSI
|
||||
// NodeExpandVolume call.
|
||||
// This is an alpha field and requires enabling CSINodeExpandSecret feature gate.
|
||||
// This field is optional, may be omitted if no secret is required. If the
|
||||
// secret object contains more than one secret, all secrets are passed.
|
||||
// +optional
|
||||
optional SecretReference nodeExpandSecretRef = 10;
|
||||
}
|
||||
|
||||
// Represents a source location of a volume to mount, managed by an external CSI driver
|
||||
|
@ -1800,11 +1800,20 @@ type CSIPersistentVolumeSource struct {
|
||||
// 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 is an beta 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"`
|
||||
|
||||
// nodeExpandSecretRef is a reference to the secret object containing
|
||||
// sensitive information to pass to the CSI driver to complete the CSI
|
||||
// NodeExpandVolume call.
|
||||
// This is an alpha field and requires enabling CSINodeExpandSecret feature gate.
|
||||
// This field is optional, may be omitted if no secret is required. If the
|
||||
// secret object contains more than one secret, all secrets are passed.
|
||||
// +optional
|
||||
NodeExpandSecretRef *SecretReference `json:"nodeExpandSecretRef,omitempty" protobuf:"bytes,10,opt,name=nodeExpandSecretRef"`
|
||||
}
|
||||
|
||||
// Represents a source location of a volume to mount, managed by an external CSI driver
|
||||
|
@ -126,7 +126,8 @@ 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.",
|
||||
"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 beta 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.",
|
||||
"nodeExpandSecretRef": "nodeExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeExpandVolume call. This is an alpha field and requires enabling CSINodeExpandSecret feature gate. This field is optional, may be omitted if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
|
||||
}
|
||||
|
||||
func (CSIPersistentVolumeSource) SwaggerDoc() map[string]string {
|
||||
|
@ -243,6 +243,11 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
|
||||
*out = new(SecretReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.NodeExpandSecretRef != nil {
|
||||
in, out := &in.NodeExpandSecretRef, &out.NodeExpandSecretRef
|
||||
*out = new(SecretReference)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -251,6 +251,10 @@
|
||||
"controllerExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
}
|
||||
},
|
||||
"accessModes": [
|
||||
|
Binary file not shown.
@ -89,6 +89,9 @@ spec:
|
||||
namespace: namespaceValue
|
||||
driver: driverValue
|
||||
fsType: fsTypeValue
|
||||
nodeExpandSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
nodePublishSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
|
@ -255,6 +255,10 @@
|
||||
"controllerExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
}
|
||||
},
|
||||
"accessModes": [
|
||||
|
Binary file not shown.
@ -93,6 +93,9 @@ spec:
|
||||
namespace: namespaceValue
|
||||
driver: driverValue
|
||||
fsType: fsTypeValue
|
||||
nodeExpandSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
nodePublishSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
|
@ -255,6 +255,10 @@
|
||||
"controllerExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
}
|
||||
},
|
||||
"accessModes": [
|
||||
|
Binary file not shown.
@ -93,6 +93,9 @@ spec:
|
||||
namespace: namespaceValue
|
||||
driver: driverValue
|
||||
fsType: fsTypeValue
|
||||
nodeExpandSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
nodePublishSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
|
@ -255,6 +255,10 @@
|
||||
"controllerExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
},
|
||||
"nodeExpandSecretRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
}
|
||||
},
|
||||
"accessModes": [
|
||||
|
Binary file not shown.
@ -93,6 +93,9 @@ spec:
|
||||
namespace: namespaceValue
|
||||
driver: driverValue
|
||||
fsType: fsTypeValue
|
||||
nodeExpandSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
nodePublishSecretRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
|
@ -30,6 +30,7 @@ type CSIPersistentVolumeSourceApplyConfiguration struct {
|
||||
NodeStageSecretRef *SecretReferenceApplyConfiguration `json:"nodeStageSecretRef,omitempty"`
|
||||
NodePublishSecretRef *SecretReferenceApplyConfiguration `json:"nodePublishSecretRef,omitempty"`
|
||||
ControllerExpandSecretRef *SecretReferenceApplyConfiguration `json:"controllerExpandSecretRef,omitempty"`
|
||||
NodeExpandSecretRef *SecretReferenceApplyConfiguration `json:"nodeExpandSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// CSIPersistentVolumeSourceApplyConfiguration constructs an declarative configuration of the CSIPersistentVolumeSource type for use with
|
||||
@ -115,3 +116,11 @@ func (b *CSIPersistentVolumeSourceApplyConfiguration) WithControllerExpandSecret
|
||||
b.ControllerExpandSecretRef = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNodeExpandSecretRef sets the NodeExpandSecretRef field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the NodeExpandSecretRef field is set to the value of the last call.
|
||||
func (b *CSIPersistentVolumeSourceApplyConfiguration) WithNodeExpandSecretRef(value *SecretReferenceApplyConfiguration) *CSIPersistentVolumeSourceApplyConfiguration {
|
||||
b.NodeExpandSecretRef = value
|
||||
return b
|
||||
}
|
||||
|
@ -3565,6 +3565,9 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: fsType
|
||||
type:
|
||||
scalar: string
|
||||
- name: nodeExpandSecretRef
|
||||
type:
|
||||
namedType: io.k8s.api.core.v1.SecretReference
|
||||
- name: nodePublishSecretRef
|
||||
type:
|
||||
namedType: io.k8s.api.core.v1.SecretReference
|
||||
|
Loading…
Reference in New Issue
Block a user