mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
API
This commit is contained in:
parent
e4aa9db258
commit
5bfacf59f6
@ -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
|
||||
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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,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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user