123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /*
- Copyright 2016 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package controller
- import (
- "fmt"
- "sync"
- apps "k8s.io/api/apps/v1"
- "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/runtime/schema"
- utilerrors "k8s.io/apimachinery/pkg/util/errors"
- "k8s.io/klog"
- )
- type BaseControllerRefManager struct {
- Controller metav1.Object
- Selector labels.Selector
- canAdoptErr error
- canAdoptOnce sync.Once
- CanAdoptFunc func() error
- }
- func (m *BaseControllerRefManager) CanAdopt() error {
- m.canAdoptOnce.Do(func() {
- if m.CanAdoptFunc != nil {
- m.canAdoptErr = m.CanAdoptFunc()
- }
- })
- return m.canAdoptErr
- }
- // ClaimObject tries to take ownership of an object for this controller.
- //
- // It will reconcile the following:
- // * Adopt orphans if the match function returns true.
- // * Release owned objects if the match function returns false.
- //
- // A non-nil error is returned if some form of reconciliation was attempted and
- // failed. Usually, controllers should try again later in case reconciliation
- // is still needed.
- //
- // If the error is nil, either the reconciliation succeeded, or no
- // reconciliation was necessary. The returned boolean indicates whether you now
- // own the object.
- //
- // No reconciliation will be attempted if the controller is being deleted.
- func (m *BaseControllerRefManager) ClaimObject(obj metav1.Object, match func(metav1.Object) bool, adopt, release func(metav1.Object) error) (bool, error) {
- controllerRef := metav1.GetControllerOf(obj)
- if controllerRef != nil {
- if controllerRef.UID != m.Controller.GetUID() {
- // Owned by someone else. Ignore.
- return false, nil
- }
- if match(obj) {
- // We already own it and the selector matches.
- // Return true (successfully claimed) before checking deletion timestamp.
- // We're still allowed to claim things we already own while being deleted
- // because doing so requires taking no actions.
- return true, nil
- }
- // Owned by us but selector doesn't match.
- // Try to release, unless we're being deleted.
- if m.Controller.GetDeletionTimestamp() != nil {
- return false, nil
- }
- if err := release(obj); err != nil {
- // If the pod no longer exists, ignore the error.
- if errors.IsNotFound(err) {
- return false, nil
- }
- // Either someone else released it, or there was a transient error.
- // The controller should requeue and try again if it's still stale.
- return false, err
- }
- // Successfully released.
- return false, nil
- }
- // It's an orphan.
- if m.Controller.GetDeletionTimestamp() != nil || !match(obj) {
- // Ignore if we're being deleted or selector doesn't match.
- return false, nil
- }
- if obj.GetDeletionTimestamp() != nil {
- // Ignore if the object is being deleted
- return false, nil
- }
- // Selector matches. Try to adopt.
- if err := adopt(obj); err != nil {
- // If the pod no longer exists, ignore the error.
- if errors.IsNotFound(err) {
- return false, nil
- }
- // Either someone else claimed it first, or there was a transient error.
- // The controller should requeue and try again if it's still orphaned.
- return false, err
- }
- // Successfully adopted.
- return true, nil
- }
- type PodControllerRefManager struct {
- BaseControllerRefManager
- controllerKind schema.GroupVersionKind
- podControl PodControlInterface
- }
- // NewPodControllerRefManager returns a PodControllerRefManager that exposes
- // methods to manage the controllerRef of pods.
- //
- // The CanAdopt() function can be used to perform a potentially expensive check
- // (such as a live GET from the API server) prior to the first adoption.
- // It will only be called (at most once) if an adoption is actually attempted.
- // If CanAdopt() returns a non-nil error, all adoptions will fail.
- //
- // NOTE: Once CanAdopt() is called, it will not be called again by the same
- // PodControllerRefManager instance. Create a new instance if it makes
- // sense to check CanAdopt() again (e.g. in a different sync pass).
- func NewPodControllerRefManager(
- podControl PodControlInterface,
- controller metav1.Object,
- selector labels.Selector,
- controllerKind schema.GroupVersionKind,
- canAdopt func() error,
- ) *PodControllerRefManager {
- return &PodControllerRefManager{
- BaseControllerRefManager: BaseControllerRefManager{
- Controller: controller,
- Selector: selector,
- CanAdoptFunc: canAdopt,
- },
- controllerKind: controllerKind,
- podControl: podControl,
- }
- }
- // ClaimPods tries to take ownership of a list of Pods.
- //
- // It will reconcile the following:
- // * Adopt orphans if the selector matches.
- // * Release owned objects if the selector no longer matches.
- //
- // Optional: If one or more filters are specified, a Pod will only be claimed if
- // all filters return true.
- //
- // A non-nil error is returned if some form of reconciliation was attempted and
- // failed. Usually, controllers should try again later in case reconciliation
- // is still needed.
- //
- // If the error is nil, either the reconciliation succeeded, or no
- // reconciliation was necessary. The list of Pods that you now own is returned.
- func (m *PodControllerRefManager) ClaimPods(pods []*v1.Pod, filters ...func(*v1.Pod) bool) ([]*v1.Pod, error) {
- var claimed []*v1.Pod
- var errlist []error
- match := func(obj metav1.Object) bool {
- pod := obj.(*v1.Pod)
- // Check selector first so filters only run on potentially matching Pods.
- if !m.Selector.Matches(labels.Set(pod.Labels)) {
- return false
- }
- for _, filter := range filters {
- if !filter(pod) {
- return false
- }
- }
- return true
- }
- adopt := func(obj metav1.Object) error {
- return m.AdoptPod(obj.(*v1.Pod))
- }
- release := func(obj metav1.Object) error {
- return m.ReleasePod(obj.(*v1.Pod))
- }
- for _, pod := range pods {
- ok, err := m.ClaimObject(pod, match, adopt, release)
- if err != nil {
- errlist = append(errlist, err)
- continue
- }
- if ok {
- claimed = append(claimed, pod)
- }
- }
- return claimed, utilerrors.NewAggregate(errlist)
- }
- // AdoptPod sends a patch to take control of the pod. It returns the error if
- // the patching fails.
- func (m *PodControllerRefManager) AdoptPod(pod *v1.Pod) error {
- if err := m.CanAdopt(); err != nil {
- return fmt.Errorf("can't adopt Pod %v/%v (%v): %v", pod.Namespace, pod.Name, pod.UID, err)
- }
- // Note that ValidateOwnerReferences() will reject this patch if another
- // OwnerReference exists with controller=true.
- addControllerPatch := fmt.Sprintf(
- `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`,
- m.controllerKind.GroupVersion(), m.controllerKind.Kind,
- m.Controller.GetName(), m.Controller.GetUID(), pod.UID)
- return m.podControl.PatchPod(pod.Namespace, pod.Name, []byte(addControllerPatch))
- }
- // ReleasePod sends a patch to free the pod from the control of the controller.
- // It returns the error if the patching fails. 404 and 422 errors are ignored.
- func (m *PodControllerRefManager) ReleasePod(pod *v1.Pod) error {
- klog.V(2).Infof("patching pod %s_%s to remove its controllerRef to %s/%s:%s",
- pod.Namespace, pod.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName())
- deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), pod.UID)
- err := m.podControl.PatchPod(pod.Namespace, pod.Name, []byte(deleteOwnerRefPatch))
- if err != nil {
- if errors.IsNotFound(err) {
- // If the pod no longer exists, ignore it.
- return nil
- }
- if errors.IsInvalid(err) {
- // Invalid error will be returned in two cases: 1. the pod
- // has no owner reference, 2. the uid of the pod doesn't
- // match, which means the pod is deleted and then recreated.
- // In both cases, the error can be ignored.
- // TODO: If the pod has owner references, but none of them
- // has the owner.UID, server will silently ignore the patch.
- // Investigate why.
- return nil
- }
- }
- return err
- }
- // ReplicaSetControllerRefManager is used to manage controllerRef of ReplicaSets.
- // Three methods are defined on this object 1: Classify 2: AdoptReplicaSet and
- // 3: ReleaseReplicaSet which are used to classify the ReplicaSets into appropriate
- // categories and accordingly adopt or release them. See comments on these functions
- // for more details.
- type ReplicaSetControllerRefManager struct {
- BaseControllerRefManager
- controllerKind schema.GroupVersionKind
- rsControl RSControlInterface
- }
- // NewReplicaSetControllerRefManager returns a ReplicaSetControllerRefManager that exposes
- // methods to manage the controllerRef of ReplicaSets.
- //
- // The CanAdopt() function can be used to perform a potentially expensive check
- // (such as a live GET from the API server) prior to the first adoption.
- // It will only be called (at most once) if an adoption is actually attempted.
- // If CanAdopt() returns a non-nil error, all adoptions will fail.
- //
- // NOTE: Once CanAdopt() is called, it will not be called again by the same
- // ReplicaSetControllerRefManager instance. Create a new instance if it
- // makes sense to check CanAdopt() again (e.g. in a different sync pass).
- func NewReplicaSetControllerRefManager(
- rsControl RSControlInterface,
- controller metav1.Object,
- selector labels.Selector,
- controllerKind schema.GroupVersionKind,
- canAdopt func() error,
- ) *ReplicaSetControllerRefManager {
- return &ReplicaSetControllerRefManager{
- BaseControllerRefManager: BaseControllerRefManager{
- Controller: controller,
- Selector: selector,
- CanAdoptFunc: canAdopt,
- },
- controllerKind: controllerKind,
- rsControl: rsControl,
- }
- }
- // ClaimReplicaSets tries to take ownership of a list of ReplicaSets.
- //
- // It will reconcile the following:
- // * Adopt orphans if the selector matches.
- // * Release owned objects if the selector no longer matches.
- //
- // A non-nil error is returned if some form of reconciliation was attempted and
- // failed. Usually, controllers should try again later in case reconciliation
- // is still needed.
- //
- // If the error is nil, either the reconciliation succeeded, or no
- // reconciliation was necessary. The list of ReplicaSets that you now own is
- // returned.
- func (m *ReplicaSetControllerRefManager) ClaimReplicaSets(sets []*apps.ReplicaSet) ([]*apps.ReplicaSet, error) {
- var claimed []*apps.ReplicaSet
- var errlist []error
- match := func(obj metav1.Object) bool {
- return m.Selector.Matches(labels.Set(obj.GetLabels()))
- }
- adopt := func(obj metav1.Object) error {
- return m.AdoptReplicaSet(obj.(*apps.ReplicaSet))
- }
- release := func(obj metav1.Object) error {
- return m.ReleaseReplicaSet(obj.(*apps.ReplicaSet))
- }
- for _, rs := range sets {
- ok, err := m.ClaimObject(rs, match, adopt, release)
- if err != nil {
- errlist = append(errlist, err)
- continue
- }
- if ok {
- claimed = append(claimed, rs)
- }
- }
- return claimed, utilerrors.NewAggregate(errlist)
- }
- // AdoptReplicaSet sends a patch to take control of the ReplicaSet. It returns
- // the error if the patching fails.
- func (m *ReplicaSetControllerRefManager) AdoptReplicaSet(rs *apps.ReplicaSet) error {
- if err := m.CanAdopt(); err != nil {
- return fmt.Errorf("can't adopt ReplicaSet %v/%v (%v): %v", rs.Namespace, rs.Name, rs.UID, err)
- }
- // Note that ValidateOwnerReferences() will reject this patch if another
- // OwnerReference exists with controller=true.
- addControllerPatch := fmt.Sprintf(
- `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`,
- m.controllerKind.GroupVersion(), m.controllerKind.Kind,
- m.Controller.GetName(), m.Controller.GetUID(), rs.UID)
- return m.rsControl.PatchReplicaSet(rs.Namespace, rs.Name, []byte(addControllerPatch))
- }
- // ReleaseReplicaSet sends a patch to free the ReplicaSet from the control of the Deployment controller.
- // It returns the error if the patching fails. 404 and 422 errors are ignored.
- func (m *ReplicaSetControllerRefManager) ReleaseReplicaSet(replicaSet *apps.ReplicaSet) error {
- klog.V(2).Infof("patching ReplicaSet %s_%s to remove its controllerRef to %s/%s:%s",
- replicaSet.Namespace, replicaSet.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName())
- deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), replicaSet.UID)
- err := m.rsControl.PatchReplicaSet(replicaSet.Namespace, replicaSet.Name, []byte(deleteOwnerRefPatch))
- if err != nil {
- if errors.IsNotFound(err) {
- // If the ReplicaSet no longer exists, ignore it.
- return nil
- }
- if errors.IsInvalid(err) {
- // Invalid error will be returned in two cases: 1. the ReplicaSet
- // has no owner reference, 2. the uid of the ReplicaSet doesn't
- // match, which means the ReplicaSet is deleted and then recreated.
- // In both cases, the error can be ignored.
- return nil
- }
- }
- return err
- }
- // RecheckDeletionTimestamp returns a CanAdopt() function to recheck deletion.
- //
- // The CanAdopt() function calls getObject() to fetch the latest value,
- // and denies adoption attempts if that object has a non-nil DeletionTimestamp.
- func RecheckDeletionTimestamp(getObject func() (metav1.Object, error)) func() error {
- return func() error {
- obj, err := getObject()
- if err != nil {
- return fmt.Errorf("can't recheck DeletionTimestamp: %v", err)
- }
- if obj.GetDeletionTimestamp() != nil {
- return fmt.Errorf("%v/%v has just been deleted at %v", obj.GetNamespace(), obj.GetName(), obj.GetDeletionTimestamp())
- }
- return nil
- }
- }
- // ControllerRevisionControllerRefManager is used to manage controllerRef of ControllerRevisions.
- // Three methods are defined on this object 1: Classify 2: AdoptControllerRevision and
- // 3: ReleaseControllerRevision which are used to classify the ControllerRevisions into appropriate
- // categories and accordingly adopt or release them. See comments on these functions
- // for more details.
- type ControllerRevisionControllerRefManager struct {
- BaseControllerRefManager
- controllerKind schema.GroupVersionKind
- crControl ControllerRevisionControlInterface
- }
- // NewControllerRevisionControllerRefManager returns a ControllerRevisionControllerRefManager that exposes
- // methods to manage the controllerRef of ControllerRevisions.
- //
- // The canAdopt() function can be used to perform a potentially expensive check
- // (such as a live GET from the API server) prior to the first adoption.
- // It will only be called (at most once) if an adoption is actually attempted.
- // If canAdopt() returns a non-nil error, all adoptions will fail.
- //
- // NOTE: Once canAdopt() is called, it will not be called again by the same
- // ControllerRevisionControllerRefManager instance. Create a new instance if it
- // makes sense to check canAdopt() again (e.g. in a different sync pass).
- func NewControllerRevisionControllerRefManager(
- crControl ControllerRevisionControlInterface,
- controller metav1.Object,
- selector labels.Selector,
- controllerKind schema.GroupVersionKind,
- canAdopt func() error,
- ) *ControllerRevisionControllerRefManager {
- return &ControllerRevisionControllerRefManager{
- BaseControllerRefManager: BaseControllerRefManager{
- Controller: controller,
- Selector: selector,
- CanAdoptFunc: canAdopt,
- },
- controllerKind: controllerKind,
- crControl: crControl,
- }
- }
- // ClaimControllerRevisions tries to take ownership of a list of ControllerRevisions.
- //
- // It will reconcile the following:
- // * Adopt orphans if the selector matches.
- // * Release owned objects if the selector no longer matches.
- //
- // A non-nil error is returned if some form of reconciliation was attempted and
- // failed. Usually, controllers should try again later in case reconciliation
- // is still needed.
- //
- // If the error is nil, either the reconciliation succeeded, or no
- // reconciliation was necessary. The list of ControllerRevisions that you now own is
- // returned.
- func (m *ControllerRevisionControllerRefManager) ClaimControllerRevisions(histories []*apps.ControllerRevision) ([]*apps.ControllerRevision, error) {
- var claimed []*apps.ControllerRevision
- var errlist []error
- match := func(obj metav1.Object) bool {
- return m.Selector.Matches(labels.Set(obj.GetLabels()))
- }
- adopt := func(obj metav1.Object) error {
- return m.AdoptControllerRevision(obj.(*apps.ControllerRevision))
- }
- release := func(obj metav1.Object) error {
- return m.ReleaseControllerRevision(obj.(*apps.ControllerRevision))
- }
- for _, h := range histories {
- ok, err := m.ClaimObject(h, match, adopt, release)
- if err != nil {
- errlist = append(errlist, err)
- continue
- }
- if ok {
- claimed = append(claimed, h)
- }
- }
- return claimed, utilerrors.NewAggregate(errlist)
- }
- // AdoptControllerRevision sends a patch to take control of the ControllerRevision. It returns the error if
- // the patching fails.
- func (m *ControllerRevisionControllerRefManager) AdoptControllerRevision(history *apps.ControllerRevision) error {
- if err := m.CanAdopt(); err != nil {
- return fmt.Errorf("can't adopt ControllerRevision %v/%v (%v): %v", history.Namespace, history.Name, history.UID, err)
- }
- // Note that ValidateOwnerReferences() will reject this patch if another
- // OwnerReference exists with controller=true.
- addControllerPatch := fmt.Sprintf(
- `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`,
- m.controllerKind.GroupVersion(), m.controllerKind.Kind,
- m.Controller.GetName(), m.Controller.GetUID(), history.UID)
- return m.crControl.PatchControllerRevision(history.Namespace, history.Name, []byte(addControllerPatch))
- }
- // ReleaseControllerRevision sends a patch to free the ControllerRevision from the control of its controller.
- // It returns the error if the patching fails. 404 and 422 errors are ignored.
- func (m *ControllerRevisionControllerRefManager) ReleaseControllerRevision(history *apps.ControllerRevision) error {
- klog.V(2).Infof("patching ControllerRevision %s_%s to remove its controllerRef to %s/%s:%s",
- history.Namespace, history.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName())
- deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), history.UID)
- err := m.crControl.PatchControllerRevision(history.Namespace, history.Name, []byte(deleteOwnerRefPatch))
- if err != nil {
- if errors.IsNotFound(err) {
- // If the ControllerRevision no longer exists, ignore it.
- return nil
- }
- if errors.IsInvalid(err) {
- // Invalid error will be returned in two cases: 1. the ControllerRevision
- // has no owner reference, 2. the uid of the ControllerRevision doesn't
- // match, which means the ControllerRevision is deleted and then recreated.
- // In both cases, the error can be ignored.
- return nil
- }
- }
- return err
- }
|