|
| 1 | +package controller |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + |
| 6 | + "github.com/golang/glog" |
| 7 | + kerrors "k8s.io/apimachinery/pkg/api/errors" |
| 8 | + kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 9 | + klabels "k8s.io/apimachinery/pkg/labels" |
| 10 | + kschema "k8s.io/apimachinery/pkg/runtime/schema" |
| 11 | + "k8s.io/apimachinery/pkg/types" |
| 12 | + kutilerrors "k8s.io/apimachinery/pkg/util/errors" |
| 13 | + "k8s.io/client-go/tools/record" |
| 14 | + kapi "k8s.io/kubernetes/pkg/api" |
| 15 | + kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" |
| 16 | + kcontroller "k8s.io/kubernetes/pkg/controller" |
| 17 | +) |
| 18 | + |
| 19 | +// RSControlInterface is an interface that knows how to add or delete |
| 20 | +// ReplicationControllers, as well as increment or decrement them. It is used |
| 21 | +// by the DeploymentConfig controller to ease testing of actions that it takes. |
| 22 | +type RCControlInterface interface { |
| 23 | + PatchReplicationController(namespace, name string, data []byte) error |
| 24 | +} |
| 25 | + |
| 26 | +// RealRCControl is the default implementation of RCControlInterface. |
| 27 | +type RealRCControl struct { |
| 28 | + KubeClient kclientset.Interface |
| 29 | + Recorder record.EventRecorder |
| 30 | +} |
| 31 | + |
| 32 | +// To make sure RealRCControl implements RCControlInterface |
| 33 | +var _ RCControlInterface = &RealRCControl{} |
| 34 | + |
| 35 | +// PatchReplicationController executes a strategic merge patch contained in 'data' on RC specified by 'namespace' and 'name' |
| 36 | +func (r RealRCControl) PatchReplicationController(namespace, name string, data []byte) error { |
| 37 | + _, err := r.KubeClient.Core().ReplicationControllers(namespace).Patch(name, types.StrategicMergePatchType, data) |
| 38 | + return err |
| 39 | +} |
| 40 | + |
| 41 | +type RCControllerRefManager struct { |
| 42 | + kcontroller.BaseControllerRefManager |
| 43 | + controllerKind kschema.GroupVersionKind |
| 44 | + rcControl RCControlInterface |
| 45 | +} |
| 46 | + |
| 47 | +// NewRCControllerRefManager returns a RCControllerRefManager that exposes |
| 48 | +// methods to manage the controllerRef of ReplicationControllers. |
| 49 | +// |
| 50 | +// The CanAdopt() function can be used to perform a potentially expensive check |
| 51 | +// (such as a live GET from the API server) prior to the first adoption. |
| 52 | +// It will only be called (at most once) if an adoption is actually attempted. |
| 53 | +// If CanAdopt() returns a non-nil error, all adoptions will fail. |
| 54 | +// |
| 55 | +// NOTE: Once CanAdopt() is called, it will not be called again by the same |
| 56 | +// RCControllerRefManager instance. Create a new instance if it |
| 57 | +// makes sense to check CanAdopt() again (e.g. in a different sync pass). |
| 58 | +func NewRCControllerRefManager( |
| 59 | + rcControl RCControlInterface, |
| 60 | + controller kmetav1.Object, |
| 61 | + selector klabels.Selector, |
| 62 | + controllerKind kschema.GroupVersionKind, |
| 63 | + canAdopt func() error, |
| 64 | +) *RCControllerRefManager { |
| 65 | + return &RCControllerRefManager{ |
| 66 | + BaseControllerRefManager: kcontroller.BaseControllerRefManager{ |
| 67 | + Controller: controller, |
| 68 | + Selector: selector, |
| 69 | + CanAdoptFunc: canAdopt, |
| 70 | + }, |
| 71 | + controllerKind: controllerKind, |
| 72 | + rcControl: rcControl, |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +// ClaimReplicationController tries to take ownership of a ReplicationController. |
| 77 | +// |
| 78 | +// It will reconcile the following: |
| 79 | +// * Adopt the ReplicationController if it's an orphan. |
| 80 | +// * Release owned ReplicationController if the selector no longer matches. |
| 81 | +// |
| 82 | +// A non-nil error is returned if some form of reconciliation was attempted and |
| 83 | +// failed. Usually, controllers should try again later in case reconciliation |
| 84 | +// is still needed. |
| 85 | +// |
| 86 | +// If the error is nil, either the reconciliation succeeded, or no |
| 87 | +// reconciliation was necessary. The returned boolean indicates whether you now |
| 88 | +// own the object. |
| 89 | +func (m *RCControllerRefManager) ClaimReplicationController(rc *kapi.ReplicationController) (bool, error) { |
| 90 | + match := func(obj kmetav1.Object) bool { |
| 91 | + return m.Selector.Matches(klabels.Set(obj.GetLabels())) |
| 92 | + } |
| 93 | + adopt := func(obj kmetav1.Object) error { |
| 94 | + return m.AdoptReplicationController(obj.(*kapi.ReplicationController)) |
| 95 | + } |
| 96 | + release := func(obj kmetav1.Object) error { |
| 97 | + return m.ReleaseReplicationController(obj.(*kapi.ReplicationController)) |
| 98 | + } |
| 99 | + |
| 100 | + return m.ClaimObject(rc, match, adopt, release) |
| 101 | +} |
| 102 | + |
| 103 | +// ClaimReplicationControllers tries to take ownership of a list of ReplicationControllers. |
| 104 | +// |
| 105 | +// It will reconcile the following: |
| 106 | +// * Adopt orphans if the selector matches. |
| 107 | +// * Release owned objects if the selector no longer matches. |
| 108 | +// |
| 109 | +// A non-nil error is returned if some form of reconciliation was attempted and |
| 110 | +// failed. Usually, controllers should try again later in case reconciliation |
| 111 | +// is still needed. |
| 112 | +// |
| 113 | +// If the error is nil, either the reconciliation succeeded, or no |
| 114 | +// reconciliation was necessary. The list of ReplicationControllers that you now own is |
| 115 | +// returned. |
| 116 | +func (m *RCControllerRefManager) ClaimReplicationControllers(rcs []*kapi.ReplicationController) ([]*kapi.ReplicationController, error) { |
| 117 | + var claimed []*kapi.ReplicationController |
| 118 | + var errlist []error |
| 119 | + |
| 120 | + for _, rc := range rcs { |
| 121 | + ok, err := m.ClaimReplicationController(rc) |
| 122 | + if err != nil { |
| 123 | + errlist = append(errlist, err) |
| 124 | + continue |
| 125 | + } |
| 126 | + if ok { |
| 127 | + claimed = append(claimed, rc) |
| 128 | + } |
| 129 | + } |
| 130 | + return claimed, kutilerrors.NewAggregate(errlist) |
| 131 | +} |
| 132 | + |
| 133 | +// AdoptReplicationController sends a patch to take control of the ReplicationController. It returns the error if |
| 134 | +// the patching fails. |
| 135 | +func (m *RCControllerRefManager) AdoptReplicationController(rs *kapi.ReplicationController) error { |
| 136 | + if err := m.CanAdopt(); err != nil { |
| 137 | + return fmt.Errorf("can't adopt ReplicationController %s/%s (%s): %v", rs.Namespace, rs.Name, rs.UID, err) |
| 138 | + } |
| 139 | + // Note that ValidateOwnerReferences() will reject this patch if another |
| 140 | + // OwnerReference exists with controller=true. |
| 141 | + addControllerPatch := fmt.Sprintf( |
| 142 | + `{"metadata":{ |
| 143 | + "ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}], |
| 144 | + "uid":"%s", |
| 145 | + "finalizers": ["%s"] |
| 146 | + } |
| 147 | + }`, |
| 148 | + m.controllerKind.GroupVersion(), m.controllerKind.Kind, |
| 149 | + m.Controller.GetName(), m.Controller.GetUID(), rs.UID, |
| 150 | + kmetav1.FinalizerDeleteDependents) |
| 151 | + return m.rcControl.PatchReplicationController(rs.Namespace, rs.Name, []byte(addControllerPatch)) |
| 152 | +} |
| 153 | + |
| 154 | +// ReleaseReplicationController sends a patch to free the ReplicationController from the control of the Deployment controller. |
| 155 | +// It returns the error if the patching fails. 404 and 422 errors are ignored. |
| 156 | +func (m *RCControllerRefManager) ReleaseReplicationController(rc *kapi.ReplicationController) error { |
| 157 | + glog.V(4).Infof("patching ReplicationController %s/%s to remove its controllerRef to %s/%s:%s", |
| 158 | + rc.Namespace, rc.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName()) |
| 159 | + deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), rc.UID) |
| 160 | + err := m.rcControl.PatchReplicationController(rc.Namespace, rc.Name, []byte(deleteOwnerRefPatch)) |
| 161 | + if err != nil { |
| 162 | + if kerrors.IsNotFound(err) { |
| 163 | + // If the ReplicationController no longer exists, ignore it. |
| 164 | + return nil |
| 165 | + } |
| 166 | + if kerrors.IsInvalid(err) { |
| 167 | + // Invalid error will be returned in two cases: 1. the ReplicationController |
| 168 | + // has no owner reference, 2. the uid of the ReplicationController doesn't |
| 169 | + // match, which means the ReplicationController is deleted and then recreated. |
| 170 | + // In both cases, the error can be ignored. |
| 171 | + return nil |
| 172 | + } |
| 173 | + } |
| 174 | + return err |
| 175 | +} |
0 commit comments