mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-01 02:38:12 +00:00 
			
		
		
		
	Merge pull request #36251 from screeley44/k8-immutable-scannotation
Automatic merge from submit-queue Make pvc storage class annotation immutable after create Fixes #34516 Added immutable check in validation.go ``` # Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # # persistentvolumeclaims "gce-claim-storageclass" was not valid: # * metadata.annotations.volume.beta.kubernetes.io/storage-class: Invalid value: "slow2": field is immutable # ```
This commit is contained in:
		| @@ -30,6 +30,7 @@ go_library( | ||||
|         "//pkg/api/util:go_default_library", | ||||
|         "//pkg/api/v1:go_default_library", | ||||
|         "//pkg/apimachinery/registered:go_default_library", | ||||
|         "//pkg/apis/storage/util:go_default_library", | ||||
|         "//pkg/capabilities:go_default_library", | ||||
|         "//pkg/labels:go_default_library", | ||||
|         "//pkg/runtime:go_default_library", | ||||
| @@ -75,6 +76,7 @@ go_test( | ||||
|         "//pkg/api/unversioned:go_default_library", | ||||
|         "//pkg/apimachinery/registered:go_default_library", | ||||
|         "//pkg/apis/extensions:go_default_library", | ||||
|         "//pkg/apis/storage/util:go_default_library", | ||||
|         "//pkg/capabilities:go_default_library", | ||||
|         "//pkg/runtime:go_default_library", | ||||
|         "//pkg/security/apparmor:go_default_library", | ||||
|   | ||||
| @@ -34,6 +34,7 @@ import ( | ||||
| 	apiservice "k8s.io/kubernetes/pkg/api/service" | ||||
| 	unversionedvalidation "k8s.io/kubernetes/pkg/api/unversioned/validation" | ||||
| 	"k8s.io/kubernetes/pkg/api/v1" | ||||
| 	storageutil "k8s.io/kubernetes/pkg/apis/storage/util" | ||||
| 	"k8s.io/kubernetes/pkg/capabilities" | ||||
| 	"k8s.io/kubernetes/pkg/labels" | ||||
| 	"k8s.io/kubernetes/pkg/runtime/schema" | ||||
| @@ -351,6 +352,15 @@ func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) fie | ||||
| 	return allErrs | ||||
| } | ||||
|  | ||||
| func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string, fldPath *field.Path) field.ErrorList { | ||||
| 	allErrs := field.ErrorList{} | ||||
|  | ||||
| 	if oldVal != newVal { | ||||
| 		allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations", annotation), newVal, fieldImmutableErrorMsg)) | ||||
| 	} | ||||
| 	return allErrs | ||||
| } | ||||
|  | ||||
| // ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already | ||||
| // been performed. | ||||
| // It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before. | ||||
| @@ -1262,11 +1272,15 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *api.PersistentVolumeCla | ||||
| 		oldPvc.Spec.VolumeName = newPvc.Spec.VolumeName | ||||
| 		defer func() { oldPvc.Spec.VolumeName = "" }() | ||||
| 	} | ||||
| 	// changes to Spec are not allowed, but updates to label/annotations are OK. | ||||
| 	// changes to Spec are not allowed, but updates to label/and some annotations are OK. | ||||
| 	// no-op updates pass validation. | ||||
| 	if !api.Semantic.DeepEqual(newPvc.Spec, oldPvc.Spec) { | ||||
| 		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "field is immutable after creation")) | ||||
| 	} | ||||
|  | ||||
| 	// storageclass annotation should be immutable after creation | ||||
| 	allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[storageutil.StorageClassAnnotation], oldPvc.ObjectMeta.Annotations[storageutil.StorageClassAnnotation], storageutil.StorageClassAnnotation, field.NewPath("metadata"))...) | ||||
|  | ||||
| 	newPvc.Status = oldPvc.Status | ||||
| 	return allErrs | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import ( | ||||
| 	"k8s.io/kubernetes/pkg/api/service" | ||||
| 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||
| 	"k8s.io/kubernetes/pkg/apimachinery/registered" | ||||
| 	storageutil "k8s.io/kubernetes/pkg/apis/storage/util" | ||||
| 	"k8s.io/kubernetes/pkg/capabilities" | ||||
| 	"k8s.io/kubernetes/pkg/security/apparmor" | ||||
| 	"k8s.io/kubernetes/pkg/util/intstr" | ||||
| @@ -661,6 +662,36 @@ func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeCla | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func testVolumeClaimStorageClass(name string, namespace string, annval string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim { | ||||
| 	annotations := map[string]string{ | ||||
| 		storageutil.StorageClassAnnotation: annval, | ||||
| 	} | ||||
|  | ||||
| 	return &api.PersistentVolumeClaim{ | ||||
| 		ObjectMeta: api.ObjectMeta{ | ||||
| 			Name:        name, | ||||
| 			Namespace:   namespace, | ||||
| 			Annotations: annotations, | ||||
| 		}, | ||||
| 		Spec: spec, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func testVolumeClaimAnnotation(name string, namespace string, ann string, annval string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim { | ||||
| 	annotations := map[string]string{ | ||||
| 		ann: annval, | ||||
| 	} | ||||
|  | ||||
| 	return &api.PersistentVolumeClaim{ | ||||
| 		ObjectMeta: api.ObjectMeta{ | ||||
| 			Name:        name, | ||||
| 			Namespace:   namespace, | ||||
| 			Annotations: annotations, | ||||
| 		}, | ||||
| 		Spec: spec, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestValidatePersistentVolumeClaim(t *testing.T) { | ||||
| 	scenarios := map[string]struct { | ||||
| 		isExpectedFailure bool | ||||
| @@ -814,6 +845,26 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) { | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	validClaimStorageClass := testVolumeClaimStorageClass("foo", "ns", "fast", api.PersistentVolumeClaimSpec{ | ||||
| 		AccessModes: []api.PersistentVolumeAccessMode{ | ||||
| 			api.ReadOnlyMany, | ||||
| 		}, | ||||
| 		Resources: api.ResourceRequirements{ | ||||
| 			Requests: api.ResourceList{ | ||||
| 				api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	validClaimAnnotation := testVolumeClaimAnnotation("foo", "ns", "description", "foo-description", api.PersistentVolumeClaimSpec{ | ||||
| 		AccessModes: []api.PersistentVolumeAccessMode{ | ||||
| 			api.ReadOnlyMany, | ||||
| 		}, | ||||
| 		Resources: api.ResourceRequirements{ | ||||
| 			Requests: api.ResourceList{ | ||||
| 				api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| 	validUpdateClaim := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ | ||||
| 		AccessModes: []api.PersistentVolumeAccessMode{ | ||||
| 			api.ReadWriteOnce, | ||||
| @@ -849,6 +900,40 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) { | ||||
| 		}, | ||||
| 		VolumeName: "volume", | ||||
| 	}) | ||||
| 	invalidUpdateClaimStorageClass := testVolumeClaimStorageClass("foo", "ns", "fast2", api.PersistentVolumeClaimSpec{ | ||||
| 		AccessModes: []api.PersistentVolumeAccessMode{ | ||||
| 			api.ReadOnlyMany, | ||||
| 		}, | ||||
| 		Resources: api.ResourceRequirements{ | ||||
| 			Requests: api.ResourceList{ | ||||
| 				api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		VolumeName: "volume", | ||||
| 	}) | ||||
| 	validUpdateClaimMutableAnnotation := testVolumeClaimAnnotation("foo", "ns", "description", "updated-or-added-foo-description", api.PersistentVolumeClaimSpec{ | ||||
| 		AccessModes: []api.PersistentVolumeAccessMode{ | ||||
| 			api.ReadOnlyMany, | ||||
| 		}, | ||||
| 		Resources: api.ResourceRequirements{ | ||||
| 			Requests: api.ResourceList{ | ||||
| 				api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		VolumeName: "volume", | ||||
| 	}) | ||||
| 	validAddClaimAnnotation := testVolumeClaimAnnotation("foo", "ns", "description", "updated-or-added-foo-description", api.PersistentVolumeClaimSpec{ | ||||
| 		AccessModes: []api.PersistentVolumeAccessMode{ | ||||
| 			api.ReadWriteOnce, | ||||
| 			api.ReadOnlyMany, | ||||
| 		}, | ||||
| 		Resources: api.ResourceRequirements{ | ||||
| 			Requests: api.ResourceList{ | ||||
| 				api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		VolumeName: "volume", | ||||
| 	}) | ||||
| 	scenarios := map[string]struct { | ||||
| 		isExpectedFailure bool | ||||
| 		oldClaim          *api.PersistentVolumeClaim | ||||
| @@ -874,6 +959,21 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) { | ||||
| 			oldClaim:          validUpdateClaim, | ||||
| 			newClaim:          invalidUpdateClaimAccessModes, | ||||
| 		}, | ||||
| 		"invalid-update-change-storage-class-annotation-after-creation": { | ||||
| 			isExpectedFailure: true, | ||||
| 			oldClaim:          validClaimStorageClass, | ||||
| 			newClaim:          invalidUpdateClaimStorageClass, | ||||
| 		}, | ||||
| 		"valid-update-mutable-annotation": { | ||||
| 			isExpectedFailure: false, | ||||
| 			oldClaim:          validClaimAnnotation, | ||||
| 			newClaim:          validUpdateClaimMutableAnnotation, | ||||
| 		}, | ||||
| 		"valid-update-add-annotation": { | ||||
| 			isExpectedFailure: false, | ||||
| 			oldClaim:          validClaim, | ||||
| 			newClaim:          validAddClaimAnnotation, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for name, scenario := range scenarios { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue