Merge pull request #87217 from prameshj/patch-helper

Add PatchService method in service/helper.
This commit is contained in:
Kubernetes Prow Robot 2020-01-15 14:01:33 -08:00 committed by GitHub
commit 82c9e5c814
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 120 additions and 0 deletions

View File

@ -21,6 +21,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=

View File

@ -8,6 +8,9 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//vendor/k8s.io/utils/net:go_default_library",
],
)
@ -33,6 +36,7 @@ go_test(
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/utils/net:go_default_library",
],
)

View File

@ -17,10 +17,14 @@ limitations under the License.
package helpers
import (
"encoding/json"
"fmt"
"strings"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
utilnet "k8s.io/utils/net"
)
@ -121,6 +125,40 @@ func LoadBalancerStatusEqual(l, r *v1.LoadBalancerStatus) bool {
return ingressSliceEqual(l.Ingress, r.Ingress)
}
// PatchService patches the given service's Status or ObjectMeta based on the original and
// updated ones. Change to spec will be ignored.
func PatchService(c corev1.CoreV1Interface, oldSvc, newSvc *v1.Service) (*v1.Service, error) {
// Reset spec to make sure only patch for Status or ObjectMeta.
newSvc.Spec = oldSvc.Spec
patchBytes, err := getPatchBytes(oldSvc, newSvc)
if err != nil {
return nil, err
}
return c.Services(oldSvc.Namespace).Patch(oldSvc.Name, types.StrategicMergePatchType, patchBytes, "status")
}
func getPatchBytes(oldSvc, newSvc *v1.Service) ([]byte, error) {
oldData, err := json.Marshal(oldSvc)
if err != nil {
return nil, fmt.Errorf("failed to Marshal oldData for svc %s/%s: %v", oldSvc.Namespace, oldSvc.Name, err)
}
newData, err := json.Marshal(newSvc)
if err != nil {
return nil, fmt.Errorf("failed to Marshal newData for svc %s/%s: %v", newSvc.Namespace, newSvc.Name, err)
}
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Service{})
if err != nil {
return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for svc %s/%s: %v", oldSvc.Namespace, oldSvc.Name, err)
}
return patchBytes, nil
}
func ingressSliceEqual(lhs, rhs []v1.LoadBalancerIngress) bool {
if len(lhs) != len(rhs) {
return false

View File

@ -17,11 +17,13 @@ limitations under the License.
package helpers
import (
"reflect"
"strings"
"testing"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
utilnet "k8s.io/utils/net"
)
@ -269,3 +271,78 @@ func TestHasLBFinalizer(t *testing.T) {
})
}
}
func TestPatchService(t *testing.T) {
svcOrigin := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-patch",
Annotations: map[string]string{},
},
Spec: v1.ServiceSpec{
ClusterIP: "10.0.0.1",
},
}
fakeCs := fake.NewSimpleClientset(svcOrigin)
// Issue a separate update and verify patch doesn't fail after this.
svcToUpdate := svcOrigin.DeepCopy()
addAnnotations(svcToUpdate)
if _, err := fakeCs.CoreV1().Services(svcOrigin.Namespace).Update(svcToUpdate); err != nil {
t.Fatalf("Failed to update service: %v", err)
}
// Attempt to patch based the original service.
svcToPatch := svcOrigin.DeepCopy()
svcToPatch.Finalizers = []string{"foo"}
svcToPatch.Spec.ClusterIP = "10.0.0.2"
svcToPatch.Status = v1.ServiceStatus{
LoadBalancer: v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{IP: "8.8.8.8"},
},
},
}
svcPatched, err := PatchService(fakeCs.CoreV1(), svcOrigin, svcToPatch)
if err != nil {
t.Fatalf("Failed to patch service: %v", err)
}
// Service returned by patch will contain latest content (e.g from
// the separate update).
addAnnotations(svcToPatch)
if !reflect.DeepEqual(svcPatched, svcToPatch) {
t.Errorf("PatchStatus() = %+v, want %+v", svcPatched, svcToPatch)
}
// Explicitly validate if spec is unchanged from origin.
if !reflect.DeepEqual(svcPatched.Spec, svcOrigin.Spec) {
t.Errorf("Got spec = %+v, want %+v", svcPatched.Spec, svcOrigin.Spec)
}
}
func Test_getPatchBytes(t *testing.T) {
origin := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-patch-bytes",
Finalizers: []string{"foo"},
},
}
updated := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-patch-bytes",
Finalizers: []string{"foo", "bar"},
},
}
b, err := getPatchBytes(origin, updated)
if err != nil {
t.Fatal(err)
}
expected := `{"metadata":{"$setElementOrder/finalizers":["foo","bar"],"finalizers":["bar"]}}`
if string(b) != expected {
t.Errorf("getPatchBytes(%+v, %+v) = %s ; want %s", origin, updated, string(b), expected)
}
}
func addAnnotations(svc *v1.Service) {
svc.Annotations["foo"] = "bar"
}