Improve reconciler test result checking

Consistently verify creates/updates based on the fake client's action
tracking, not based on the return values of the reconciler functions.
(This will also let us check that both Endpoints and EndpointSlices
were created/updated correctly.)
This commit is contained in:
Dan Winship 2022-04-27 10:38:00 -04:00
parent 4033de2034
commit f543e7434a
5 changed files with 132 additions and 174 deletions

View File

@ -123,6 +123,7 @@ func (adapter *EndpointsAdapter) EnsureEndpointSliceFromEndpoints(namespace stri
func endpointSliceFromEndpoints(endpoints *corev1.Endpoints) *discovery.EndpointSlice { func endpointSliceFromEndpoints(endpoints *corev1.Endpoints) *discovery.EndpointSlice {
endpointSlice := &discovery.EndpointSlice{} endpointSlice := &discovery.EndpointSlice{}
endpointSlice.Name = endpoints.Name endpointSlice.Name = endpoints.Name
endpointSlice.Namespace = endpoints.Namespace
endpointSlice.Labels = map[string]string{discovery.LabelServiceName: endpoints.Name} endpointSlice.Labels = map[string]string{discovery.LabelServiceName: endpoints.Name}
// TODO: Add support for dual stack here (and in the rest of // TODO: Add support for dual stack here (and in the rest of

View File

@ -113,18 +113,18 @@ func TestEndpointsAdapterCreate(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
endpointSlicesEnabled bool endpointSlicesEnabled bool
expectedError error expectedError error
expectedEndpoints *corev1.Endpoints expectedResult *corev1.Endpoints
expectedEndpointSlice *discovery.EndpointSlice expectCreate []runtime.Object
expectUpdate []runtime.Object
initialState []runtime.Object initialState []runtime.Object
endpointSlices []*discovery.EndpointSlice
namespaceParam string namespaceParam string
endpointsParam *corev1.Endpoints endpointsParam *corev1.Endpoints
}{ }{
"single-endpoint": { "single-endpoint": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints1, expectedResult: endpoints1,
expectedEndpointSlice: epSlice1, expectCreate: []runtime.Object{endpoints1, epSlice1},
initialState: []runtime.Object{}, initialState: []runtime.Object{},
namespaceParam: endpoints1.Namespace, namespaceParam: endpoints1.Namespace,
endpointsParam: endpoints1, endpointsParam: endpoints1,
@ -132,8 +132,8 @@ func TestEndpointsAdapterCreate(t *testing.T) {
"single-endpoint-partial-ipv6": { "single-endpoint-partial-ipv6": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints2, expectedResult: endpoints2,
expectedEndpointSlice: epSlice2, expectCreate: []runtime.Object{endpoints2, epSlice2},
initialState: []runtime.Object{}, initialState: []runtime.Object{},
namespaceParam: endpoints2.Namespace, namespaceParam: endpoints2.Namespace,
endpointsParam: endpoints2, endpointsParam: endpoints2,
@ -141,8 +141,8 @@ func TestEndpointsAdapterCreate(t *testing.T) {
"single-endpoint-full-ipv6": { "single-endpoint-full-ipv6": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints3, expectedResult: endpoints3,
expectedEndpointSlice: epSlice3, expectCreate: []runtime.Object{endpoints3, epSlice3},
initialState: []runtime.Object{}, initialState: []runtime.Object{},
namespaceParam: endpoints3.Namespace, namespaceParam: endpoints3.Namespace,
endpointsParam: endpoints3, endpointsParam: endpoints3,
@ -150,8 +150,8 @@ func TestEndpointsAdapterCreate(t *testing.T) {
"single-endpoint-no-slices": { "single-endpoint-no-slices": {
endpointSlicesEnabled: false, endpointSlicesEnabled: false,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints1, expectedResult: endpoints1,
expectedEndpointSlice: nil, expectCreate: []runtime.Object{endpoints1},
initialState: []runtime.Object{}, initialState: []runtime.Object{},
namespaceParam: endpoints1.Namespace, namespaceParam: endpoints1.Namespace,
endpointsParam: endpoints1, endpointsParam: endpoints1,
@ -159,11 +159,13 @@ func TestEndpointsAdapterCreate(t *testing.T) {
"existing-endpoint": { "existing-endpoint": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: errors.NewAlreadyExists(schema.GroupResource{Group: "", Resource: "endpoints"}, "foo"), expectedError: errors.NewAlreadyExists(schema.GroupResource{Group: "", Resource: "endpoints"}, "foo"),
expectedEndpoints: nil, expectedResult: nil,
expectedEndpointSlice: nil, initialState: []runtime.Object{endpoints1, epSlice1},
initialState: []runtime.Object{endpoints1},
namespaceParam: endpoints1.Namespace, namespaceParam: endpoints1.Namespace,
endpointsParam: endpoints1, endpointsParam: endpoints1,
// We expect the create to be attempted, we just also expect it to fail
expectCreate: []runtime.Object{endpoints1},
}, },
} }
@ -181,37 +183,20 @@ func TestEndpointsAdapterCreate(t *testing.T) {
t.Errorf("Expected error: %v, got: %v", testCase.expectedError, err) t.Errorf("Expected error: %v, got: %v", testCase.expectedError, err)
} }
if !apiequality.Semantic.DeepEqual(endpoints, testCase.expectedEndpoints) { if !apiequality.Semantic.DeepEqual(endpoints, testCase.expectedResult) {
t.Errorf("Expected endpoints: %v, got: %v", testCase.expectedEndpoints, endpoints) t.Errorf("Expected endpoints: %v, got: %v", testCase.expectedResult, endpoints)
} }
epSliceList, err := client.DiscoveryV1().EndpointSlices(testCase.namespaceParam).List(context.TODO(), metav1.ListOptions{}) err = verifyCreatesAndUpdates(client, testCase.expectCreate, nil)
if err != nil { if err != nil {
t.Fatalf("Error listing Endpoint Slices: %v", err) t.Errorf("unexpected error in side effects: %v", err)
}
if testCase.expectedEndpointSlice == nil {
if len(epSliceList.Items) != 0 {
t.Fatalf("Expected no Endpoint Slices, got: %v", epSliceList.Items)
}
} else {
if len(epSliceList.Items) == 0 {
t.Fatalf("No Endpoint Slices found, expected: %v", testCase.expectedEndpointSlice)
}
if len(epSliceList.Items) > 1 {
t.Errorf("Only 1 Endpoint Slice expected, got: %v", testCase.expectedEndpointSlice)
}
if !apiequality.Semantic.DeepEqual(*testCase.expectedEndpointSlice, epSliceList.Items[0]) {
t.Errorf("Expected Endpoint Slice: %v, got: %v", testCase.expectedEndpointSlice, epSliceList.Items[0])
}
} }
}) })
} }
} }
func TestEndpointsAdapterUpdate(t *testing.T) { func TestEndpointsAdapterUpdate(t *testing.T) {
endpoints1, _ := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.3", "10.1.2.4"}) endpoints1, epSlice1 := generateEndpointsAndSlice("foo", "testing", []int{80}, []string{"10.1.2.3", "10.1.2.4"})
endpoints2, epSlice2 := generateEndpointsAndSlice("foo", "testing", []int{80, 443}, []string{"10.1.2.3", "10.1.2.4", "10.1.2.5"}) endpoints2, epSlice2 := generateEndpointsAndSlice("foo", "testing", []int{80, 443}, []string{"10.1.2.3", "10.1.2.4", "10.1.2.5"})
endpoints3, _ := generateEndpointsAndSlice("bar", "testing", []int{80, 443}, []string{"10.1.2.3", "10.1.2.4", "10.1.2.5"}) endpoints3, _ := generateEndpointsAndSlice("bar", "testing", []int{80, 443}, []string{"10.1.2.3", "10.1.2.4", "10.1.2.5"})
@ -226,49 +211,57 @@ func TestEndpointsAdapterUpdate(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
endpointSlicesEnabled bool endpointSlicesEnabled bool
expectedError error expectedError error
expectedEndpoints *corev1.Endpoints expectedResult *corev1.Endpoints
expectedEndpointSlice *discovery.EndpointSlice expectCreate []runtime.Object
expectUpdate []runtime.Object
initialState []runtime.Object initialState []runtime.Object
endpointSlices []*discovery.EndpointSlice
namespaceParam string namespaceParam string
endpointsParam *corev1.Endpoints endpointsParam *corev1.Endpoints
}{ }{
"single-existing-endpoints-no-change": { "single-existing-endpoints-no-change": {
endpointSlicesEnabled: false, endpointSlicesEnabled: false,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints1, expectedResult: endpoints1,
expectedEndpointSlice: nil,
initialState: []runtime.Object{endpoints1}, initialState: []runtime.Object{endpoints1},
namespaceParam: "testing", namespaceParam: "testing",
endpointsParam: endpoints1, endpointsParam: endpoints1,
// Even though there's no change, we still expect Update() to be
// called, because this unit test ALWAYS calls Update().
expectUpdate: []runtime.Object{endpoints1},
}, },
"existing-endpointslice-replaced-with-updated-ipv4-address-type": { "existing-endpointslice-replaced-with-updated-ipv4-address-type": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints4, expectedResult: endpoints4,
expectedEndpointSlice: epSlice4IPv4, initialState: []runtime.Object{endpoints4, epSlice4IP},
initialState: []runtime.Object{endpoints4},
endpointSlices: []*discovery.EndpointSlice{epSlice4IP},
namespaceParam: "testing", namespaceParam: "testing",
endpointsParam: endpoints4, endpointsParam: endpoints4,
// When AddressType changes, we Delete+Create the EndpointSlice,
// so that shows up in expectCreate, not expectUpdate.
expectUpdate: []runtime.Object{endpoints4},
expectCreate: []runtime.Object{epSlice4IPv4},
}, },
"add-ports-and-ips": { "add-ports-and-ips": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: nil, expectedError: nil,
expectedEndpoints: endpoints2, expectedResult: endpoints2,
expectedEndpointSlice: epSlice2, expectUpdate: []runtime.Object{endpoints2, epSlice2},
initialState: []runtime.Object{endpoints1}, initialState: []runtime.Object{endpoints1, epSlice1},
namespaceParam: "testing", namespaceParam: "testing",
endpointsParam: endpoints2, endpointsParam: endpoints2,
}, },
"missing-endpoints": { "missing-endpoints": {
endpointSlicesEnabled: true, endpointSlicesEnabled: true,
expectedError: errors.NewNotFound(schema.GroupResource{Group: "", Resource: "endpoints"}, "bar"), expectedError: errors.NewNotFound(schema.GroupResource{Group: "", Resource: "endpoints"}, "bar"),
expectedEndpoints: nil, expectedResult: nil,
expectedEndpointSlice: nil, initialState: []runtime.Object{endpoints1, epSlice1},
initialState: []runtime.Object{endpoints1},
namespaceParam: "testing", namespaceParam: "testing",
endpointsParam: endpoints3, endpointsParam: endpoints3,
// We expect the update to be attempted, we just also expect it to fail
expectUpdate: []runtime.Object{endpoints3},
}, },
} }
@ -286,30 +279,13 @@ func TestEndpointsAdapterUpdate(t *testing.T) {
t.Errorf("Expected error: %v, got: %v", testCase.expectedError, err) t.Errorf("Expected error: %v, got: %v", testCase.expectedError, err)
} }
if !apiequality.Semantic.DeepEqual(endpoints, testCase.expectedEndpoints) { if !apiequality.Semantic.DeepEqual(endpoints, testCase.expectedResult) {
t.Errorf("Expected endpoints: %v, got: %v", testCase.expectedEndpoints, endpoints) t.Errorf("Expected endpoints: %v, got: %v", testCase.expectedResult, endpoints)
} }
epSliceList, err := client.DiscoveryV1().EndpointSlices(testCase.namespaceParam).List(context.TODO(), metav1.ListOptions{}) err = verifyCreatesAndUpdates(client, testCase.expectCreate, testCase.expectUpdate)
if err != nil { if err != nil {
t.Fatalf("Error listing Endpoint Slices: %v", err) t.Errorf("unexpected error in side effects: %v", err)
}
if testCase.expectedEndpointSlice == nil {
if len(epSliceList.Items) != 0 {
t.Fatalf("Expected no Endpoint Slices, got: %v", epSliceList.Items)
}
} else {
if len(epSliceList.Items) == 0 {
t.Fatalf("No Endpoint Slices found, expected: %v", testCase.expectedEndpointSlice)
}
if len(epSliceList.Items) > 1 {
t.Errorf("Only 1 Endpoint Slice expected, got: %v", testCase.expectedEndpointSlice)
}
if !apiequality.Semantic.DeepEqual(*testCase.expectedEndpointSlice, epSliceList.Items[0]) {
t.Errorf("Expected Endpoint Slice: %v, got: %v", testCase.expectedEndpointSlice, epSliceList.Items[0])
}
} }
}) })
} }

View File

@ -17,10 +17,16 @@ limitations under the License.
package reconcilers package reconcilers
import ( import (
"fmt"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1" discoveryv1 "k8s.io/api/discovery/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/kubernetes/fake"
k8stesting "k8s.io/client-go/testing"
) )
func makeEndpointsArray(name string, ips []string, ports []corev1.EndpointPort) []runtime.Object { func makeEndpointsArray(name string, ips []string, ports []corev1.EndpointPort) []runtime.Object {
@ -50,3 +56,51 @@ func makeEndpoints(name string, ips []string, ports []corev1.EndpointPort) *core
} }
return endpoints return endpoints
} }
func verifyCreatesAndUpdates(fakeClient *fake.Clientset, expectedCreates, expectedUpdates []runtime.Object) error {
errors := []error{}
updates := []k8stesting.UpdateAction{}
creates := []k8stesting.CreateAction{}
for _, action := range fakeClient.Actions() {
if action.GetVerb() == "update" {
updates = append(updates, action.(k8stesting.UpdateAction))
} else if action.GetVerb() == "create" {
creates = append(creates, action.(k8stesting.CreateAction))
}
}
if len(creates) != len(expectedCreates) {
errors = append(errors, fmt.Errorf("expected %d creates got %d", len(expectedCreates), len(creates)))
}
for i := 0; i < len(creates) || i < len(expectedCreates); i++ {
var expected, actual runtime.Object
if i < len(creates) {
actual = creates[i].GetObject()
}
if i < len(expectedCreates) {
expected = expectedCreates[i]
}
if !apiequality.Semantic.DeepEqual(expected, actual) {
errors = append(errors, fmt.Errorf("expected create %d to be:\n%#v\ngot:\n%#v\n", i, expected, actual))
}
}
if len(updates) != len(expectedUpdates) {
errors = append(errors, fmt.Errorf("expected %d updates got %d", len(expectedUpdates), len(updates)))
}
for i := 0; i < len(updates) || i < len(expectedUpdates); i++ {
var expected, actual runtime.Object
if i < len(updates) {
actual = updates[i].GetObject()
}
if i < len(expectedUpdates) {
expected = expectedUpdates[i]
}
if !apiequality.Semantic.DeepEqual(expected, actual) {
errors = append(errors, fmt.Errorf("expected update %d to be:\n%#v\ngot:\n%#v\n", i, expected, actual))
}
}
return utilerrors.NewAggregate(errors)
}

View File

@ -17,13 +17,11 @@ limitations under the License.
package reconcilers package reconcilers
import ( import (
"reflect"
"testing" "testing"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing"
netutils "k8s.io/utils/net" netutils "k8s.io/utils/net"
) )
@ -186,43 +184,12 @@ func TestMasterCountEndpointReconciler(t *testing.T) {
reconciler := NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter) reconciler := NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter)
err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, true) err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, true)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error reconciling: %v", err)
} }
updates := []core.UpdateAction{} err = verifyCreatesAndUpdates(fakeClient, test.expectCreate, test.expectUpdate)
for _, action := range fakeClient.Actions() { if err != nil {
if action.GetVerb() != "update" { t.Errorf("unexpected error in side effects: %v", err)
continue
}
updates = append(updates, action.(core.UpdateAction))
}
if test.expectUpdate != nil {
if len(updates) != 1 {
t.Errorf("unexpected updates: %v", updates)
} else if e, a := test.expectUpdate[0], updates[0].GetObject(); !reflect.DeepEqual(e, a) {
t.Errorf("expected update:\n%#v\ngot:\n%#v\n", e, a)
}
}
if test.expectUpdate == nil && len(updates) > 0 {
t.Errorf("no update expected, yet saw: %v", 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("unexpected creates: %v", creates)
} else if e, a := test.expectCreate[0], creates[0].GetObject(); !reflect.DeepEqual(e, a) {
t.Errorf("expected create:\n%#v\ngot:\n%#v\n", e, a)
}
}
if test.expectCreate == nil && len(creates) > 0 {
t.Errorf("no create expected, yet saw: %v", creates)
} }
}) })
} }
@ -275,47 +242,15 @@ func TestMasterCountEndpointReconciler(t *testing.T) {
reconciler := NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter) reconciler := NewMasterCountEndpointReconciler(test.additionalMasters+1, epAdapter)
err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, false) err := reconciler.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, false)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error reconciling: %v", err)
} }
updates := []core.UpdateAction{} err = verifyCreatesAndUpdates(fakeClient, test.expectCreate, test.expectUpdate)
for _, action := range fakeClient.Actions() { if err != nil {
if action.GetVerb() != "update" { t.Errorf("unexpected error in side effects: %v", err)
continue
}
updates = append(updates, action.(core.UpdateAction))
}
if test.expectUpdate != nil {
if len(updates) != 1 {
t.Errorf("unexpected updates: %v", updates)
} else if e, a := test.expectUpdate[0], updates[0].GetObject(); !reflect.DeepEqual(e, a) {
t.Errorf("expected update:\n%#v\ngot:\n%#v\n", e, a)
}
}
if test.expectUpdate == nil && len(updates) > 0 {
t.Errorf("no update expected, yet saw: %v", 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("unexpected creates: %v", creates)
} else if e, a := test.expectCreate[0], creates[0].GetObject(); !reflect.DeepEqual(e, a) {
t.Errorf("expected create:\n%#v\ngot:\n%#v\n", e, a)
}
}
if test.expectCreate == nil && len(creates) > 0 {
t.Errorf("no create expected, yet saw: %v", creates)
} }
}) })
} }
} }
func TestEmptySubsets(t *testing.T) { func TestEmptySubsets(t *testing.T) {

View File

@ -22,8 +22,6 @@ https://github.com/openshift/origin/blob/bb340c5dd5ff72718be86fb194dedc0faed7f4c
*/ */
import ( import (
"context"
"reflect"
"testing" "testing"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -89,6 +87,7 @@ func TestLeaseEndpointReconciler(t *testing.T) {
endpointKeys []string endpointKeys []string
initialState []runtime.Object initialState []runtime.Object
expectUpdate []runtime.Object expectUpdate []runtime.Object
expectCreate []runtime.Object
}{ }{
{ {
testName: "no existing endpoints", testName: "no existing endpoints",
@ -96,7 +95,7 @@ func TestLeaseEndpointReconciler(t *testing.T) {
ip: "1.2.3.4", ip: "1.2.3.4",
endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
initialState: nil, initialState: nil,
expectUpdate: makeEndpointsArray("foo", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}), expectCreate: makeEndpointsArray("foo", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}),
}, },
{ {
testName: "existing endpoints satisfy", testName: "existing endpoints satisfy",
@ -154,7 +153,7 @@ func TestLeaseEndpointReconciler(t *testing.T) {
ip: "1.2.3.4", ip: "1.2.3.4",
endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
initialState: makeEndpointsArray("bar", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}), initialState: makeEndpointsArray("bar", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}),
expectUpdate: makeEndpointsArray("foo", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}), expectCreate: makeEndpointsArray("foo", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}),
}, },
{ {
testName: "existing endpoints wrong IP", testName: "existing endpoints wrong IP",
@ -253,17 +252,14 @@ func TestLeaseEndpointReconciler(t *testing.T) {
r := NewLeaseEndpointReconciler(epAdapter, fakeLeases) r := NewLeaseEndpointReconciler(epAdapter, fakeLeases)
err := r.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, true) err := r.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, true)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error reconciling: %v", err)
} }
actualEndpoints, err := clientset.CoreV1().Endpoints(corev1.NamespaceDefault).Get(context.TODO(), test.serviceName, metav1.GetOptions{})
err = verifyCreatesAndUpdates(clientset, test.expectCreate, test.expectUpdate)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error in side effects: %v", err)
}
if test.expectUpdate != nil {
if e, a := test.expectUpdate[0], actualEndpoints; !reflect.DeepEqual(e, a) {
t.Errorf("expected update:\n%#v\ngot:\n%#v\n", e, a)
}
} }
if updatedKeys := fakeLeases.GetUpdatedKeys(); len(updatedKeys) != 1 || updatedKeys[0] != test.ip { if updatedKeys := fakeLeases.GetUpdatedKeys(); len(updatedKeys) != 1 || updatedKeys[0] != test.ip {
t.Errorf("expected the master's IP to be refreshed, but the following IPs were refreshed instead: %v", updatedKeys) t.Errorf("expected the master's IP to be refreshed, but the following IPs were refreshed instead: %v", updatedKeys)
} }
@ -278,6 +274,7 @@ func TestLeaseEndpointReconciler(t *testing.T) {
endpointKeys []string endpointKeys []string
initialState []runtime.Object initialState []runtime.Object
expectUpdate []runtime.Object expectUpdate []runtime.Object
expectCreate []runtime.Object
}{ }{
{ {
testName: "existing endpoints extra service ports missing port no update", testName: "existing endpoints extra service ports missing port no update",
@ -307,7 +304,7 @@ func TestLeaseEndpointReconciler(t *testing.T) {
ip: "1.2.3.4", ip: "1.2.3.4",
endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
initialState: nil, initialState: nil,
expectUpdate: makeEndpointsArray("foo", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}), expectCreate: makeEndpointsArray("foo", []string{"1.2.3.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}),
}, },
} }
for _, test := range nonReconcileTests { for _, test := range nonReconcileTests {
@ -319,17 +316,14 @@ func TestLeaseEndpointReconciler(t *testing.T) {
r := NewLeaseEndpointReconciler(epAdapter, fakeLeases) r := NewLeaseEndpointReconciler(epAdapter, fakeLeases)
err := r.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, false) err := r.ReconcileEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts, false)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error reconciling: %v", err)
} }
actualEndpoints, err := clientset.CoreV1().Endpoints(corev1.NamespaceDefault).Get(context.TODO(), test.serviceName, metav1.GetOptions{})
err = verifyCreatesAndUpdates(clientset, test.expectCreate, test.expectUpdate)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error in side effects: %v", err)
}
if test.expectUpdate != nil {
if e, a := test.expectUpdate[0], actualEndpoints; !reflect.DeepEqual(e, a) {
t.Errorf("expected update:\n%#v\ngot:\n%#v\n", e, a)
}
} }
if updatedKeys := fakeLeases.GetUpdatedKeys(); len(updatedKeys) != 1 || updatedKeys[0] != test.ip { if updatedKeys := fakeLeases.GetUpdatedKeys(); len(updatedKeys) != 1 || updatedKeys[0] != test.ip {
t.Errorf("expected the master's IP to be refreshed, but the following IPs were refreshed instead: %v", updatedKeys) t.Errorf("expected the master's IP to be refreshed, but the following IPs were refreshed instead: %v", updatedKeys)
} }
@ -371,6 +365,7 @@ func TestLeaseRemoveEndpoints(t *testing.T) {
endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}, endpointPorts: []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
endpointKeys: []string{"1.2.3.4", "4.3.2.2", "4.3.2.3", "4.3.2.4"}, endpointKeys: []string{"1.2.3.4", "4.3.2.2", "4.3.2.3", "4.3.2.4"},
initialState: makeEndpointsArray("foo", nil, nil), initialState: makeEndpointsArray("foo", nil, nil),
expectUpdate: makeEndpointsArray("foo", []string{"1.2.3.4", "4.3.2.2", "4.3.2.3", "4.3.2.4"}, []corev1.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}}),
}, },
} }
for _, test := range stopTests { for _, test := range stopTests {
@ -382,17 +377,14 @@ func TestLeaseRemoveEndpoints(t *testing.T) {
r := NewLeaseEndpointReconciler(epAdapter, fakeLeases) r := NewLeaseEndpointReconciler(epAdapter, fakeLeases)
err := r.RemoveEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts) err := r.RemoveEndpoints(test.serviceName, netutils.ParseIPSloppy(test.ip), test.endpointPorts)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error reconciling: %v", err)
} }
actualEndpoints, err := clientset.CoreV1().Endpoints(corev1.NamespaceDefault).Get(context.TODO(), test.serviceName, metav1.GetOptions{})
err = verifyCreatesAndUpdates(clientset, nil, test.expectUpdate)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error in side effects: %v", err)
}
if test.expectUpdate != nil {
if e, a := test.expectUpdate[0], actualEndpoints; !reflect.DeepEqual(e, a) {
t.Errorf("expected update:\n%#v\ngot:\n%#v\n", e, a)
}
} }
for _, key := range fakeLeases.GetUpdatedKeys() { for _, key := range fakeLeases.GetUpdatedKeys() {
if key == test.ip { if key == test.ip {
t.Errorf("Found ip %s in leases but shouldn't be there", key) t.Errorf("Found ip %s in leases but shouldn't be there", key)