From 217f720c72a88f145363c3bb05ebd6221cfa82d3 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Sat, 29 Jan 2022 10:29:35 -0500 Subject: [PATCH] Fix up some kubernetes service reconciling code organization. When the endpoint reconcilers got split out of pkg/controlplane, GetMasterServiceUpdateIfNeeded() got moved to pkg/controlplane/reconcilers, even though it needs to be kept in sync with CreateOrUpdateMasterServiceIfNeeded() which stayed in pkg/controlplane. (And everything else in pkg/controlplane/reconcilers is about the Endpoints not the Service anyway.) So move it back. On the flip side, the implementation of masterCountEndpointReconciler got moved to pkg/controlplane/reconcilers, but its unit tests didn't. So belatedly fix that. --- pkg/controlplane/controller.go | 33 +- pkg/controlplane/controller_test.go | 562 ----------------- pkg/controlplane/reconcilers/instancecount.go | 43 -- .../reconcilers/instancecount_test.go | 590 ++++++++++++++++++ 4 files changed, 622 insertions(+), 606 deletions(-) create mode 100644 pkg/controlplane/reconcilers/instancecount_test.go diff --git a/pkg/controlplane/controller.go b/pkg/controlplane/controller.go index b0aa57c78d7..3f67cf95fd0 100644 --- a/pkg/controlplane/controller.go +++ b/pkg/controlplane/controller.go @@ -327,7 +327,7 @@ func (c *Controller) CreateOrUpdateMasterServiceIfNeeded(serviceName string, ser if s, err := c.ServiceClient.Services(metav1.NamespaceDefault).Get(context.TODO(), serviceName, metav1.GetOptions{}); err == nil { // The service already exists. if reconcile { - if svc, updated := reconcilers.GetMasterServiceUpdateIfNeeded(s, servicePorts, serviceType); updated { + if svc, updated := getMasterServiceUpdateIfNeeded(s, servicePorts, serviceType); updated { klog.Warningf("Resetting master service %q to %#v", serviceName, svc) _, err := c.ServiceClient.Services(metav1.NamespaceDefault).Update(context.TODO(), svc, metav1.UpdateOptions{}) return err @@ -359,3 +359,34 @@ func (c *Controller) CreateOrUpdateMasterServiceIfNeeded(serviceName string, ser } return err } + +// getMasterServiceUpdateIfNeeded sets service attributes for the given apiserver service. +func getMasterServiceUpdateIfNeeded(svc *corev1.Service, servicePorts []corev1.ServicePort, serviceType corev1.ServiceType) (s *corev1.Service, updated bool) { + // Determine if the service is in the format we expect + // (servicePorts are present and service type matches) + formatCorrect := checkServiceFormat(svc, servicePorts, serviceType) + if formatCorrect { + return svc, false + } + svc.Spec.Ports = servicePorts + svc.Spec.Type = serviceType + return svc, true +} + +// Determine if the service is in the correct format +// getMasterServiceUpdateIfNeeded expects (servicePorts are correct +// and service type matches). +func checkServiceFormat(s *corev1.Service, ports []corev1.ServicePort, serviceType corev1.ServiceType) (formatCorrect bool) { + if s.Spec.Type != serviceType { + return false + } + if len(ports) != len(s.Spec.Ports) { + return false + } + for i, port := range ports { + if port != s.Spec.Ports[i] { + return false + } + } + return true +} diff --git a/pkg/controlplane/controller_test.go b/pkg/controlplane/controller_test.go index f20cdee24b0..2615de8baa1 100644 --- a/pkg/controlplane/controller_test.go +++ b/pkg/controlplane/controller_test.go @@ -21,7 +21,6 @@ import ( "testing" corev1 "k8s.io/api/core/v1" - discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" genericapiserver "k8s.io/apiserver/pkg/server" @@ -35,567 +34,6 @@ import ( netutils "k8s.io/utils/net" ) -func TestReconcileEndpoints(t *testing.T) { - ns := metav1.NamespaceDefault - om := func(name string, skipMirrorLabel bool) metav1.ObjectMeta { - o := metav1.ObjectMeta{Namespace: ns, Name: name} - if skipMirrorLabel { - o.Labels = map[string]string{ - discoveryv1.LabelSkipMirror: "true", - } - } - return o - } - reconcileTests := []struct { - testName string - serviceName string - ip string - endpointPorts []corev1.EndpointPort - additionalMasters int - endpoints *corev1.EndpointsList - expectUpdate *corev1.Endpoints // nil means none expected - expectCreate *corev1.Endpoints // nil means none expected - }{ - { - testName: "no existing endpoints", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: nil, - expectCreate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints satisfy", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - }, - { - testName: "existing endpoints satisfy but too many", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}, {IP: "4.3.2.1"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints satisfy but too many + extra masters", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - additionalMasters: 3, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "1.2.3.4"}, - {IP: "4.3.2.1"}, - {IP: "4.3.2.2"}, - {IP: "4.3.2.3"}, - {IP: "4.3.2.4"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "1.2.3.4"}, - {IP: "4.3.2.2"}, - {IP: "4.3.2.3"}, - {IP: "4.3.2.4"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints satisfy but too many + extra masters + delete first", - serviceName: "foo", - ip: "4.3.2.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - additionalMasters: 3, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "1.2.3.4"}, - {IP: "4.3.2.1"}, - {IP: "4.3.2.2"}, - {IP: "4.3.2.3"}, - {IP: "4.3.2.4"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "4.3.2.1"}, - {IP: "4.3.2.2"}, - {IP: "4.3.2.3"}, - {IP: "4.3.2.4"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints satisfy and endpoint addresses length less than master count", - serviceName: "foo", - ip: "4.3.2.2", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - additionalMasters: 3, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "4.3.2.1"}, - {IP: "4.3.2.2"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: nil, - }, - { - testName: "existing endpoints current IP missing and address length less than master count", - serviceName: "foo", - ip: "4.3.2.2", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - additionalMasters: 3, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "4.3.2.1"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{ - {IP: "4.3.2.1"}, - {IP: "4.3.2.2"}, - }, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints wrong name", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("bar", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectCreate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints wrong IP", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "4.3.2.1"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints wrong port", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 9090, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints wrong protocol", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "UDP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints wrong port name", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "baz", Port: 8080, Protocol: "TCP"}}, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "baz", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "existing endpoints extra service ports satisfy", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - {Name: "bar", Port: 1000, Protocol: "TCP"}, - {Name: "baz", Port: 1010, Protocol: "TCP"}, - }, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - {Name: "bar", Port: 1000, Protocol: "TCP"}, - {Name: "baz", Port: 1010, Protocol: "TCP"}, - }, - }}, - }}, - }, - }, - { - testName: "existing endpoints extra service ports missing port", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - {Name: "bar", Port: 1000, Protocol: "TCP"}, - }, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - {Name: "bar", Port: 1000, Protocol: "TCP"}, - }, - }}, - }, - }, - { - testName: "no existing sctp endpoints", - serviceName: "boo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "boo", Port: 7777, Protocol: "SCTP"}}, - endpoints: nil, - expectCreate: &corev1.Endpoints{ - ObjectMeta: om("boo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "boo", Port: 7777, Protocol: "SCTP"}}, - }}, - }, - }, - } - for _, test := range reconcileTests { - fakeClient := fake.NewSimpleClientset() - if test.endpoints != nil { - fakeClient = fake.NewSimpleClientset(test.endpoints) - } - epAdapter := reconcilers.NewEndpointsAdapter(fakeClient.CoreV1(), nil) - reconciler := reconcilers.NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter) - err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, true) - if err != nil { - t.Errorf("case %q: unexpected error: %v", test.testName, err) - } - - updates := []core.UpdateAction{} - for _, action := range fakeClient.Actions() { - if action.GetVerb() != "update" { - continue - } - updates = append(updates, action.(core.UpdateAction)) - } - if test.expectUpdate != nil { - if len(updates) != 1 { - t.Errorf("case %q: unexpected updates: %v", test.testName, updates) - } else if e, a := test.expectUpdate, updates[0].GetObject(); !reflect.DeepEqual(e, a) { - t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a) - } - } - if test.expectUpdate == nil && len(updates) > 0 { - t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates) - } - - creates := []core.CreateAction{} - for _, action := range fakeClient.Actions() { - if action.GetVerb() != "create" { - continue - } - creates = append(creates, action.(core.CreateAction)) - } - if test.expectCreate != nil { - if len(creates) != 1 { - t.Errorf("case %q: unexpected creates: %v", test.testName, creates) - } else if e, a := test.expectCreate, creates[0].GetObject(); !reflect.DeepEqual(e, a) { - t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a) - } - } - if test.expectCreate == nil && len(creates) > 0 { - t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates) - } - - } - - nonReconcileTests := []struct { - testName string - serviceName string - ip string - endpointPorts []corev1.EndpointPort - additionalMasters int - endpoints *corev1.EndpointsList - expectUpdate *corev1.Endpoints // nil means none expected - expectCreate *corev1.Endpoints // nil means none expected - }{ - { - testName: "existing endpoints extra service ports missing port no update", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - {Name: "bar", Port: 1000, Protocol: "TCP"}, - }, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: nil, - }, - { - testName: "existing endpoints extra service ports, wrong ports, wrong IP", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - {Name: "bar", Port: 1000, Protocol: "TCP"}, - }, - endpoints: &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "4.3.2.1"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }}, - }, - expectUpdate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - { - testName: "no existing endpoints", - serviceName: "foo", - ip: "1.2.3.4", - endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - endpoints: nil, - expectCreate: &corev1.Endpoints{ - ObjectMeta: om("foo", true), - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, - }}, - }, - }, - } - for _, test := range nonReconcileTests { - fakeClient := fake.NewSimpleClientset() - if test.endpoints != nil { - fakeClient = fake.NewSimpleClientset(test.endpoints) - } - epAdapter := reconcilers.NewEndpointsAdapter(fakeClient.CoreV1(), nil) - reconciler := reconcilers.NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter) - err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, false) - if err != nil { - t.Errorf("case %q: unexpected error: %v", test.testName, err) - } - - updates := []core.UpdateAction{} - for _, action := range fakeClient.Actions() { - if action.GetVerb() != "update" { - continue - } - updates = append(updates, action.(core.UpdateAction)) - } - if test.expectUpdate != nil { - if len(updates) != 1 { - t.Errorf("case %q: unexpected updates: %v", test.testName, updates) - } else if e, a := test.expectUpdate, updates[0].GetObject(); !reflect.DeepEqual(e, a) { - t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a) - } - } - if test.expectUpdate == nil && len(updates) > 0 { - t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates) - } - - creates := []core.CreateAction{} - for _, action := range fakeClient.Actions() { - if action.GetVerb() != "create" { - continue - } - creates = append(creates, action.(core.CreateAction)) - } - if test.expectCreate != nil { - if len(creates) != 1 { - t.Errorf("case %q: unexpected creates: %v", test.testName, creates) - } else if e, a := test.expectCreate, creates[0].GetObject(); !reflect.DeepEqual(e, a) { - t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a) - } - } - if test.expectCreate == nil && len(creates) > 0 { - t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates) - } - - } - -} - -func TestEmptySubsets(t *testing.T) { - ns := metav1.NamespaceDefault - om := func(name string) metav1.ObjectMeta { - return metav1.ObjectMeta{Namespace: ns, Name: name} - } - endpoints := &corev1.EndpointsList{ - Items: []corev1.Endpoints{{ - ObjectMeta: om("foo"), - Subsets: nil, - }}, - } - fakeClient := fake.NewSimpleClientset() - if endpoints != nil { - fakeClient = fake.NewSimpleClientset(endpoints) - } - epAdapter := reconcilers.NewEndpointsAdapter(fakeClient.CoreV1(), nil) - reconciler := reconcilers.NewMasterCountEndpointReconciler(1, epAdapter) - endpointPorts := []corev1.EndpointPort{ - {Name: "foo", Port: 8080, Protocol: "TCP"}, - } - err := reconciler.RemoveEndpoints("foo", netutils.ParseIPSloppy("1.2.3.4"), endpointPorts) - if err != nil { - t.Errorf("unexpected error: %v", err) - } -} - func TestCreateOrUpdateMasterService(t *testing.T) { singleStack := corev1.IPFamilyPolicySingleStack ns := metav1.NamespaceDefault diff --git a/pkg/controlplane/reconcilers/instancecount.go b/pkg/controlplane/reconcilers/instancecount.go index 35178963a72..6b36747ea6f 100644 --- a/pkg/controlplane/reconcilers/instancecount.go +++ b/pkg/controlplane/reconcilers/instancecount.go @@ -217,46 +217,3 @@ func checkEndpointSubsetFormat(e *corev1.Endpoints, ip string, ports []corev1.En } return true, ipCorrect, portsCorrect } - -// GetMasterServiceUpdateIfNeeded sets service attributes for the -// given apiserver service. -// * GetMasterServiceUpdateIfNeeded expects that the service object it -// manages will be managed only by GetMasterServiceUpdateIfNeeded; -// therefore, to understand this, you need only understand the -// requirements and the body of this function. -// * GetMasterServiceUpdateIfNeeded ensures that the correct ports are -// are set. -// -// Requirements: -// * All apiservers MUST use GetMasterServiceUpdateIfNeeded and only -// GetMasterServiceUpdateIfNeeded to manage service attributes -// * updateMasterService is called periodically from all apiservers. -func GetMasterServiceUpdateIfNeeded(svc *corev1.Service, servicePorts []corev1.ServicePort, serviceType corev1.ServiceType) (s *corev1.Service, updated bool) { - // Determine if the service is in the format we expect - // (servicePorts are present and service type matches) - formatCorrect := checkServiceFormat(svc, servicePorts, serviceType) - if formatCorrect { - return svc, false - } - svc.Spec.Ports = servicePorts - svc.Spec.Type = serviceType - return svc, true -} - -// Determine if the service is in the correct format -// GetMasterServiceUpdateIfNeeded expects (servicePorts are correct -// and service type matches). -func checkServiceFormat(s *corev1.Service, ports []corev1.ServicePort, serviceType corev1.ServiceType) (formatCorrect bool) { - if s.Spec.Type != serviceType { - return false - } - if len(ports) != len(s.Spec.Ports) { - return false - } - for i, port := range ports { - if port != s.Spec.Ports[i] { - return false - } - } - return true -} diff --git a/pkg/controlplane/reconcilers/instancecount_test.go b/pkg/controlplane/reconcilers/instancecount_test.go new file mode 100644 index 00000000000..a17a61613ad --- /dev/null +++ b/pkg/controlplane/reconcilers/instancecount_test.go @@ -0,0 +1,590 @@ +/* +Copyright 2014 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 reconcilers + +import ( + "reflect" + "testing" + + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + netutils "k8s.io/utils/net" +) + +func TestMasterCountEndpointReconciler(t *testing.T) { + ns := metav1.NamespaceDefault + om := func(name string, skipMirrorLabel bool) metav1.ObjectMeta { + o := metav1.ObjectMeta{Namespace: ns, Name: name} + if skipMirrorLabel { + o.Labels = map[string]string{ + discoveryv1.LabelSkipMirror: "true", + } + } + return o + } + reconcileTests := []struct { + testName string + serviceName string + ip string + endpointPorts []corev1.EndpointPort + additionalMasters int + endpoints *corev1.EndpointsList + expectUpdate *corev1.Endpoints // nil means none expected + expectCreate *corev1.Endpoints // nil means none expected + }{ + { + testName: "no existing endpoints", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: nil, + expectCreate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints satisfy", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + }, + { + testName: "existing endpoints satisfy but too many", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}, {IP: "4.3.2.1"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints satisfy but too many + extra masters", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + additionalMasters: 3, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "1.2.3.4"}, + {IP: "4.3.2.1"}, + {IP: "4.3.2.2"}, + {IP: "4.3.2.3"}, + {IP: "4.3.2.4"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "1.2.3.4"}, + {IP: "4.3.2.2"}, + {IP: "4.3.2.3"}, + {IP: "4.3.2.4"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints satisfy but too many + extra masters + delete first", + serviceName: "foo", + ip: "4.3.2.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + additionalMasters: 3, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "1.2.3.4"}, + {IP: "4.3.2.1"}, + {IP: "4.3.2.2"}, + {IP: "4.3.2.3"}, + {IP: "4.3.2.4"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "4.3.2.1"}, + {IP: "4.3.2.2"}, + {IP: "4.3.2.3"}, + {IP: "4.3.2.4"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints satisfy and endpoint addresses length less than master count", + serviceName: "foo", + ip: "4.3.2.2", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + additionalMasters: 3, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "4.3.2.1"}, + {IP: "4.3.2.2"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: nil, + }, + { + testName: "existing endpoints current IP missing and address length less than master count", + serviceName: "foo", + ip: "4.3.2.2", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + additionalMasters: 3, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "4.3.2.1"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{ + {IP: "4.3.2.1"}, + {IP: "4.3.2.2"}, + }, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints wrong name", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("bar", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectCreate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints wrong IP", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "4.3.2.1"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints wrong port", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 9090, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints wrong protocol", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "UDP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints wrong port name", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "baz", Port: 8080, Protocol: "TCP"}}, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "baz", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "existing endpoints extra service ports satisfy", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + {Name: "bar", Port: 1000, Protocol: "TCP"}, + {Name: "baz", Port: 1010, Protocol: "TCP"}, + }, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + {Name: "bar", Port: 1000, Protocol: "TCP"}, + {Name: "baz", Port: 1010, Protocol: "TCP"}, + }, + }}, + }}, + }, + }, + { + testName: "existing endpoints extra service ports missing port", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + {Name: "bar", Port: 1000, Protocol: "TCP"}, + }, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + {Name: "bar", Port: 1000, Protocol: "TCP"}, + }, + }}, + }, + }, + { + testName: "no existing sctp endpoints", + serviceName: "boo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "boo", Port: 7777, Protocol: "SCTP"}}, + endpoints: nil, + expectCreate: &corev1.Endpoints{ + ObjectMeta: om("boo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "boo", Port: 7777, Protocol: "SCTP"}}, + }}, + }, + }, + } + for _, test := range reconcileTests { + fakeClient := fake.NewSimpleClientset() + if test.endpoints != nil { + fakeClient = fake.NewSimpleClientset(test.endpoints) + } + epAdapter := NewEndpointsAdapter(fakeClient.CoreV1(), nil) + reconciler := NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter) + err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, true) + if err != nil { + t.Errorf("case %q: unexpected error: %v", test.testName, err) + } + + updates := []core.UpdateAction{} + for _, action := range fakeClient.Actions() { + if action.GetVerb() != "update" { + continue + } + updates = append(updates, action.(core.UpdateAction)) + } + if test.expectUpdate != nil { + if len(updates) != 1 { + t.Errorf("case %q: unexpected updates: %v", test.testName, updates) + } else if e, a := test.expectUpdate, updates[0].GetObject(); !reflect.DeepEqual(e, a) { + t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a) + } + } + if test.expectUpdate == nil && len(updates) > 0 { + t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates) + } + + creates := []core.CreateAction{} + for _, action := range fakeClient.Actions() { + if action.GetVerb() != "create" { + continue + } + creates = append(creates, action.(core.CreateAction)) + } + if test.expectCreate != nil { + if len(creates) != 1 { + t.Errorf("case %q: unexpected creates: %v", test.testName, creates) + } else if e, a := test.expectCreate, creates[0].GetObject(); !reflect.DeepEqual(e, a) { + t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a) + } + } + if test.expectCreate == nil && len(creates) > 0 { + t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates) + } + + } + + nonReconcileTests := []struct { + testName string + serviceName string + ip string + endpointPorts []corev1.EndpointPort + additionalMasters int + endpoints *corev1.EndpointsList + expectUpdate *corev1.Endpoints // nil means none expected + expectCreate *corev1.Endpoints // nil means none expected + }{ + { + testName: "existing endpoints extra service ports missing port no update", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + {Name: "bar", Port: 1000, Protocol: "TCP"}, + }, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: nil, + }, + { + testName: "existing endpoints extra service ports, wrong ports, wrong IP", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + {Name: "bar", Port: 1000, Protocol: "TCP"}, + }, + endpoints: &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "4.3.2.1"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }}, + }, + expectUpdate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + { + testName: "no existing endpoints", + serviceName: "foo", + ip: "1.2.3.4", + endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + endpoints: nil, + expectCreate: &corev1.Endpoints{ + ObjectMeta: om("foo", true), + Subsets: []corev1.EndpointSubset{{ + Addresses: []corev1.EndpointAddress{{IP: "1.2.3.4"}}, + Ports: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, + }}, + }, + }, + } + for _, test := range nonReconcileTests { + fakeClient := fake.NewSimpleClientset() + if test.endpoints != nil { + fakeClient = fake.NewSimpleClientset(test.endpoints) + } + epAdapter := NewEndpointsAdapter(fakeClient.CoreV1(), nil) + reconciler := NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter) + err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, false) + if err != nil { + t.Errorf("case %q: unexpected error: %v", test.testName, err) + } + + updates := []core.UpdateAction{} + for _, action := range fakeClient.Actions() { + if action.GetVerb() != "update" { + continue + } + updates = append(updates, action.(core.UpdateAction)) + } + if test.expectUpdate != nil { + if len(updates) != 1 { + t.Errorf("case %q: unexpected updates: %v", test.testName, updates) + } else if e, a := test.expectUpdate, updates[0].GetObject(); !reflect.DeepEqual(e, a) { + t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a) + } + } + if test.expectUpdate == nil && len(updates) > 0 { + t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates) + } + + creates := []core.CreateAction{} + for _, action := range fakeClient.Actions() { + if action.GetVerb() != "create" { + continue + } + creates = append(creates, action.(core.CreateAction)) + } + if test.expectCreate != nil { + if len(creates) != 1 { + t.Errorf("case %q: unexpected creates: %v", test.testName, creates) + } else if e, a := test.expectCreate, creates[0].GetObject(); !reflect.DeepEqual(e, a) { + t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a) + } + } + if test.expectCreate == nil && len(creates) > 0 { + t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates) + } + + } + +} + +func TestEmptySubsets(t *testing.T) { + ns := metav1.NamespaceDefault + om := func(name string) metav1.ObjectMeta { + return metav1.ObjectMeta{Namespace: ns, Name: name} + } + endpoints := &corev1.EndpointsList{ + Items: []corev1.Endpoints{{ + ObjectMeta: om("foo"), + Subsets: nil, + }}, + } + fakeClient := fake.NewSimpleClientset() + if endpoints != nil { + fakeClient = fake.NewSimpleClientset(endpoints) + } + epAdapter := NewEndpointsAdapter(fakeClient.CoreV1(), nil) + reconciler := NewMasterCountEndpointReconciler(1, epAdapter) + endpointPorts := []corev1.EndpointPort{ + {Name: "foo", Port: 8080, Protocol: "TCP"}, + } + err := reconciler.RemoveEndpoints("foo", netutils.ParseIPSloppy("1.2.3.4"), endpointPorts) + if err != nil { + t.Errorf("unexpected error: %v", err) + } +}