Merge pull request #105963 from zhucan/bugfix-95367

csi: add nodeExpandSecret support for CSI client & add unit test
This commit is contained in:
Kubernetes Prow Robot 2022-05-03 17:17:58 -07:00 committed by GitHub
commit 1aec0c81b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1337 additions and 909 deletions

View File

@ -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."

View File

@ -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": [
{

View File

@ -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": [
{

View 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
}

View 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
}

View File

@ -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
}

View File

@ -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"))

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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},

View File

@ -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"),
},
},

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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)
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -251,6 +251,10 @@
"controllerExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
},
"nodeExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
}
},
"accessModes": [

View File

@ -89,6 +89,9 @@ spec:
namespace: namespaceValue
driver: driverValue
fsType: fsTypeValue
nodeExpandSecretRef:
name: nameValue
namespace: namespaceValue
nodePublishSecretRef:
name: nameValue
namespace: namespaceValue

View File

@ -255,6 +255,10 @@
"controllerExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
},
"nodeExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
}
},
"accessModes": [

View File

@ -93,6 +93,9 @@ spec:
namespace: namespaceValue
driver: driverValue
fsType: fsTypeValue
nodeExpandSecretRef:
name: nameValue
namespace: namespaceValue
nodePublishSecretRef:
name: nameValue
namespace: namespaceValue

View File

@ -255,6 +255,10 @@
"controllerExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
},
"nodeExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
}
},
"accessModes": [

View File

@ -93,6 +93,9 @@ spec:
namespace: namespaceValue
driver: driverValue
fsType: fsTypeValue
nodeExpandSecretRef:
name: nameValue
namespace: namespaceValue
nodePublishSecretRef:
name: nameValue
namespace: namespaceValue

View File

@ -255,6 +255,10 @@
"controllerExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
},
"nodeExpandSecretRef": {
"name": "nameValue",
"namespace": "namespaceValue"
}
},
"accessModes": [

View File

@ -93,6 +93,9 @@ spec:
namespace: namespaceValue
driver: driverValue
fsType: fsTypeValue
nodeExpandSecretRef:
name: nameValue
namespace: namespaceValue
nodePublishSecretRef:
name: nameValue
namespace: namespaceValue

View File

@ -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
}

View File

@ -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