mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 06:54:01 +00:00
API changes for finalizers and system-wide garbage collector
This commit is contained in:
parent
93e3df8e55
commit
a46b7775fc
@ -138,6 +138,16 @@ type ObjectMeta struct {
|
||||
// objects. Annotation keys have the same formatting restrictions as Label keys. See the
|
||||
// comments on Labels for details.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// List of objects depended by this object. If ALL objects in the list have
|
||||
// been deleted, this object will be garbage collected.
|
||||
OwnerReferences []OwnerReference `json:"ownerReferences,omitempty"`
|
||||
|
||||
// Must be empty before the object is deleted from the registry. Each entry
|
||||
// is an identifier for the responsible component that will remove the entry
|
||||
// from the list. If the deletionTimestamp of the object is non-nil, entries
|
||||
// in this list can only be removed.
|
||||
Finalizers []string `json:"finalizers,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
@ -1925,6 +1935,10 @@ type DeleteOptions struct {
|
||||
// Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be
|
||||
// returned.
|
||||
Preconditions *Preconditions `json:"preconditions,omitempty"`
|
||||
|
||||
// Should the dependent objects be orphaned. If true/false, the "orphan"
|
||||
// finalizer will be added to/removed from the object's finalizers list.
|
||||
OrphanDependents *bool `json:"orphanDependents,omitempty"`
|
||||
}
|
||||
|
||||
// ExportOptions is the query options to the standard REST get call.
|
||||
@ -2057,6 +2071,23 @@ type ServiceProxyOptions struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// OwnerReference contains enough information to let you identify an owning
|
||||
// object. Currently, an owning object must be in the same namespace, so there
|
||||
// is no namespace field.
|
||||
type OwnerReference struct {
|
||||
// API version of the referent.
|
||||
APIVersion string `json:"apiVersion"`
|
||||
// Kind of the referent.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
Kind string `json:"kind"`
|
||||
// Name of the referent.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names
|
||||
Name string `json:"name"`
|
||||
// UID of the referent.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids
|
||||
UID types.UID `json:"uid"`
|
||||
}
|
||||
|
||||
// ObjectReference contains enough information to let you inspect or modify the referred object.
|
||||
type ObjectReference struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
|
@ -175,6 +175,16 @@ type ObjectMeta struct {
|
||||
// queryable and should be preserved when modifying objects.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/annotations.md
|
||||
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
|
||||
|
||||
// List of objects depended by this object. If ALL objects in the list have
|
||||
// been deleted, this object will be garbage collected.
|
||||
OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid"`
|
||||
|
||||
// Must be empty before the object is deleted from the registry. Each entry
|
||||
// is an identifier for the responsible component that will remove the entry
|
||||
// from the list. If the deletionTimestamp of the object is non-nil, entries
|
||||
// in this list can only be removed.
|
||||
Finalizers []string `json:"finalizers,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
@ -2331,6 +2341,10 @@ type DeleteOptions struct {
|
||||
// Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be
|
||||
// returned.
|
||||
Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"`
|
||||
|
||||
// Should the dependent objects be orphaned. If true/false, the "orphan"
|
||||
// finalizer will be added to/removed from the object's finalizers list.
|
||||
OrphanDependents *bool `json:"orphanDependents,omitempty"`
|
||||
}
|
||||
|
||||
// ExportOptions is the query options to the standard REST get call.
|
||||
@ -2484,6 +2498,23 @@ type ServiceProxyOptions struct {
|
||||
Path string `json:"path,omitempty" protobuf:"bytes,1,opt,name=path"`
|
||||
}
|
||||
|
||||
// OwnerReference contains enough information to let you identify an owning
|
||||
// object. Currently, an owning object must be in the same namespace, so there
|
||||
// is no namespace field.
|
||||
type OwnerReference struct {
|
||||
// API version of the referent.
|
||||
APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"`
|
||||
// Kind of the referent.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
|
||||
// Name of the referent.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names
|
||||
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
|
||||
// UID of the referent.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids
|
||||
UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`
|
||||
}
|
||||
|
||||
// ObjectReference contains enough information to let you inspect or modify the referred object.
|
||||
type ObjectReference struct {
|
||||
// Kind of the referent.
|
||||
|
@ -33,6 +33,8 @@ import (
|
||||
utilpod "k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
apiservice "k8s.io/kubernetes/pkg/api/service"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/capabilities"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
@ -67,6 +69,11 @@ var PortNameErrorMsg string = fmt.Sprintf(`must be an IANA_SVC_NAME (at most 15
|
||||
|
||||
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
|
||||
|
||||
// BannedOwners is a black list of object that are not allowed to be owners.
|
||||
var BannedOwners = map[unversioned.GroupVersionKind]struct{}{
|
||||
v1.SchemeGroupVersion.WithKind("Event"): {},
|
||||
}
|
||||
|
||||
func ValidateLabelName(labelName string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if !validation.IsQualifiedName(labelName) {
|
||||
@ -145,6 +152,36 @@ func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateOwnerReference(ownerReference api.OwnerReference, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
gvk := unversioned.FromAPIVersionAndKind(ownerReference.APIVersion, ownerReference.Kind)
|
||||
// gvk.Group is empty for the legacy group.
|
||||
if len(gvk.Version) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVersion"), ownerReference.APIVersion, "version must not be empty"))
|
||||
}
|
||||
if len(gvk.Kind) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ownerReference.Kind, "kind must not be empty"))
|
||||
}
|
||||
if len(ownerReference.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "name must not be empty"))
|
||||
}
|
||||
if len(ownerReference.UID) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
|
||||
}
|
||||
if _, ok := BannedOwners[*gvk]; ok {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateOwnerReferences(ownerReferences []api.OwnerReference, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for _, ref := range ownerReferences {
|
||||
allErrs = append(allErrs, validateOwnerReference(ref, fldPath)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateNameFunc validates that the provided name is valid for a given resource type.
|
||||
// Not all resources have the same validation rules for names. Prefix is true if the
|
||||
// name will have a value appended to it.
|
||||
@ -326,6 +363,7 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
|
||||
allErrs = append(allErrs, ValidateNonnegativeField(meta.Generation, fldPath.Child("generation"))...)
|
||||
allErrs = append(allErrs, ValidateLabels(meta.Labels, fldPath.Child("labels"))...)
|
||||
allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidateOwnerReferences(meta.OwnerReferences, fldPath.Child("ownerReferences"))...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
@ -376,9 +414,11 @@ func ValidateObjectMetaUpdate(newMeta, oldMeta *api.ObjectMeta, fldPath *field.P
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.Namespace, oldMeta.Namespace, fldPath.Child("namespace"))...)
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.UID, oldMeta.UID, fldPath.Child("uid"))...)
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.CreationTimestamp, oldMeta.CreationTimestamp, fldPath.Child("creationTimestamp"))...)
|
||||
allErrs = append(allErrs, ValidateImmutableField(newMeta.Finalizers, oldMeta.Finalizers, fldPath.Child("finalizers"))...)
|
||||
|
||||
allErrs = append(allErrs, ValidateLabels(newMeta.Labels, fldPath.Child("labels"))...)
|
||||
allErrs = append(allErrs, ValidateAnnotations(newMeta.Annotations, fldPath.Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidateOwnerReferences(newMeta.OwnerReferences, fldPath.Child("ownerReferences"))...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
@ -98,6 +98,56 @@ func TestValidateObjectMetaNamespaces(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateObjectMetaOwnerReferences(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ownerReferences []api.OwnerReference
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
[]api.OwnerReference{
|
||||
{
|
||||
APIVersion: "thirdpartyVersion",
|
||||
Kind: "thirdpartyKind",
|
||||
Name: "name",
|
||||
UID: "1",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
// event shouldn't be set as an owner
|
||||
[]api.OwnerReference{
|
||||
{
|
||||
APIVersion: "v1",
|
||||
Kind: "Event",
|
||||
Name: "name",
|
||||
UID: "1",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
errs := ValidateObjectMeta(
|
||||
&api.ObjectMeta{Name: "test", Namespace: "test", OwnerReferences: tc.ownerReferences},
|
||||
true,
|
||||
func(s string, prefix bool) (bool, string) {
|
||||
return true, ""
|
||||
},
|
||||
field.NewPath("field"))
|
||||
if len(errs) != 0 && !tc.expectError {
|
||||
t.Errorf("unexpected error: %v", errs)
|
||||
}
|
||||
if len(errs) == 0 && tc.expectError {
|
||||
t.Errorf("expect error")
|
||||
}
|
||||
if len(errs) != 0 && !strings.Contains(errs[0].Error(), "is disallowed from being an owner") {
|
||||
t.Errorf("unexpected error message: %v", errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
|
||||
if errs := ValidateObjectMetaUpdate(
|
||||
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
|
||||
@ -217,6 +267,21 @@ func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure updating finalizers is disallowed
|
||||
func TestValidateObjectMetaUpdateDisallowsUpdatingFinalizers(t *testing.T) {
|
||||
errs := ValidateObjectMetaUpdate(
|
||||
&api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"orphaning"}},
|
||||
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
|
||||
field.NewPath("field"),
|
||||
)
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("unexpected errors: %v", errs)
|
||||
}
|
||||
if !strings.Contains(errs[0].Error(), "field is immutable") {
|
||||
t.Errorf("unexpected error message: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLabels(t *testing.T) {
|
||||
successCases := []map[string]string{
|
||||
{"simple": "bar"},
|
||||
|
Loading…
Reference in New Issue
Block a user