diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 7f6e5f0058..4fb268b8de 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -616,7 +616,7 @@ func (lbc *LoadBalancerController) updateIngressStatus(l7 *loadbalancers.L7, ing if err != nil { return err } - currIng.Status = v1beta1.IngressStatus{ + updatedIngStatus := v1beta1.IngressStatus{ LoadBalancer: apiv1.LoadBalancerStatus{ Ingress: []apiv1.LoadBalancerIngress{ {IP: ip}, @@ -629,7 +629,8 @@ func (lbc *LoadBalancerController) updateIngressStatus(l7 *loadbalancers.L7, ing // TODO: If this update fails it's probably resource version related, // which means it's advantageous to retry right away vs requeuing. klog.Infof("Updating loadbalancer %v/%v with IP %v", ing.Namespace, ing.Name, ip) - if _, err := ingClient.UpdateStatus(context2.TODO(), currIng, metav1.UpdateOptions{}); err != nil { + if _, err := common.PatchIngressStatus(ingClient, currIng, updatedIngStatus); err != nil { + klog.Errorf("PatchIngressStatus(%s/%s) failed: %v", currIng.Namespace, currIng.Name, err) return err } lbc.ctx.Recorder(ing.Namespace).Eventf(currIng, apiv1.EventTypeNormal, "CREATE", "ip: %v", ip) @@ -691,8 +692,10 @@ func updateAnnotations(client kubernetes.Interface, name, namespace string, anno } if !reflect.DeepEqual(currIng.Annotations, annotations) { klog.V(3).Infof("Updating annotations of %v/%v", namespace, name) - currIng.Annotations = annotations - if _, err := ingClient.Update(context2.TODO(), currIng, metav1.UpdateOptions{}); err != nil { + updatedObjectMeta := currIng.ObjectMeta.DeepCopy() + updatedObjectMeta.Annotations = annotations + if _, err := common.PatchIngressObjectMetadata(ingClient, currIng, *updatedObjectMeta); err != nil { + klog.Errorf("PatchIngressObjectMetadata(%s/%s) failed: %v", currIng.Namespace, currIng.Name, err) return err } } diff --git a/pkg/utils/common/common.go b/pkg/utils/common/common.go index 0fb6663e3e..be8c281be7 100644 --- a/pkg/utils/common/common.go +++ b/pkg/utils/common/common.go @@ -17,11 +17,17 @@ limitations under the License. package common import ( + "context" "crypto/sha256" + "encoding/json" + "fmt" "strconv" "k8s.io/api/networking/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + client "k8s.io/client-go/kubernetes/typed/networking/v1beta1" "k8s.io/client-go/tools/cache" "k8s.io/klog" ) @@ -89,3 +95,44 @@ func ToIngressKeys(ings []*v1beta1.Ingress) []string { } return ingKeys } + +// PatchIngressObjectMetadata patches the given ingress's metadata based on new +// ingress metadata. +func PatchIngressObjectMetadata(ic client.IngressInterface, ing *v1beta1.Ingress, newObjectMetadata metav1.ObjectMeta) (*v1beta1.Ingress, error) { + newIng := ing.DeepCopy() + newIng.ObjectMeta = newObjectMetadata + return patchIngress(ic, ing, newIng) +} + +// PatchIngressStatus patches the given ingress's Status based on new ingress +// status. +func PatchIngressStatus(ic client.IngressInterface, ing *v1beta1.Ingress, newStatus v1beta1.IngressStatus) (*v1beta1.Ingress, error) { + newIng := ing.DeepCopy() + newIng.Status = newStatus + return patchIngress(ic, ing, newIng) +} + +// patchIngress patches the given ingress's Status or ObjectMetadata based on +// the old and new ingresses. +// Note that both Status and ObjectMetadata (annotations and finalizers) +// can be patched via `status` subresource API endpoint. +func patchIngress(ic client.IngressInterface, oldIngress, newIngress *v1beta1.Ingress) (*v1beta1.Ingress, error) { + ingKey := fmt.Sprintf("%s/%s", oldIngress.Namespace, oldIngress.Name) + oldData, err := json.Marshal(oldIngress) + if err != nil { + return nil, fmt.Errorf("failed to Marshal oldData for ingress %s: %v", ingKey, err) + } + + newData, err := json.Marshal(newIngress) + if err != nil { + return nil, fmt.Errorf("failed to Marshal newData for ingress %s: %v", ingKey, err) + } + + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1beta1.Ingress{}) + if err != nil { + return nil, fmt.Errorf("failed to create TwoWayMergePatch for ingress %s: %v", ingKey, err) + } + + klog.V(4).Infof("Patch bytes for ingress %s: %s", ingKey, patchBytes) + return ic.Patch(context.TODO(), oldIngress.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}, "status") +} diff --git a/pkg/utils/common/finalizer.go b/pkg/utils/common/finalizer.go index d3bbb86aba..48713fc270 100644 --- a/pkg/utils/common/finalizer.go +++ b/pkg/utils/common/finalizer.go @@ -68,7 +68,7 @@ func EnsureFinalizer(ing *v1beta1.Ingress, ingClient client.IngressInterface, fi updated := ing.DeepCopy() if needToAddFinalizer(ing.ObjectMeta, finalizerKey) { updated.ObjectMeta.Finalizers = append(updated.ObjectMeta.Finalizers, finalizerKey) - if _, err := patchIngressFinalizer(ingClient, ing, updated); err != nil { + if _, err := PatchIngressObjectMetadata(ingClient, ing, updated.ObjectMeta); err != nil { return nil, fmt.Errorf("error patching Ingress %s/%s: %v", ing.Namespace, ing.Name, err) } klog.V(2).Infof("Added finalizer %q for Ingress %s/%s", finalizerKey, ing.Namespace, ing.Name) @@ -84,9 +84,9 @@ func needToAddFinalizer(m meta_v1.ObjectMeta, key string) bool { // EnsureDeleteFinalizer ensures that the specified finalizer is deleted from given Ingress. func EnsureDeleteFinalizer(ing *v1beta1.Ingress, ingClient client.IngressInterface, finalizerKey string) error { if HasGivenFinalizer(ing.ObjectMeta, finalizerKey) { - updated := ing.DeepCopy() - updated.ObjectMeta.Finalizers = slice.RemoveString(updated.ObjectMeta.Finalizers, finalizerKey, nil) - if _, err := patchIngressFinalizer(ingClient, ing, updated); err != nil { + updatedObjectMeta := ing.ObjectMeta.DeepCopy() + updatedObjectMeta.Finalizers = slice.RemoveString(updatedObjectMeta.Finalizers, finalizerKey, nil) + if _, err := PatchIngressObjectMetadata(ingClient, ing, *updatedObjectMeta); err != nil { return fmt.Errorf("error patching Ingress %s/%s: %v", ing.Namespace, ing.Name, err) } klog.V(2).Infof("Removed finalizer %q for Ingress %s/%s", finalizerKey, ing.Namespace, ing.Name) @@ -94,27 +94,6 @@ func EnsureDeleteFinalizer(ing *v1beta1.Ingress, ingClient client.IngressInterfa return nil } -func patchIngressFinalizer(ic client.IngressInterface, oldIngress, newIngress *v1beta1.Ingress) (*v1beta1.Ingress, error) { - ingKey := fmt.Sprintf("%s/%s", oldIngress.Namespace, oldIngress.Name) - oldData, err := json.Marshal(oldIngress) - if err != nil { - return nil, fmt.Errorf("failed to Marshal oldData for ingress %s: %v", ingKey, err) - } - - newData, err := json.Marshal(newIngress) - if err != nil { - return nil, fmt.Errorf("failed to Marshal newData for ingress %s: %v", ingKey, err) - } - - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1beta1.Ingress{}) - if err != nil { - return nil, fmt.Errorf("failed to create TwoWayMergePatch for ingress %s: %v", ingKey, err) - } - - klog.V(3).Infof("Patch bytes for ingress %s: %s", ingKey, patchBytes) - return ic.Patch(context.TODO(), oldIngress.Name, types.StrategicMergePatchType, patchBytes, meta_v1.PatchOptions{}, "status") -} - // EnsureServiceFinalizer patches the service to add finalizer. func EnsureServiceFinalizer(service *corev1.Service, key string, kubeClient kubernetes.Interface) error { if HasGivenFinalizer(service.ObjectMeta, key) {