mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +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