mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 11:38:15 +00:00
Add APIGroup ratcheting validation to PVC.DataSource
This commit is contained in:
parent
160fe010f3
commit
5fcffcf4e4
@ -2022,6 +2022,8 @@ type PersistentVolumeClaimSpecValidationOptions struct {
|
|||||||
EnableRecoverFromExpansionFailure bool
|
EnableRecoverFromExpansionFailure bool
|
||||||
// Allow to validate the label value of the label selector
|
// Allow to validate the label value of the label selector
|
||||||
AllowInvalidLabelValueInSelector bool
|
AllowInvalidLabelValueInSelector bool
|
||||||
|
// Allow to validate the API group of the data source and data source reference
|
||||||
|
AllowInvalidAPIGroupInDataSourceOrRef bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
|
func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
|
||||||
@ -2034,6 +2036,10 @@ func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolum
|
|||||||
// If there's no old PVC, use the options based solely on feature enablement
|
// If there's no old PVC, use the options based solely on feature enablement
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the old object had an invalid API group in the data source or data source reference, continue to allow it in the new object
|
||||||
|
opts.AllowInvalidAPIGroupInDataSourceOrRef = allowInvalidAPIGroupInDataSourceOrRef(&oldPvc.Spec)
|
||||||
|
|
||||||
labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
|
labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
|
||||||
AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
|
AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
|
||||||
}
|
}
|
||||||
@ -2077,6 +2083,17 @@ func ValidationOptionsForPersistentVolumeClaimTemplate(claimTemplate, oldClaimTe
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allowInvalidAPIGroupInDataSourceOrRef returns true if the spec contains a data source or data source reference with an API group
|
||||||
|
func allowInvalidAPIGroupInDataSourceOrRef(spec *core.PersistentVolumeClaimSpec) bool {
|
||||||
|
if spec.DataSource != nil && spec.DataSource.APIGroup != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if spec.DataSourceRef != nil && spec.DataSourceRef.APIGroup != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
|
// ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
|
||||||
func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
|
func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
|
||||||
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
|
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
|
||||||
@ -2085,7 +2102,7 @@ func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts Persist
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validateDataSource validates a DataSource/DataSourceRef in a PersistentVolumeClaimSpec
|
// validateDataSource validates a DataSource/DataSourceRef in a PersistentVolumeClaimSpec
|
||||||
func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList {
|
func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *field.Path, allowInvalidAPIGroupInDataSourceOrRef bool) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(dataSource.Name) == 0 {
|
if len(dataSource.Name) == 0 {
|
||||||
@ -2101,12 +2118,17 @@ func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *fie
|
|||||||
if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
|
if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
|
allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
|
||||||
}
|
}
|
||||||
|
if len(apiGroup) > 0 && !allowInvalidAPIGroupInDataSourceOrRef {
|
||||||
|
for _, errString := range validation.IsDNS1123Subdomain(apiGroup) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), apiGroup, errString))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateDataSourceRef validates a DataSourceRef in a PersistentVolumeClaimSpec
|
// validateDataSourceRef validates a DataSourceRef in a PersistentVolumeClaimSpec
|
||||||
func validateDataSourceRef(dataSourceRef *core.TypedObjectReference, fldPath *field.Path) field.ErrorList {
|
func validateDataSourceRef(dataSourceRef *core.TypedObjectReference, fldPath *field.Path, allowInvalidAPIGroupInDataSourceOrRef bool) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(dataSourceRef.Name) == 0 {
|
if len(dataSourceRef.Name) == 0 {
|
||||||
@ -2122,6 +2144,11 @@ func validateDataSourceRef(dataSourceRef *core.TypedObjectReference, fldPath *fi
|
|||||||
if len(apiGroup) == 0 && dataSourceRef.Kind != "PersistentVolumeClaim" {
|
if len(apiGroup) == 0 && dataSourceRef.Kind != "PersistentVolumeClaim" {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath, dataSourceRef.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
|
allErrs = append(allErrs, field.Invalid(fldPath, dataSourceRef.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
|
||||||
}
|
}
|
||||||
|
if len(apiGroup) > 0 && !allowInvalidAPIGroupInDataSourceOrRef {
|
||||||
|
for _, errString := range validation.IsDNS1123Subdomain(apiGroup) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), apiGroup, errString))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if dataSourceRef.Namespace != nil && len(*dataSourceRef.Namespace) > 0 {
|
if dataSourceRef.Namespace != nil && len(*dataSourceRef.Namespace) > 0 {
|
||||||
for _, msg := range ValidateNameFunc(ValidateNamespaceName)(*dataSourceRef.Namespace, false) {
|
for _, msg := range ValidateNameFunc(ValidateNamespaceName)(*dataSourceRef.Namespace, false) {
|
||||||
@ -2185,10 +2212,10 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
|
|||||||
}
|
}
|
||||||
|
|
||||||
if spec.DataSource != nil {
|
if spec.DataSource != nil {
|
||||||
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"))...)
|
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"), opts.AllowInvalidAPIGroupInDataSourceOrRef)...)
|
||||||
}
|
}
|
||||||
if spec.DataSourceRef != nil {
|
if spec.DataSourceRef != nil {
|
||||||
allErrs = append(allErrs, validateDataSourceRef(spec.DataSourceRef, fldPath.Child("dataSourceRef"))...)
|
allErrs = append(allErrs, validateDataSourceRef(spec.DataSourceRef, fldPath.Child("dataSourceRef"), opts.AllowInvalidAPIGroupInDataSourceOrRef)...)
|
||||||
}
|
}
|
||||||
if spec.DataSourceRef != nil && spec.DataSourceRef.Namespace != nil && len(*spec.DataSourceRef.Namespace) > 0 {
|
if spec.DataSourceRef != nil && spec.DataSourceRef.Namespace != nil && len(*spec.DataSourceRef.Namespace) > 0 {
|
||||||
if spec.DataSource != nil {
|
if spec.DataSource != nil {
|
||||||
|
@ -996,6 +996,21 @@ func pvcTemplateWithAccessModes(accessModes []core.PersistentVolumeAccessMode) *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pvcWithDataSource(dataSource *core.TypedLocalObjectReference) *core.PersistentVolumeClaim {
|
||||||
|
return &core.PersistentVolumeClaim{
|
||||||
|
Spec: core.PersistentVolumeClaimSpec{
|
||||||
|
DataSource: dataSource,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func pvcWithDataSourceRef(ref *core.TypedObjectReference) *core.PersistentVolumeClaim {
|
||||||
|
return &core.PersistentVolumeClaim{
|
||||||
|
Spec: core.PersistentVolumeClaimSpec{
|
||||||
|
DataSourceRef: ref,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testLocalVolume(path string, affinity *core.VolumeNodeAffinity) core.PersistentVolumeSpec {
|
func testLocalVolume(path string, affinity *core.VolumeNodeAffinity) core.PersistentVolumeSpec {
|
||||||
return core.PersistentVolumeSpec{
|
return core.PersistentVolumeSpec{
|
||||||
Capacity: core.ResourceList{
|
Capacity: core.ResourceList{
|
||||||
@ -1545,6 +1560,7 @@ func testVolumeClaimStorageClassInAnnotationAndNilInSpec(name, namespace, scName
|
|||||||
func testValidatePVC(t *testing.T, ephemeral bool) {
|
func testValidatePVC(t *testing.T, ephemeral bool) {
|
||||||
invalidClassName := "-invalid-"
|
invalidClassName := "-invalid-"
|
||||||
validClassName := "valid"
|
validClassName := "valid"
|
||||||
|
invalidAPIGroup := "^invalid"
|
||||||
invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
|
invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
|
||||||
validMode := core.PersistentVolumeFilesystem
|
validMode := core.PersistentVolumeFilesystem
|
||||||
goodName := "foo"
|
goodName := "foo"
|
||||||
@ -1934,6 +1950,42 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
"invaild-apigroup-in-data-source": {
|
||||||
|
isExpectedFailure: true,
|
||||||
|
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []core.PersistentVolumeAccessMode{
|
||||||
|
core.ReadWriteOnce,
|
||||||
|
},
|
||||||
|
Resources: core.VolumeResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DataSource: &core.TypedLocalObjectReference{
|
||||||
|
APIGroup: &invalidAPIGroup,
|
||||||
|
Kind: "Foo",
|
||||||
|
Name: "foo1",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
"invaild-apigroup-in-data-source-ref": {
|
||||||
|
isExpectedFailure: true,
|
||||||
|
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []core.PersistentVolumeAccessMode{
|
||||||
|
core.ReadWriteOnce,
|
||||||
|
},
|
||||||
|
Resources: core.VolumeResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DataSourceRef: &core.TypedObjectReference{
|
||||||
|
APIGroup: &invalidAPIGroup,
|
||||||
|
Kind: "Foo",
|
||||||
|
Name: "foo1",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, scenario := range scenarios {
|
for name, scenario := range scenarios {
|
||||||
@ -2057,6 +2109,7 @@ func TestAlphaPVVolumeModeUpdate(t *testing.T) {
|
|||||||
func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
||||||
block := core.PersistentVolumeBlock
|
block := core.PersistentVolumeBlock
|
||||||
file := core.PersistentVolumeFilesystem
|
file := core.PersistentVolumeFilesystem
|
||||||
|
invaildAPIGroup := "^invalid"
|
||||||
|
|
||||||
validClaim := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
|
validClaim := testVolumeClaimWithStatus("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||||
AccessModes: []core.PersistentVolumeAccessMode{
|
AccessModes: []core.PersistentVolumeAccessMode{
|
||||||
@ -2427,6 +2480,42 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
invalidClaimDataSourceAPIGroup := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []core.PersistentVolumeAccessMode{
|
||||||
|
core.ReadWriteOnce,
|
||||||
|
},
|
||||||
|
VolumeMode: &file,
|
||||||
|
Resources: core.VolumeResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
VolumeName: "volume",
|
||||||
|
DataSource: &core.TypedLocalObjectReference{
|
||||||
|
APIGroup: &invaildAPIGroup,
|
||||||
|
Kind: "Foo",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
invalidClaimDataSourceRefAPIGroup := testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []core.PersistentVolumeAccessMode{
|
||||||
|
core.ReadWriteOnce,
|
||||||
|
},
|
||||||
|
VolumeMode: &file,
|
||||||
|
Resources: core.VolumeResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
VolumeName: "volume",
|
||||||
|
DataSourceRef: &core.TypedObjectReference{
|
||||||
|
APIGroup: &invaildAPIGroup,
|
||||||
|
Kind: "Foo",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
scenarios := map[string]struct {
|
scenarios := map[string]struct {
|
||||||
isExpectedFailure bool
|
isExpectedFailure bool
|
||||||
oldClaim *core.PersistentVolumeClaim
|
oldClaim *core.PersistentVolumeClaim
|
||||||
@ -2631,6 +2720,16 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
enableRecoverFromExpansion: true,
|
enableRecoverFromExpansion: true,
|
||||||
isExpectedFailure: true,
|
isExpectedFailure: true,
|
||||||
},
|
},
|
||||||
|
"allow-update-pvc-when-data-source-used": {
|
||||||
|
oldClaim: invalidClaimDataSourceAPIGroup,
|
||||||
|
newClaim: invalidClaimDataSourceAPIGroup,
|
||||||
|
isExpectedFailure: false,
|
||||||
|
},
|
||||||
|
"allow-update-pvc-when-data-source-ref-used": {
|
||||||
|
oldClaim: invalidClaimDataSourceRefAPIGroup,
|
||||||
|
newClaim: invalidClaimDataSourceRefAPIGroup,
|
||||||
|
isExpectedFailure: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, scenario := range scenarios {
|
for name, scenario := range scenarios {
|
||||||
@ -2651,6 +2750,8 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
||||||
|
invaildAPIGroup := "^invalid"
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
oldPvc *core.PersistentVolumeClaim
|
oldPvc *core.PersistentVolumeClaim
|
||||||
enableReadWriteOncePod bool
|
enableReadWriteOncePod bool
|
||||||
@ -2696,6 +2797,18 @@ func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
|
|||||||
EnableRecoverFromExpansionFailure: false,
|
EnableRecoverFromExpansionFailure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"invaild apiGroup in dataSource allowed because the old pvc is used": {
|
||||||
|
oldPvc: pvcWithDataSource(&core.TypedLocalObjectReference{APIGroup: &invaildAPIGroup}),
|
||||||
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
|
AllowInvalidAPIGroupInDataSourceOrRef: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"invaild apiGroup in dataSourceRef allowed because the old pvc is used": {
|
||||||
|
oldPvc: pvcWithDataSourceRef(&core.TypedObjectReference{APIGroup: &invaildAPIGroup}),
|
||||||
|
expectValidationOpts: PersistentVolumeClaimSpecValidationOptions{
|
||||||
|
AllowInvalidAPIGroupInDataSourceOrRef: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range tests {
|
for name, tc := range tests {
|
||||||
|
Loading…
Reference in New Issue
Block a user