mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	API
This commit is contained in:
		| @@ -104,7 +104,7 @@ func TestConfigMapController(t *testing.T) { | ||||
| 	// There should be 2 updates to add both the finalizers. | ||||
| 	updatedConfigMap := GetConfigMapFromChan(configmapUpdateChan) | ||||
| 	assert.True(t, configmapController.hasFinalizerFunc(updatedConfigMap, deletionhelper.FinalizerDeleteFromUnderlyingClusters)) | ||||
| 	assert.True(t, configmapController.hasFinalizerFunc(updatedConfigMap, metav1.FinalizerOrphan)) | ||||
| 	assert.True(t, configmapController.hasFinalizerFunc(updatedConfigMap, metav1.FinalizerOrphanDependents)) | ||||
|  | ||||
| 	// Verify that the configmap is created in underlying cluster1. | ||||
| 	createdConfigMap := GetConfigMapFromChan(cluster1CreateChan) | ||||
|   | ||||
| @@ -105,7 +105,7 @@ func TestDaemonSetController(t *testing.T) { | ||||
| 	// There should be an update to add both the finalizers. | ||||
| 	updatedDaemonSet := GetDaemonSetFromChan(daemonsetUpdateChan) | ||||
| 	assert.True(t, daemonsetController.hasFinalizerFunc(updatedDaemonSet, deletionhelper.FinalizerDeleteFromUnderlyingClusters)) | ||||
| 	assert.True(t, daemonsetController.hasFinalizerFunc(updatedDaemonSet, metav1.FinalizerOrphan)) | ||||
| 	assert.True(t, daemonsetController.hasFinalizerFunc(updatedDaemonSet, metav1.FinalizerOrphanDependents)) | ||||
| 	daemonset1 = *updatedDaemonSet | ||||
|  | ||||
| 	createdDaemonSet := GetDaemonSetFromChan(cluster1CreateChan) | ||||
|   | ||||
| @@ -146,7 +146,7 @@ func TestIngressController(t *testing.T) { | ||||
| 	// There should be an update to add both the finalizers. | ||||
| 	updatedIngress := GetIngressFromChan(t, fedIngressUpdateChan) | ||||
| 	assert.True(t, ingressController.hasFinalizerFunc(updatedIngress, deletionhelper.FinalizerDeleteFromUnderlyingClusters)) | ||||
| 	assert.True(t, ingressController.hasFinalizerFunc(updatedIngress, metav1.FinalizerOrphan), fmt.Sprintf("ingress does not have the orphan finalizer: %v", updatedIngress)) | ||||
| 	assert.True(t, ingressController.hasFinalizerFunc(updatedIngress, metav1.FinalizerOrphanDependents), fmt.Sprintf("ingress does not have the orphan finalizer: %v", updatedIngress)) | ||||
| 	fedIngress = *updatedIngress | ||||
|  | ||||
| 	t.Log("Checking that Ingress was correctly created in cluster 1") | ||||
| @@ -319,7 +319,7 @@ func WaitForFinalizersInFederationStore(ingressController *IngressController, st | ||||
| 			return false, err | ||||
| 		} | ||||
| 		ingress := obj.(*extensionsv1beta1.Ingress) | ||||
| 		if ingressController.hasFinalizerFunc(ingress, metav1.FinalizerOrphan) && | ||||
| 		if ingressController.hasFinalizerFunc(ingress, metav1.FinalizerOrphanDependents) && | ||||
| 			ingressController.hasFinalizerFunc(ingress, deletionhelper.FinalizerDeleteFromUnderlyingClusters) { | ||||
| 			return true, nil | ||||
| 		} | ||||
|   | ||||
| @@ -133,7 +133,7 @@ func TestNamespaceController(t *testing.T) { | ||||
| 	// Delete the namespace with orphan finalizer (let namespaces | ||||
| 	// in underlying clusters be as is). | ||||
| 	// TODO: Add a test without orphan finalizer. | ||||
| 	ns1.ObjectMeta.Finalizers = append(ns1.ObjectMeta.Finalizers, metav1.FinalizerOrphan) | ||||
| 	ns1.ObjectMeta.Finalizers = append(ns1.ObjectMeta.Finalizers, metav1.FinalizerOrphanDependents) | ||||
| 	ns1.DeletionTimestamp = &metav1.Time{Time: time.Now()} | ||||
| 	namespaceWatch.Modify(&ns1) | ||||
| 	assert.Equal(t, ns1.Name, GetStringFromChan(nsDeleteChan)) | ||||
|   | ||||
| @@ -105,7 +105,7 @@ func TestSecretController(t *testing.T) { | ||||
| 	// There should be an update to add both the finalizers. | ||||
| 	updatedSecret := GetSecretFromChan(secretUpdateChan) | ||||
| 	assert.True(t, secretController.hasFinalizerFunc(updatedSecret, deletionhelper.FinalizerDeleteFromUnderlyingClusters)) | ||||
| 	assert.True(t, secretController.hasFinalizerFunc(updatedSecret, metav1.FinalizerOrphan)) | ||||
| 	assert.True(t, secretController.hasFinalizerFunc(updatedSecret, metav1.FinalizerOrphanDependents)) | ||||
| 	secret1 = *updatedSecret | ||||
|  | ||||
| 	// Verify that the secret is created in underlying cluster1. | ||||
|   | ||||
| @@ -93,8 +93,8 @@ func (dh *DeletionHelper) EnsureFinalizers(obj runtime.Object) ( | ||||
| 	if !dh.hasFinalizerFunc(obj, FinalizerDeleteFromUnderlyingClusters) { | ||||
| 		finalizers = append(finalizers, FinalizerDeleteFromUnderlyingClusters) | ||||
| 	} | ||||
| 	if !dh.hasFinalizerFunc(obj, metav1.FinalizerOrphan) { | ||||
| 		finalizers = append(finalizers, metav1.FinalizerOrphan) | ||||
| 	if !dh.hasFinalizerFunc(obj, metav1.FinalizerOrphanDependents) { | ||||
| 		finalizers = append(finalizers, metav1.FinalizerOrphanDependents) | ||||
| 	} | ||||
| 	if len(finalizers) != 0 { | ||||
| 		glog.V(2).Infof("Adding finalizers %v to %s", finalizers, dh.objNameFunc(obj)) | ||||
| @@ -117,7 +117,7 @@ func (dh *DeletionHelper) HandleObjectInUnderlyingClusters(obj runtime.Object) ( | ||||
| 		glog.V(2).Infof("obj does not have %s finalizer. Nothing to do", FinalizerDeleteFromUnderlyingClusters) | ||||
| 		return obj, nil | ||||
| 	} | ||||
| 	hasOrphanFinalizer := dh.hasFinalizerFunc(obj, metav1.FinalizerOrphan) | ||||
| 	hasOrphanFinalizer := dh.hasFinalizerFunc(obj, metav1.FinalizerOrphanDependents) | ||||
| 	if hasOrphanFinalizer { | ||||
| 		glog.V(2).Infof("Found finalizer orphan. Nothing to do, just remove the finalizer") | ||||
| 		// If the obj has FinalizerOrphan finalizer, then we need to orphan the | ||||
| @@ -127,7 +127,7 @@ func (dh *DeletionHelper) HandleObjectInUnderlyingClusters(obj runtime.Object) ( | ||||
| 		if err != nil { | ||||
| 			return obj, err | ||||
| 		} | ||||
| 		return dh.removeFinalizerFunc(obj, metav1.FinalizerOrphan) | ||||
| 		return dh.removeFinalizerFunc(obj, metav1.FinalizerOrphanDependents) | ||||
| 	} | ||||
|  | ||||
| 	glog.V(2).Infof("Deleting obj %s from underlying clusters", objName) | ||||
|   | ||||
| @@ -244,7 +244,7 @@ func IsServiceIPRequested(service *Service) bool { | ||||
|  | ||||
| var standardFinalizers = sets.NewString( | ||||
| 	string(FinalizerKubernetes), | ||||
| 	metav1.FinalizerOrphan, | ||||
| 	metav1.FinalizerOrphanDependents, | ||||
| ) | ||||
|  | ||||
| // HasAnnotation returns a bool if passed in annotation exists | ||||
|   | ||||
| @@ -20,8 +20,6 @@ import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/google/gofuzz" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/api/meta" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| @@ -31,50 +29,6 @@ import ( | ||||
|  | ||||
| var _ metav1.Object = &metav1.ObjectMeta{} | ||||
|  | ||||
| func getObjectMetaAndOwnerReferences() (objectMeta metav1.ObjectMeta, metaOwnerReferences []metav1.OwnerReference) { | ||||
| 	fuzz.New().NilChance(.5).NumElements(1, 5).Fuzz(&objectMeta) | ||||
| 	references := objectMeta.OwnerReferences | ||||
| 	metaOwnerReferences = make([]metav1.OwnerReference, 0) | ||||
| 	for i := 0; i < len(references); i++ { | ||||
| 		metaOwnerReferences = append(metaOwnerReferences, metav1.OwnerReference{ | ||||
| 			Kind:       references[i].Kind, | ||||
| 			Name:       references[i].Name, | ||||
| 			UID:        references[i].UID, | ||||
| 			APIVersion: references[i].APIVersion, | ||||
| 			Controller: references[i].Controller, | ||||
| 		}) | ||||
| 	} | ||||
| 	if len(references) == 0 { | ||||
| 		objectMeta.OwnerReferences = make([]metav1.OwnerReference, 0) | ||||
| 	} | ||||
| 	return objectMeta, metaOwnerReferences | ||||
| } | ||||
|  | ||||
| func testGetOwnerReferences(t *testing.T) { | ||||
| 	meta, expected := getObjectMetaAndOwnerReferences() | ||||
| 	refs := meta.GetOwnerReferences() | ||||
| 	if !reflect.DeepEqual(refs, expected) { | ||||
| 		t.Errorf("expect %v\n got %v", expected, refs) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func testSetOwnerReferences(t *testing.T) { | ||||
| 	expected, newRefs := getObjectMetaAndOwnerReferences() | ||||
| 	objectMeta := &metav1.ObjectMeta{} | ||||
| 	objectMeta.SetOwnerReferences(newRefs) | ||||
| 	if !reflect.DeepEqual(expected.OwnerReferences, objectMeta.OwnerReferences) { | ||||
| 		t.Errorf("expect: %#v\n got: %#v", expected.OwnerReferences, objectMeta.OwnerReferences) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestAccessOwnerReferences(t *testing.T) { | ||||
| 	fuzzIter := 5 | ||||
| 	for i := 0; i < fuzzIter; i++ { | ||||
| 		testGetOwnerReferences(t) | ||||
| 		testSetOwnerReferences(t) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestAccessorImplementations(t *testing.T) { | ||||
| 	for _, gv := range api.Registry.EnabledVersions() { | ||||
| 		internalGV := schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal} | ||||
|   | ||||
| @@ -3000,6 +3000,20 @@ type Preconditions struct { | ||||
| 	UID *types.UID | ||||
| } | ||||
|  | ||||
| // DeletionPropagation decides whether and how garbage collection will be performed. | ||||
| type DeletionPropagation string | ||||
|  | ||||
| const ( | ||||
| 	// Orphans the dependents. | ||||
| 	DeletePropagationOrphan DeletionPropagation = "Orphan" | ||||
| 	// Deletes the object from the key-value store, the garbage collector will delete the dependents in the background. | ||||
| 	DeletePropagationBackground DeletionPropagation = "Background" | ||||
| 	// The object exists in the key-value store until the garbage collector deletes all the dependents whose ownerReference.blockOwnerDeletion=true from the key-value store. | ||||
| 	// API sever will put the "DeletingDependents" finalizer on the object, and sets its deletionTimestamp. | ||||
| 	// This policy is cascading, i.e., the dependents will be deleted with Foreground. | ||||
| 	DeletePropagationForeground DeletionPropagation = "Foreground" | ||||
| ) | ||||
|  | ||||
| // DeleteOptions may be provided when deleting an API object | ||||
| // DEPRECATED: This type has been moved to meta/v1 and will be removed soon. | ||||
| type DeleteOptions struct { | ||||
| @@ -3016,10 +3030,18 @@ type DeleteOptions struct { | ||||
| 	// +optional | ||||
| 	Preconditions *Preconditions | ||||
|  | ||||
| 	// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. | ||||
| 	// Should the dependent objects be orphaned. If true/false, the "orphan" | ||||
| 	// finalizer will be added to/removed from the object's finalizers list. | ||||
| 	// Either this field or PropagationPolicy may be set, but not both. | ||||
| 	// +optional | ||||
| 	OrphanDependents *bool | ||||
|  | ||||
| 	// Whether and how garbage collection will be performed. | ||||
| 	// Defaults to Default. | ||||
| 	// Either this field or OrphanDependents may be set, but not both. | ||||
| 	// +optional | ||||
| 	PropagationPolicy *DeletionPropagation | ||||
| } | ||||
|  | ||||
| // ListOptions is the query options to a standard REST list call, and has future support for | ||||
|   | ||||
| @@ -84,7 +84,7 @@ func IsServiceIPRequested(service *Service) bool { | ||||
|  | ||||
| var standardFinalizers = sets.NewString( | ||||
| 	string(FinalizerKubernetes), | ||||
| 	metav1.FinalizerOrphan, | ||||
| 	metav1.FinalizerOrphanDependents, | ||||
| ) | ||||
|  | ||||
| func IsStandardFinalizerName(str string) bool { | ||||
|   | ||||
| @@ -63,6 +63,10 @@ func (meta *ObjectMeta) GetOwnerReferences() []metav1.OwnerReference { | ||||
| 			value := *meta.OwnerReferences[i].Controller | ||||
| 			ret[i].Controller = &value | ||||
| 		} | ||||
| 		if meta.OwnerReferences[i].BlockOwnerDeletion != nil { | ||||
| 			value := *meta.OwnerReferences[i].BlockOwnerDeletion | ||||
| 			ret[i].BlockOwnerDeletion = &value | ||||
| 		} | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
| @@ -78,6 +82,10 @@ func (meta *ObjectMeta) SetOwnerReferences(references []metav1.OwnerReference) { | ||||
| 			value := *references[i].Controller | ||||
| 			newReferences[i].Controller = &value | ||||
| 		} | ||||
| 		if references[i].BlockOwnerDeletion != nil { | ||||
| 			value := *references[i].BlockOwnerDeletion | ||||
| 			newReferences[i].BlockOwnerDeletion = &value | ||||
| 		} | ||||
| 	} | ||||
| 	meta.OwnerReferences = newReferences | ||||
| } | ||||
|   | ||||
| @@ -3432,6 +3432,20 @@ type Preconditions struct { | ||||
| 	UID *types.UID `json:"uid,omitempty" protobuf:"bytes,1,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"` | ||||
| } | ||||
|  | ||||
| // DeletionPropagation decides if a deletion will propagate to the dependents of the object, and how the garbage collector will handle the propagation. | ||||
| type DeletionPropagation string | ||||
|  | ||||
| const ( | ||||
| 	// Orphans the dependents. | ||||
| 	DeletePropagationOrphan DeletionPropagation = "Orphan" | ||||
| 	// Deletes the object from the key-value store, the garbage collector will delete the dependents in the background. | ||||
| 	DeletePropagationBackground DeletionPropagation = "Background" | ||||
| 	// The object exists in the key-value store until the garbage collector deletes all the dependents whose ownerReference.blockOwnerDeletion=true from the key-value store. | ||||
| 	// API sever will put the "DeletingDependents" finalizer on the object, and sets its deletionTimestamp. | ||||
| 	// This policy is cascading, i.e., the dependents will be deleted with Foreground. | ||||
| 	DeletePropagationForeground DeletionPropagation = "Foreground" | ||||
| ) | ||||
|  | ||||
| // DeleteOptions may be provided when deleting an API object | ||||
| // DEPRECATED: This type has been moved to meta/v1 and will be removed soon. | ||||
| // +k8s:openapi-gen=false | ||||
| @@ -3450,10 +3464,18 @@ type DeleteOptions struct { | ||||
| 	// +optional | ||||
| 	Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"` | ||||
|  | ||||
| 	// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. | ||||
| 	// Should the dependent objects be orphaned. If true/false, the "orphan" | ||||
| 	// finalizer will be added to/removed from the object's finalizers list. | ||||
| 	// Either this field or PropagationPolicy may be set, but not both. | ||||
| 	// +optional | ||||
| 	OrphanDependents *bool `json:"orphanDependents,omitempty" protobuf:"varint,3,opt,name=orphanDependents"` | ||||
|  | ||||
| 	// Whether and how garbage collection will be performed. | ||||
| 	// Defaults to Default. | ||||
| 	// Either this field or OrphanDependents may be set, but not both. | ||||
| 	// +optional | ||||
| 	PropagationPolicy *DeletionPropagation | ||||
| } | ||||
|  | ||||
| // ListOptions is the query options to a standard REST list call. | ||||
|   | ||||
| @@ -295,7 +295,6 @@ func ValidateObjectMeta(meta *metav1.ObjectMeta, requiresNamespace bool, nameFn | ||||
| 	for i := range meta.Finalizers { | ||||
| 		allErrs = append(allErrs, validateKubeFinalizerName(string(meta.Finalizers[i]), fldPath.Child("finalizers").Index(i))...) | ||||
| 	} | ||||
|  | ||||
| 	return allErrs | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -335,7 +335,7 @@ type MyAPIObject2 struct { | ||||
| 	metav1.ObjectMeta | ||||
| } | ||||
|  | ||||
| func getObjectMetaAndOwnerRefereneces() (myAPIObject2 MyAPIObject2, metaOwnerReferences []metav1.OwnerReference) { | ||||
| func getObjectMetaAndOwnerReferences() (myAPIObject2 MyAPIObject2, metaOwnerReferences []metav1.OwnerReference) { | ||||
| 	fuzz.New().NilChance(.5).NumElements(1, 5).Fuzz(&myAPIObject2) | ||||
| 	references := myAPIObject2.ObjectMeta.OwnerReferences | ||||
| 	// This is necessary for the test to pass because the getter will return a | ||||
| @@ -348,6 +348,7 @@ func getObjectMetaAndOwnerRefereneces() (myAPIObject2 MyAPIObject2, metaOwnerRef | ||||
| 			UID:                references[i].UID, | ||||
| 			APIVersion:         references[i].APIVersion, | ||||
| 			Controller:         references[i].Controller, | ||||
| 			BlockOwnerDeletion: references[i].BlockOwnerDeletion, | ||||
| 		}) | ||||
| 	} | ||||
| 	if len(references) == 0 { | ||||
| @@ -359,7 +360,7 @@ func getObjectMetaAndOwnerRefereneces() (myAPIObject2 MyAPIObject2, metaOwnerRef | ||||
| } | ||||
|  | ||||
| func testGetOwnerReferences(t *testing.T) { | ||||
| 	obj, expected := getObjectMetaAndOwnerRefereneces() | ||||
| 	obj, expected := getObjectMetaAndOwnerReferences() | ||||
| 	accessor, err := meta.Accessor(&obj) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| @@ -371,7 +372,7 @@ func testGetOwnerReferences(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func testSetOwnerReferences(t *testing.T) { | ||||
| 	expected, references := getObjectMetaAndOwnerRefereneces() | ||||
| 	expected, references := getObjectMetaAndOwnerReferences() | ||||
| 	obj := MyAPIObject2{} | ||||
| 	accessor, err := meta.Accessor(&obj) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -123,6 +123,7 @@ func TestDecode(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestUnstructuredGetters(t *testing.T) { | ||||
| 	trueVar := true | ||||
| 	unstruct := unstructured.Unstructured{ | ||||
| 		Object: map[string]interface{}{ | ||||
| 			"kind":       "test_kind", | ||||
| @@ -154,6 +155,10 @@ func TestUnstructuredGetters(t *testing.T) { | ||||
| 						"name":       "podb", | ||||
| 						"apiVersion": "v1", | ||||
| 						"uid":        "2", | ||||
| 						// though these fields are of type *bool, but when | ||||
| 						// decoded from JSON, they are unmarshalled as bool. | ||||
| 						"controller":         true, | ||||
| 						"blockOwnerDeletion": true, | ||||
| 					}, | ||||
| 				}, | ||||
| 				"finalizers": []interface{}{ | ||||
| @@ -225,6 +230,8 @@ func TestUnstructuredGetters(t *testing.T) { | ||||
| 			Name:               "podb", | ||||
| 			APIVersion:         "v1", | ||||
| 			UID:                "2", | ||||
| 			Controller:         &trueVar, | ||||
| 			BlockOwnerDeletion: &trueVar, | ||||
| 		}, | ||||
| 	} | ||||
| 	if got, want := refs, expectedOwnerReferences; !reflect.DeepEqual(got, want) { | ||||
| @@ -268,6 +275,7 @@ func TestUnstructuredSetters(t *testing.T) { | ||||
| 						"apiVersion":         "v1", | ||||
| 						"uid":                "1", | ||||
| 						"controller":         (*bool)(nil), | ||||
| 						"blockOwnerDeletion": (*bool)(nil), | ||||
| 					}, | ||||
| 					{ | ||||
| 						"kind":               "Pod", | ||||
| @@ -275,6 +283,7 @@ func TestUnstructuredSetters(t *testing.T) { | ||||
| 						"apiVersion":         "v1", | ||||
| 						"uid":                "2", | ||||
| 						"controller":         &trueVar, | ||||
| 						"blockOwnerDeletion": &trueVar, | ||||
| 					}, | ||||
| 				}, | ||||
| 				"finalizers": []interface{}{ | ||||
| @@ -312,6 +321,7 @@ func TestUnstructuredSetters(t *testing.T) { | ||||
| 			APIVersion:         "v1", | ||||
| 			UID:                "2", | ||||
| 			Controller:         &trueVar, | ||||
| 			BlockOwnerDeletion: &trueVar, | ||||
| 		}, | ||||
| 	} | ||||
| 	unstruct.SetOwnerReferences(newOwnerReferences) | ||||
|   | ||||
| @@ -143,7 +143,7 @@ func (r *REST) Delete(ctx genericapirequest.Context, name string, options *metav | ||||
| 					newFinalizers := []string{} | ||||
| 					for i := range existingNamespace.ObjectMeta.Finalizers { | ||||
| 						finalizer := existingNamespace.ObjectMeta.Finalizers[i] | ||||
| 						if string(finalizer) != metav1.FinalizerOrphan { | ||||
| 						if string(finalizer) != metav1.FinalizerOrphanDependents { | ||||
| 							newFinalizers = append(newFinalizers, finalizer) | ||||
| 						} | ||||
| 					} | ||||
|   | ||||
| @@ -334,6 +334,14 @@ func extractFromOwnerReference(v reflect.Value, o *metav1.OwnerReference) error | ||||
| 		controller := *controllerPtr | ||||
| 		o.Controller = &controller | ||||
| 	} | ||||
| 	var blockOwnerDeletionPtr *bool | ||||
| 	if err := runtime.Field(v, "BlockOwnerDeletion", &blockOwnerDeletionPtr); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if blockOwnerDeletionPtr != nil { | ||||
| 		block := *blockOwnerDeletionPtr | ||||
| 		o.BlockOwnerDeletion = &block | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -357,6 +365,12 @@ func setOwnerReference(v reflect.Value, o *metav1.OwnerReference) error { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	if o.BlockOwnerDeletion != nil { | ||||
| 		block := *(o.BlockOwnerDeletion) | ||||
| 		if err := runtime.SetField(&block, v, "BlockOwnerDeletion"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -171,8 +171,26 @@ func ValidateObjectMeta(meta *metav1.ObjectMeta, requiresNamespace bool, nameFn | ||||
| 	allErrs = append(allErrs, v1validation.ValidateLabels(meta.Labels, fldPath.Child("labels"))...) | ||||
| 	allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, fldPath.Child("annotations"))...) | ||||
| 	allErrs = append(allErrs, ValidateOwnerReferences(meta.OwnerReferences, fldPath.Child("ownerReferences"))...) | ||||
| 	for _, finalizer := range meta.Finalizers { | ||||
| 		allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath.Child("finalizers"))...) | ||||
| 	allErrs = append(allErrs, ValidateFinalizers(meta.Finalizers, fldPath.Child("finalizers"))...) | ||||
| 	return allErrs | ||||
| } | ||||
|  | ||||
| // ValidateFinalizers tests if the finalizers name are valid, and if there are conflicting finalizers. | ||||
| func ValidateFinalizers(finalizers []string, fldPath *field.Path) field.ErrorList { | ||||
| 	allErrs := field.ErrorList{} | ||||
| 	hasFinalizerOrphanDependents := false | ||||
| 	hasFinalizerDeleteDependents := false | ||||
| 	for _, finalizer := range finalizers { | ||||
| 		allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath)...) | ||||
| 		if finalizer == metav1.FinalizerOrphanDependents { | ||||
| 			hasFinalizerOrphanDependents = true | ||||
| 		} | ||||
| 		if finalizer == metav1.FinalizerDeleteDependents { | ||||
| 			hasFinalizerDeleteDependents = true | ||||
| 		} | ||||
| 	} | ||||
| 	if hasFinalizerDeleteDependents && hasFinalizerOrphanDependents { | ||||
| 		allErrs = append(allErrs, field.Invalid(fldPath, finalizers, fmt.Sprintf("finalizer %s and %s cannot be both set", metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents))) | ||||
| 	} | ||||
| 	return allErrs | ||||
| } | ||||
|   | ||||
| @@ -277,6 +277,28 @@ func TestValidateFinalizersUpdate(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestValidateFinalizersPreventConflictingFinalizers(t *testing.T) { | ||||
| 	testcases := map[string]struct { | ||||
| 		ObjectMeta  metav1.ObjectMeta | ||||
| 		ExpectedErr string | ||||
| 	}{ | ||||
| 		"conflicting finalizers": { | ||||
| 			ObjectMeta:  metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents}}, | ||||
| 			ExpectedErr: "cannot be both set", | ||||
| 		}, | ||||
| 	} | ||||
| 	for name, tc := range testcases { | ||||
| 		errs := ValidateObjectMeta(&tc.ObjectMeta, false, NameIsDNSSubdomain, field.NewPath("field")) | ||||
| 		if len(errs) == 0 { | ||||
| 			if len(tc.ExpectedErr) != 0 { | ||||
| 				t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr) | ||||
| 			} | ||||
| 		} else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) { | ||||
| 			t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) { | ||||
| 	now := metav1.NewTime(time.Unix(1000, 0).UTC()) | ||||
| 	later := metav1.NewTime(time.Unix(2000, 0).UTC()) | ||||
|   | ||||
| @@ -174,6 +174,10 @@ func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference { | ||||
| 			value := *meta.OwnerReferences[i].Controller | ||||
| 			ret[i].Controller = &value | ||||
| 		} | ||||
| 		if meta.OwnerReferences[i].BlockOwnerDeletion != nil { | ||||
| 			value := *meta.OwnerReferences[i].BlockOwnerDeletion | ||||
| 			ret[i].BlockOwnerDeletion = &value | ||||
| 		} | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
| @@ -189,6 +193,10 @@ func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) { | ||||
| 			value := *references[i].Controller | ||||
| 			newReferences[i].Controller = &value | ||||
| 		} | ||||
| 		if references[i].BlockOwnerDeletion != nil { | ||||
| 			value := *references[i].BlockOwnerDeletion | ||||
| 			newReferences[i].BlockOwnerDeletion = &value | ||||
| 		} | ||||
| 	} | ||||
| 	meta.OwnerReferences = newReferences | ||||
| } | ||||
|   | ||||
| @@ -73,7 +73,8 @@ type ListMeta struct { | ||||
|  | ||||
| // These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here | ||||
| const ( | ||||
| 	FinalizerOrphan string = "orphan" | ||||
| 	FinalizerOrphanDependents string = "orphan" | ||||
| 	FinalizerDeleteDependents string = "foregroundDeletion" | ||||
| ) | ||||
|  | ||||
| // ObjectMeta is metadata that all persisted resources must have, which includes all objects | ||||
| @@ -255,6 +256,14 @@ type OwnerReference struct { | ||||
| 	// If true, this reference points to the managing controller. | ||||
| 	// +optional | ||||
| 	Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"` | ||||
| 	// If true, AND if the owner has the "foregroundDeletion" finalizer, then | ||||
| 	// the owner cannot be deleted from the key-value store until this | ||||
| 	// reference is removed. | ||||
| 	// Defaults to false. | ||||
| 	// To set this field, a user needs "delete" permission of the owner, | ||||
| 	// otherwise 422 (Unprocessable Entity) will be returned. | ||||
| 	// +optional | ||||
| 	BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"` | ||||
| } | ||||
|  | ||||
| // ListOptions is the query options to a standard REST list call. | ||||
| @@ -305,6 +314,24 @@ type GetOptions struct { | ||||
| 	ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,1,opt,name=resourceVersion"` | ||||
| } | ||||
|  | ||||
| // DeletionPropagation decides if a deletion will propagate to the dependents of | ||||
| // the object, and how the garbage collector will handle the propagation. | ||||
| type DeletionPropagation string | ||||
|  | ||||
| const ( | ||||
| 	// Orphans the dependents. | ||||
| 	DeletePropagationOrphan DeletionPropagation = "Orphan" | ||||
| 	// Deletes the object from the key-value store, the garbage collector will | ||||
| 	// delete the dependents in the background. | ||||
| 	DeletePropagationBackground DeletionPropagation = "Background" | ||||
| 	// The object exists in the key-value store until the garbage collector | ||||
| 	// deletes all the dependents whose ownerReference.blockOwnerDeletion=true | ||||
| 	// from the key-value store.  API sever will put the "foregroundDeletion" | ||||
| 	// finalizer on the object, and sets its deletionTimestamp.  This policy is | ||||
| 	// cascading, i.e., the dependents will be deleted with Foreground. | ||||
| 	DeletePropagationForeground DeletionPropagation = "Foreground" | ||||
| ) | ||||
|  | ||||
| // DeleteOptions may be provided when deleting an API object. | ||||
| type DeleteOptions struct { | ||||
| 	TypeMeta `json:",inline"` | ||||
| @@ -321,10 +348,18 @@ type DeleteOptions struct { | ||||
| 	// +optional | ||||
| 	Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"` | ||||
|  | ||||
| 	// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. | ||||
| 	// Should the dependent objects be orphaned. If true/false, the "orphan" | ||||
| 	// finalizer will be added to/removed from the object's finalizers list. | ||||
| 	// Either this field or PropagationPolicy may be set, but not both. | ||||
| 	// +optional | ||||
| 	OrphanDependents *bool `json:"orphanDependents,omitempty" protobuf:"varint,3,opt,name=orphanDependents"` | ||||
|  | ||||
| 	// Whether and how garbage collection will be performed. | ||||
| 	// Defaults to Default. | ||||
| 	// Either this field or OrphanDependents may be set, but not both. | ||||
| 	// +optional | ||||
| 	PropagationPolicy *DeletionPropagation `json:"propagationPolicy,omitempty" protobuf:"varint,4,opt,name=propagationPolicy"` | ||||
| } | ||||
|  | ||||
| // Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out. | ||||
|   | ||||
| @@ -204,14 +204,23 @@ func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) { | ||||
|  | ||||
| func extractOwnerReference(src interface{}) metav1.OwnerReference { | ||||
| 	v := src.(map[string]interface{}) | ||||
| 	controllerPtr, ok := (getNestedField(v, "controller")).(*bool) | ||||
| 	// though this field is a *bool, but when decoded from JSON, it's | ||||
| 	// unmarshalled as bool. | ||||
| 	var controllerPtr *bool | ||||
| 	controller, ok := (getNestedField(v, "controller")).(bool) | ||||
| 	if !ok { | ||||
| 		controllerPtr = nil | ||||
| 	} else { | ||||
| 		if controllerPtr != nil { | ||||
| 			controller := *controllerPtr | ||||
| 			controllerPtr = &controller | ||||
| 		controllerCopy := controller | ||||
| 		controllerPtr = &controllerCopy | ||||
| 	} | ||||
| 	var blockOwnerDeletionPtr *bool | ||||
| 	blockOwnerDeletion, ok := (getNestedField(v, "blockOwnerDeletion")).(bool) | ||||
| 	if !ok { | ||||
| 		blockOwnerDeletionPtr = nil | ||||
| 	} else { | ||||
| 		blockOwnerDeletionCopy := blockOwnerDeletion | ||||
| 		blockOwnerDeletionPtr = &blockOwnerDeletionCopy | ||||
| 	} | ||||
| 	return metav1.OwnerReference{ | ||||
| 		Kind:               getNestedString(v, "kind"), | ||||
| @@ -219,6 +228,7 @@ func extractOwnerReference(src interface{}) metav1.OwnerReference { | ||||
| 		APIVersion:         getNestedString(v, "apiVersion"), | ||||
| 		UID:                (types.UID)(getNestedString(v, "uid")), | ||||
| 		Controller:         controllerPtr, | ||||
| 		BlockOwnerDeletion: blockOwnerDeletionPtr, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -229,11 +239,17 @@ func setOwnerReference(src metav1.OwnerReference) map[string]interface{} { | ||||
| 		controller := *controllerPtr | ||||
| 		controllerPtr = &controller | ||||
| 	} | ||||
| 	blockOwnerDeletionPtr := src.BlockOwnerDeletion | ||||
| 	if blockOwnerDeletionPtr != nil { | ||||
| 		blockOwnerDeletion := *blockOwnerDeletionPtr | ||||
| 		blockOwnerDeletionPtr = &blockOwnerDeletion | ||||
| 	} | ||||
| 	setNestedField(ret, src.Kind, "kind") | ||||
| 	setNestedField(ret, src.Name, "name") | ||||
| 	setNestedField(ret, src.APIVersion, "apiVersion") | ||||
| 	setNestedField(ret, string(src.UID), "uid") | ||||
| 	setNestedField(ret, controllerPtr, "controller") | ||||
| 	setNestedField(ret, blockOwnerDeletionPtr, "blockOwnerDeletion") | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,8 @@ limitations under the License. | ||||
| package validation | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/util/validation" | ||||
| 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||
| @@ -72,3 +74,17 @@ func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorLi | ||||
| 	} | ||||
| 	return allErrs | ||||
| } | ||||
|  | ||||
| func ValidateDeleteOptions(options *metav1.DeleteOptions) field.ErrorList { | ||||
| 	allErrs := field.ErrorList{} | ||||
| 	if options.OrphanDependents != nil && options.PropagationPolicy != nil { | ||||
| 		allErrs = append(allErrs, field.Invalid(field.NewPath(""), options, "OrphanDependents and DeletionPropagation cannot be both set")) | ||||
| 	} | ||||
| 	if options.PropagationPolicy != nil && | ||||
| 		*options.PropagationPolicy != metav1.DeletePropagationForeground && | ||||
| 		*options.PropagationPolicy != metav1.DeletePropagationBackground && | ||||
| 		*options.PropagationPolicy != metav1.DeletePropagationOrphan { | ||||
| 		allErrs = append(allErrs, field.Invalid(field.NewPath(""), options, fmt.Sprintf("DeletionPropagation need to be one of %q, %q, %q or nil", metav1.DeletePropagationForeground, metav1.DeletePropagationBackground, metav1.DeletePropagationOrphan))) | ||||
| 	} | ||||
| 	return allErrs | ||||
| } | ||||
|   | ||||
| @@ -244,7 +244,7 @@ func IsServiceIPRequested(service *Service) bool { | ||||
|  | ||||
| var standardFinalizers = sets.NewString( | ||||
| 	string(FinalizerKubernetes), | ||||
| 	metav1.FinalizerOrphan, | ||||
| 	metav1.FinalizerOrphanDependents, | ||||
| ) | ||||
|  | ||||
| // HasAnnotation returns a bool if passed in annotation exists | ||||
|   | ||||
| @@ -84,7 +84,7 @@ func IsServiceIPRequested(service *Service) bool { | ||||
|  | ||||
| var standardFinalizers = sets.NewString( | ||||
| 	string(FinalizerKubernetes), | ||||
| 	metav1.FinalizerOrphan, | ||||
| 	metav1.FinalizerOrphanDependents, | ||||
| ) | ||||
|  | ||||
| func IsStandardFinalizerName(str string) bool { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Chao Xu
					Chao Xu