mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-13 11:25:19 +00:00
plumb service account token down to csi driver
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
36
pkg/apis/storage/v1/zz_generated.conversion.go
generated
36
pkg/apis/storage/v1/zz_generated.conversion.go
generated
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
36
pkg/apis/storage/v1beta1/zz_generated.conversion.go
generated
36
pkg/apis/storage/v1beta1/zz_generated.conversion.go
generated
@@ -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 {
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
// 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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
// driver name: dash only
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
// driver name: numbers
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
// 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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: driverName},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: driverName},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
AttachRequired: &attachRequired,
|
||||
PodInfoOnMount: &podInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: driverName},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: driverName},
|
||||
Spec: storage.CSIDriverSpec{
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
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: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
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: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
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: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
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: ¬PodInfoOnMount,
|
||||
FSGroupPolicy: &supportedFSGroupPolicy,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
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: ¬PodInfoOnMount,
|
||||
AttachRequired: &attachNotRequired,
|
||||
PodInfoOnMount: ¬PodInfoOnMount,
|
||||
RequiresRepublish: ¬RequiresRepublish,
|
||||
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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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: ¬RequiresRepublish,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
pkg/apis/storage/zz_generated.deepcopy.go
generated
33
pkg/apis/storage/zz_generated.deepcopy.go
generated
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user