plumb service account token down to csi driver

This commit is contained in:
Shihang Zhang
2020-07-09 13:54:51 -07:00
parent f4a156eb29
commit d2859cd89b
80 changed files with 2178 additions and 408 deletions

View File

@@ -87,6 +87,10 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
obj.Spec.FSGroupPolicy = new(storage.FSGroupPolicy)
*obj.Spec.FSGroupPolicy = storage.ReadWriteOnceWithFSTypeFSGroupPolicy
}
if obj.Spec.RequiresRepublish == nil {
obj.Spec.RequiresRepublish = new(bool)
*(obj.Spec.RequiresRepublish) = false
}
if len(obj.Spec.VolumeLifecycleModes) == 0 {
obj.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
storage.VolumeLifecyclePersistent,

View File

@@ -337,6 +337,43 @@ type CSIDriverSpec struct {
//
// +optional
StorageCapacity *bool
// TokenRequests indicates the CSI driver needs pods' service account
// tokens it is mounting volume for to do necessary authentication. Kubelet
// will pass the tokens in VolumeContext in the CSI NodePublishVolume calls.
// The CSI driver should parse and validate the following VolumeContext:
// "csi.storage.k8s.io/serviceAccount.tokens": {
// "<audience>": {
// "token": <token>,
// "expirationTimestamp": <expiration timestamp in RFC3339>,
// },
// ...
// }
//
// Note: Audience in each TokenRequest should be different and at
// most one token is empty string. To receive a new token after expiry,
// RequiresRepublish can be used to trigger NodePublishVolume periodically.
//
// This is an alpha feature and only available when the
// CSIServiceAccountToken feature is enabled.
//
// +optional
// +listType=atomic
TokenRequests []TokenRequest
// RequiresRepublish indicates the CSI driver wants `NodePublishVolume`
// being periodically called to reflect any possible change in the mounted
// volume. This field defaults to false.
//
// Note: After a successful initial NodePublishVolume call, subsequent calls
// to NodePublishVolume should only update the contents of the volume. New
// mount points will not be seen by a running container.
//
// This is an alpha feature and only available when the
// CSIServiceAccountToken feature is enabled.
//
// +optional
RequiresRepublish *bool
}
// FSGroupPolicy specifies if a CSI Driver supports modifying
@@ -374,6 +411,20 @@ const (
// More modes may be added in the future.
type VolumeLifecycleMode string
// TokenRequest contains parameters of a service account token.
type TokenRequest struct {
// Audience is the intended audience of the token in "TokenRequestSpec".
// It will default to the audiences of kube apiserver.
//
Audience string
// ExpirationSeconds is the duration of validity of the token in "TokenRequestSpec".
// It has the same default value of "ExpirationSeconds" in "TokenRequestSpec."
//
// +optional
ExpirationSeconds *int64
}
const (
// VolumeLifecyclePersistent explicitly confirms that the driver implements
// the full CSI spec. It is the default when CSIDriverSpec.VolumeLifecycleModes is not

View File

@@ -58,5 +58,6 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
],
)

View File

@@ -60,4 +60,8 @@ func SetDefaults_CSIDriver(obj *storagev1.CSIDriver) {
if len(obj.Spec.VolumeLifecycleModes) == 0 && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1.VolumeLifecyclePersistent)
}
if obj.Spec.RequiresRepublish == nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIServiceAccountToken) {
obj.Spec.RequiresRepublish = new(bool)
*(obj.Spec.RequiresRepublish) = false
}
}

View File

@@ -20,6 +20,7 @@ import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -90,3 +91,48 @@ func TestSetDefaultVolumeBindingMode(t *testing.T) {
t.Errorf("Expected VolumeBindingMode to be defaulted to: %+v, got: %+v", defaultMode, outMode)
}
}
func TestSetDefaultCSIDriver(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIServiceAccountToken, true)()
enabled := true
disabled := false
tests := []struct {
desc string
field string
wantSpec *storagev1.CSIDriverSpec
}{
{
desc: "AttachRequired default to true",
field: "AttachRequired",
wantSpec: &storagev1.CSIDriverSpec{AttachRequired: &enabled},
},
{
desc: "PodInfoOnMount default to false",
field: "PodInfoOnMount",
wantSpec: &storagev1.CSIDriverSpec{PodInfoOnMount: &disabled},
},
{
desc: "VolumeLifecycleModes default to VolumeLifecyclePersistent",
field: "VolumeLifecycleModes",
wantSpec: &storagev1.CSIDriverSpec{VolumeLifecycleModes: []storagev1.VolumeLifecycleMode{storagev1.VolumeLifecyclePersistent}},
},
{
desc: "RequiresRepublish default to false",
field: "RequiresRepublish",
wantSpec: &storagev1.CSIDriverSpec{RequiresRepublish: &disabled},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
gotSpec := roundTrip(t, runtime.Object(&storagev1.CSIDriver{})).(*storagev1.CSIDriver).Spec
got := reflect.Indirect(reflect.ValueOf(gotSpec)).FieldByName(test.field).Interface()
want := reflect.Indirect(reflect.ValueOf(test.wantSpec)).FieldByName(test.field).Interface()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("CSIDriver defaults diff (-want +got):\n%s", diff)
}
})
}
}

View File

@@ -129,6 +129,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.TokenRequest)(nil), (*storage.TokenRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_TokenRequest_To_storage_TokenRequest(a.(*v1.TokenRequest), b.(*storage.TokenRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.TokenRequest)(nil), (*v1.TokenRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_TokenRequest_To_v1_TokenRequest(a.(*storage.TokenRequest), b.(*v1.TokenRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeAttachment)(nil), (*storage.VolumeAttachment)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeAttachment_To_storage_VolumeAttachment(a.(*v1.VolumeAttachment), b.(*storage.VolumeAttachment), scope)
}); err != nil {
@@ -276,6 +286,8 @@ func autoConvert_v1_CSIDriverSpec_To_storage_CSIDriverSpec(in *v1.CSIDriverSpec,
out.VolumeLifecycleModes = *(*[]storage.VolumeLifecycleMode)(unsafe.Pointer(&in.VolumeLifecycleModes))
out.StorageCapacity = (*bool)(unsafe.Pointer(in.StorageCapacity))
out.FSGroupPolicy = (*storage.FSGroupPolicy)(unsafe.Pointer(in.FSGroupPolicy))
out.TokenRequests = *(*[]storage.TokenRequest)(unsafe.Pointer(&in.TokenRequests))
out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish))
return nil
}
@@ -290,6 +302,8 @@ func autoConvert_storage_CSIDriverSpec_To_v1_CSIDriverSpec(in *storage.CSIDriver
out.PodInfoOnMount = (*bool)(unsafe.Pointer(in.PodInfoOnMount))
out.VolumeLifecycleModes = *(*[]v1.VolumeLifecycleMode)(unsafe.Pointer(&in.VolumeLifecycleModes))
out.StorageCapacity = (*bool)(unsafe.Pointer(in.StorageCapacity))
out.TokenRequests = *(*[]v1.TokenRequest)(unsafe.Pointer(&in.TokenRequests))
out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish))
return nil
}
@@ -448,6 +462,28 @@ func Convert_storage_StorageClassList_To_v1_StorageClassList(in *storage.Storage
return autoConvert_storage_StorageClassList_To_v1_StorageClassList(in, out, s)
}
func autoConvert_v1_TokenRequest_To_storage_TokenRequest(in *v1.TokenRequest, out *storage.TokenRequest, s conversion.Scope) error {
out.Audience = in.Audience
out.ExpirationSeconds = (*int64)(unsafe.Pointer(in.ExpirationSeconds))
return nil
}
// Convert_v1_TokenRequest_To_storage_TokenRequest is an autogenerated conversion function.
func Convert_v1_TokenRequest_To_storage_TokenRequest(in *v1.TokenRequest, out *storage.TokenRequest, s conversion.Scope) error {
return autoConvert_v1_TokenRequest_To_storage_TokenRequest(in, out, s)
}
func autoConvert_storage_TokenRequest_To_v1_TokenRequest(in *storage.TokenRequest, out *v1.TokenRequest, s conversion.Scope) error {
out.Audience = in.Audience
out.ExpirationSeconds = (*int64)(unsafe.Pointer(in.ExpirationSeconds))
return nil
}
// Convert_storage_TokenRequest_To_v1_TokenRequest is an autogenerated conversion function.
func Convert_storage_TokenRequest_To_v1_TokenRequest(in *storage.TokenRequest, out *v1.TokenRequest, s conversion.Scope) error {
return autoConvert_storage_TokenRequest_To_v1_TokenRequest(in, out, s)
}
func autoConvert_v1_VolumeAttachment_To_storage_VolumeAttachment(in *v1.VolumeAttachment, out *storage.VolumeAttachment, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(&in.Spec, &out.Spec, s); err != nil {

View File

@@ -58,5 +58,6 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
],
)

View File

@@ -60,4 +60,8 @@ func SetDefaults_CSIDriver(obj *storagev1beta1.CSIDriver) {
if len(obj.Spec.VolumeLifecycleModes) == 0 && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1beta1.VolumeLifecyclePersistent)
}
if obj.Spec.RequiresRepublish == nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIServiceAccountToken) {
obj.Spec.RequiresRepublish = new(bool)
*(obj.Spec.RequiresRepublish) = false
}
}

View File

@@ -20,6 +20,7 @@ import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
storagev1beta1 "k8s.io/api/storage/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -138,3 +139,43 @@ func TestSetDefaultVolumeLifecycleModesDisabled(t *testing.T) {
t.Errorf("Expected VolumeLifecycleModes to remain nil, got: %+v", outModes)
}
}
func TestSetDefaultCSIDriver(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIServiceAccountToken, true)()
enabled := true
disabled := false
tests := []struct {
desc string
field string
wantSpec *storagev1beta1.CSIDriverSpec
}{
{
desc: "AttachRequired default to true",
field: "AttachRequired",
wantSpec: &storagev1beta1.CSIDriverSpec{AttachRequired: &enabled},
},
{
desc: "PodInfoOnMount default to false",
field: "PodInfoOnMount",
wantSpec: &storagev1beta1.CSIDriverSpec{PodInfoOnMount: &disabled},
},
{
desc: "RequiresRepublish default to false",
field: "RequiresRepublish",
wantSpec: &storagev1beta1.CSIDriverSpec{RequiresRepublish: &disabled},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
gotSpec := roundTrip(t, runtime.Object(&storagev1beta1.CSIDriver{})).(*storagev1beta1.CSIDriver).Spec
got := reflect.Indirect(reflect.ValueOf(gotSpec)).FieldByName(test.field).Interface()
want := reflect.Indirect(reflect.ValueOf(test.wantSpec)).FieldByName(test.field).Interface()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("CSIDriver defaults diff (-want +got):\n%s", diff)
}
})
}
}

View File

@@ -129,6 +129,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1beta1.TokenRequest)(nil), (*storage.TokenRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_TokenRequest_To_storage_TokenRequest(a.(*v1beta1.TokenRequest), b.(*storage.TokenRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.TokenRequest)(nil), (*v1beta1.TokenRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_TokenRequest_To_v1beta1_TokenRequest(a.(*storage.TokenRequest), b.(*v1beta1.TokenRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1beta1.VolumeAttachment)(nil), (*storage.VolumeAttachment)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_VolumeAttachment_To_storage_VolumeAttachment(a.(*v1beta1.VolumeAttachment), b.(*storage.VolumeAttachment), scope)
}); err != nil {
@@ -276,6 +286,8 @@ func autoConvert_v1beta1_CSIDriverSpec_To_storage_CSIDriverSpec(in *v1beta1.CSID
out.VolumeLifecycleModes = *(*[]storage.VolumeLifecycleMode)(unsafe.Pointer(&in.VolumeLifecycleModes))
out.StorageCapacity = (*bool)(unsafe.Pointer(in.StorageCapacity))
out.FSGroupPolicy = (*storage.FSGroupPolicy)(unsafe.Pointer(in.FSGroupPolicy))
out.TokenRequests = *(*[]storage.TokenRequest)(unsafe.Pointer(&in.TokenRequests))
out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish))
return nil
}
@@ -290,6 +302,8 @@ func autoConvert_storage_CSIDriverSpec_To_v1beta1_CSIDriverSpec(in *storage.CSID
out.PodInfoOnMount = (*bool)(unsafe.Pointer(in.PodInfoOnMount))
out.VolumeLifecycleModes = *(*[]v1beta1.VolumeLifecycleMode)(unsafe.Pointer(&in.VolumeLifecycleModes))
out.StorageCapacity = (*bool)(unsafe.Pointer(in.StorageCapacity))
out.TokenRequests = *(*[]v1beta1.TokenRequest)(unsafe.Pointer(&in.TokenRequests))
out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish))
return nil
}
@@ -448,6 +462,28 @@ func Convert_storage_StorageClassList_To_v1beta1_StorageClassList(in *storage.St
return autoConvert_storage_StorageClassList_To_v1beta1_StorageClassList(in, out, s)
}
func autoConvert_v1beta1_TokenRequest_To_storage_TokenRequest(in *v1beta1.TokenRequest, out *storage.TokenRequest, s conversion.Scope) error {
out.Audience = in.Audience
out.ExpirationSeconds = (*int64)(unsafe.Pointer(in.ExpirationSeconds))
return nil
}
// Convert_v1beta1_TokenRequest_To_storage_TokenRequest is an autogenerated conversion function.
func Convert_v1beta1_TokenRequest_To_storage_TokenRequest(in *v1beta1.TokenRequest, out *storage.TokenRequest, s conversion.Scope) error {
return autoConvert_v1beta1_TokenRequest_To_storage_TokenRequest(in, out, s)
}
func autoConvert_storage_TokenRequest_To_v1beta1_TokenRequest(in *storage.TokenRequest, out *v1beta1.TokenRequest, s conversion.Scope) error {
out.Audience = in.Audience
out.ExpirationSeconds = (*int64)(unsafe.Pointer(in.ExpirationSeconds))
return nil
}
// Convert_storage_TokenRequest_To_v1beta1_TokenRequest is an autogenerated conversion function.
func Convert_storage_TokenRequest_To_v1beta1_TokenRequest(in *storage.TokenRequest, out *v1beta1.TokenRequest, s conversion.Scope) error {
return autoConvert_storage_TokenRequest_To_v1beta1_TokenRequest(in, out, s)
}
func autoConvert_v1beta1_VolumeAttachment_To_storage_VolumeAttachment(in *v1beta1.VolumeAttachment, out *storage.VolumeAttachment, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1beta1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(&in.Spec, &out.Spec, s); err != nil {

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"reflect"
"strings"
"time"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -422,6 +423,7 @@ func validateCSIDriverSpec(
allErrs = append(allErrs, validatePodInfoOnMount(spec.PodInfoOnMount, fldPath.Child("podInfoOnMount"))...)
allErrs = append(allErrs, validateStorageCapacity(spec.StorageCapacity, fldPath.Child("storageCapacity"))...)
allErrs = append(allErrs, validateFSGroupPolicy(spec.FSGroupPolicy, fldPath.Child("fsGroupPolicy"))...)
allErrs = append(allErrs, validateTokenRequests(spec.TokenRequests, fldPath.Child("tokenRequests"))...)
allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...)
return allErrs
}
@@ -473,6 +475,35 @@ func validateFSGroupPolicy(fsGroupPolicy *storage.FSGroupPolicy, fldPath *field.
return allErrs
}
// validateTokenRequests tests if the Audience in each TokenRequest are different.
// Besides, at most one TokenRequest can ignore Audience.
func validateTokenRequests(tokenRequests []storage.TokenRequest, fldPath *field.Path) field.ErrorList {
const min = 10 * time.Minute
allErrs := field.ErrorList{}
audiences := make(map[string]bool)
for i, tokenRequest := range tokenRequests {
path := fldPath.Index(i)
audience := tokenRequest.Audience
if _, ok := audiences[audience]; ok {
allErrs = append(allErrs, field.Duplicate(path.Child("audience"), audience))
continue
}
audiences[audience] = true
if tokenRequest.ExpirationSeconds == nil {
continue
}
if *tokenRequest.ExpirationSeconds < int64(min.Seconds()) {
allErrs = append(allErrs, field.Invalid(path.Child("expirationSeconds"), *tokenRequest.ExpirationSeconds, "may not specify a duration less than 10 minutes"))
}
if *tokenRequest.ExpirationSeconds > 1<<32 {
allErrs = append(allErrs, field.Invalid(path.Child("expirationSeconds"), *tokenRequest.ExpirationSeconds, "may not specify a duration larger than 2^32 seconds"))
}
}
return allErrs
}
// validateVolumeLifecycleModes tests if mode has one of the allowed values.
func validateVolumeLifecycleModes(modes []storage.VolumeLifecycleMode, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

View File

@@ -1665,6 +1665,7 @@ func TestCSIDriverValidation(t *testing.T) {
attachNotRequired := false
podInfoOnMount := true
notPodInfoOnMount := false
notRequiresRepublish := false
supportedFSGroupPolicy := storage.FileFSGroupPolicy
invalidFSGroupPolicy := storage.ReadWriteOnceWithFSTypeFSGroupPolicy
invalidFSGroupPolicy = "invalid-mode"
@@ -1672,68 +1673,77 @@ func TestCSIDriverValidation(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
// driver name: dot only
ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
// driver name: dash only
ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
// driver name: numbers
ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
// driver name: dot and dash
ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecyclePersistent,
},
@@ -1742,8 +1752,9 @@ func TestCSIDriverValidation(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleEphemeral,
},
@@ -1752,8 +1763,9 @@ func TestCSIDriverValidation(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleEphemeral,
storage.VolumeLifecyclePersistent,
@@ -1763,8 +1775,9 @@ func TestCSIDriverValidation(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleEphemeral,
storage.VolumeLifecyclePersistent,
@@ -1775,9 +1788,10 @@ func TestCSIDriverValidation(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
FSGroupPolicy: &supportedFSGroupPolicy,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
FSGroupPolicy: &supportedFSGroupPolicy,
},
},
}
@@ -1855,11 +1869,13 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
attachNotRequired := false
podInfoOnMount := true
notPodInfoOnMount := false
notRequiresRepublish := false
old := storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleEphemeral,
storage.VolumeLifecyclePersistent,
@@ -2060,3 +2076,77 @@ func TestValidateCSIStorageCapacity(t *testing.T) {
}
}
func TestCSIServiceAccountToken(t *testing.T) {
driverName := "test-driver"
gcp := "gcp"
aws := "aws"
notRequiresRepublish := false
tests := []struct {
desc string
csiDriver *storage.CSIDriver
wantErr bool
}{
{
desc: "invalid - TokenRequests has tokens with the same audience",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
TokenRequests: []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}},
RequiresRepublish: &notRequiresRepublish,
},
},
wantErr: true,
},
{
desc: "invalid - TokenRequests has tokens with ExpirationSeconds less than 10min",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64Ptr(10)}},
RequiresRepublish: &notRequiresRepublish,
},
},
wantErr: true,
},
{
desc: "invalid - TokenRequests has tokens with ExpirationSeconds less than 10min",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64Ptr(1<<32 + 1)}},
RequiresRepublish: &notRequiresRepublish,
},
},
wantErr: true,
},
{
desc: "valid - TokenRequests has at most one token with empty string audience",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
TokenRequests: []storage.TokenRequest{{Audience: ""}},
RequiresRepublish: &notRequiresRepublish,
},
},
},
{
desc: "valid - TokenRequests has tokens with different audience",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
TokenRequests: []storage.TokenRequest{{}, {Audience: gcp}, {Audience: aws}},
RequiresRepublish: &notRequiresRepublish,
},
},
},
}
for _, test := range tests {
test.csiDriver.Spec.AttachRequired = new(bool)
test.csiDriver.Spec.PodInfoOnMount = new(bool)
if errs := ValidateCSIDriver(test.csiDriver); test.wantErr != (len(errs) != 0) {
t.Errorf("ValidateCSIDriver = %v, want err: %v", errs, test.wantErr)
}
}
}

View File

@@ -114,6 +114,18 @@ func (in *CSIDriverSpec) DeepCopyInto(out *CSIDriverSpec) {
*out = new(bool)
**out = **in
}
if in.TokenRequests != nil {
in, out := &in.TokenRequests, &out.TokenRequests
*out = make([]TokenRequest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.RequiresRepublish != nil {
in, out := &in.RequiresRepublish, &out.RequiresRepublish
*out = new(bool)
**out = **in
}
return
}
@@ -398,6 +410,27 @@ func (in *StorageClassList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TokenRequest) DeepCopyInto(out *TokenRequest) {
*out = *in
if in.ExpirationSeconds != nil {
in, out := &in.ExpirationSeconds, &out.ExpirationSeconds
*out = new(int64)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenRequest.
func (in *TokenRequest) DeepCopy() *TokenRequest {
if in == nil {
return nil
}
out := new(TokenRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeAttachment) DeepCopyInto(out *VolumeAttachment) {
*out = *in