This commit is contained in:
Chao Xu 2017-02-23 11:14:55 -08:00
parent e4aa9db258
commit 5bfacf59f6
25 changed files with 247 additions and 102 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -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))

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -84,7 +84,7 @@ func IsServiceIPRequested(service *Service) bool {
var standardFinalizers = sets.NewString(
string(FinalizerKubernetes),
metav1.FinalizerOrphan,
metav1.FinalizerOrphanDependents,
)
func IsStandardFinalizerName(str string) bool {

View File

@ -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
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
@ -343,11 +343,12 @@ func getObjectMetaAndOwnerRefereneces() (myAPIObject2 MyAPIObject2, metaOwnerRef
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,
Kind: references[i].Kind,
Name: references[i].Name,
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 {

View File

@ -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{}{
@ -221,10 +226,12 @@ func TestUnstructuredGetters(t *testing.T) {
UID: "1",
},
{
Kind: "Pod",
Name: "podb",
APIVersion: "v1",
UID: "2",
Kind: "Pod",
Name: "podb",
APIVersion: "v1",
UID: "2",
Controller: &trueVar,
BlockOwnerDeletion: &trueVar,
},
}
if got, want := refs, expectedOwnerReferences; !reflect.DeepEqual(got, want) {
@ -263,18 +270,20 @@ func TestUnstructuredSetters(t *testing.T) {
},
"ownerReferences": []map[string]interface{}{
{
"kind": "Pod",
"name": "poda",
"apiVersion": "v1",
"uid": "1",
"controller": (*bool)(nil),
"kind": "Pod",
"name": "poda",
"apiVersion": "v1",
"uid": "1",
"controller": (*bool)(nil),
"blockOwnerDeletion": (*bool)(nil),
},
{
"kind": "Pod",
"name": "podb",
"apiVersion": "v1",
"uid": "2",
"controller": &trueVar,
"kind": "Pod",
"name": "podb",
"apiVersion": "v1",
"uid": "2",
"controller": &trueVar,
"blockOwnerDeletion": &trueVar,
},
},
"finalizers": []interface{}{
@ -307,11 +316,12 @@ func TestUnstructuredSetters(t *testing.T) {
UID: "1",
},
{
Kind: "Pod",
Name: "podb",
APIVersion: "v1",
UID: "2",
Controller: &trueVar,
Kind: "Pod",
Name: "podb",
APIVersion: "v1",
UID: "2",
Controller: &trueVar,
BlockOwnerDeletion: &trueVar,
},
}
unstruct.SetOwnerReferences(newOwnerReferences)

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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())

View File

@ -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
}

View File

@ -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.

View File

@ -204,21 +204,31 @@ 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"),
Name: getNestedString(v, "name"),
APIVersion: getNestedString(v, "apiVersion"),
UID: (types.UID)(getNestedString(v, "uid")),
Controller: controllerPtr,
Kind: getNestedString(v, "kind"),
Name: getNestedString(v, "name"),
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
}

View File

@ -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
}

View File

@ -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

View File

@ -84,7 +84,7 @@ func IsServiceIPRequested(service *Service) bool {
var standardFinalizers = sets.NewString(
string(FinalizerKubernetes),
metav1.FinalizerOrphan,
metav1.FinalizerOrphanDependents,
)
func IsStandardFinalizerName(str string) bool {