Refactor OnEndpointsUpdate for testing

This is a weird function, but I didn't want to change any semantics
until the tests are in place.  Testing exposed one bug where stale
connections of renamed ports were not marked stale.

There are other things that seem wrong here, more will follow.
This commit is contained in:
Tim Hockin
2017-02-01 12:47:29 -08:00
parent d578105a44
commit 9507af3c79
3 changed files with 365 additions and 39 deletions

View File

@@ -19,6 +19,8 @@ package iptables
import (
"testing"
"github.com/davecgh/go-spew/spew"
"fmt"
"net"
"strings"
@@ -1172,4 +1174,311 @@ func TestBuildServiceMapServiceUpdate(t *testing.T) {
}
}
// This is a coarse test, but it offers some modicum of confidence as the code is evolved.
func Test_accumulateEndpointsMap(t *testing.T) {
testCases := []struct {
newEndpoints api.Endpoints
oldEndpoints map[proxy.ServicePortName][]*endpointsInfo
expectedNew map[proxy.ServicePortName][]*endpointsInfo
expectedActive []proxy.ServicePortName
expectedStale []endpointServicePair
}{{
// Case[0]: nothing
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{},
expectedActive: []proxy.ServicePortName{},
expectedStale: []endpointServicePair{},
}, {
// Case[1]: no changes, unnamed port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}},
Ports: []api.EndpointPort{{
Name: "",
Port: 11,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{"1.1.1.1:11", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{"1.1.1.1:11", false},
},
},
expectedActive: []proxy.ServicePortName{makeServicePortName("ns1", "ep1", "")},
expectedStale: []endpointServicePair{},
}, {
// Case[2]: no changes, named port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}},
Ports: []api.EndpointPort{{
Name: "port",
Port: 11,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "port"): {
{"1.1.1.1:11", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "port"): {
{"1.1.1.1:11", false},
},
},
expectedActive: []proxy.ServicePortName{makeServicePortName("ns1", "ep1", "port")},
expectedStale: []endpointServicePair{},
}, {
// Case[3]: new port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}},
Ports: []api.EndpointPort{{
Port: 11,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{"1.1.1.1:11", false},
},
},
expectedActive: []proxy.ServicePortName{makeServicePortName("ns1", "ep1", "")},
expectedStale: []endpointServicePair{},
}, {
// Case[4]: remove port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{"1.1.1.1:11", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{},
expectedActive: []proxy.ServicePortName{},
expectedStale: []endpointServicePair{ /* can't detect this one */ },
}, {
// Case[5]: new IP and port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}, {
IP: "2.2.2.2",
}},
Ports: []api.EndpointPort{{
Name: "p1",
Port: 11,
}, {
Name: "p2",
Port: 22,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:11", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:11", false},
{"2.2.2.2:11", false},
},
makeServicePortName("ns1", "ep1", "p2"): {
{"1.1.1.1:22", false},
{"2.2.2.2:22", false},
},
},
expectedActive: []proxy.ServicePortName{
makeServicePortName("ns1", "ep1", "p1"),
makeServicePortName("ns1", "ep1", "p2"),
},
expectedStale: []endpointServicePair{},
}, {
// Case[6]: remove IP and port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}},
Ports: []api.EndpointPort{{
Name: "p1",
Port: 11,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:11", false},
{"2.2.2.2:11", false},
},
makeServicePortName("ns1", "ep1", "p2"): {
{"1.1.1.1:22", false},
{"2.2.2.2:22", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:11", false},
},
},
expectedActive: []proxy.ServicePortName{
makeServicePortName("ns1", "ep1", "p1"),
},
expectedStale: []endpointServicePair{{
endpoint: "2.2.2.2:11",
servicePortName: makeServicePortName("ns1", "ep1", "p1"),
}},
}, {
// Case[7]: rename port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}},
Ports: []api.EndpointPort{{
Name: "p2",
Port: 11,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:11", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p2"): {
{"1.1.1.1:11", false},
},
},
expectedActive: []proxy.ServicePortName{
makeServicePortName("ns1", "ep1", "p2"),
},
expectedStale: []endpointServicePair{},
}, {
// Case[8]: renumber port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}},
Ports: []api.EndpointPort{{
Name: "p1",
Port: 22,
}},
},
}
}),
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:11", false},
},
},
expectedNew: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{"1.1.1.1:22", false},
},
},
expectedActive: []proxy.ServicePortName{
makeServicePortName("ns1", "ep1", "p1"),
},
expectedStale: []endpointServicePair{{
endpoint: "1.1.1.1:11",
servicePortName: makeServicePortName("ns1", "ep1", "p1"),
}},
}}
for tci, tc := range testCases {
// outputs
newEndpoints := map[proxy.ServicePortName][]*endpointsInfo{}
svcPortToInfoMap := map[proxy.ServicePortName][]hostPortInfo{}
staleConnections := map[endpointServicePair]bool{}
activeEndpoints := map[proxy.ServicePortName]bool{}
accumulateEndpointsMap(&tc.newEndpoints, "host", tc.oldEndpoints,
&newEndpoints, &svcPortToInfoMap, &staleConnections, &activeEndpoints)
if len(newEndpoints) != len(tc.expectedNew) {
t.Errorf("[%d] expected %d new, got %d: %v", tci, len(tc.expectedNew), len(newEndpoints), spew.Sdump(newEndpoints))
}
for x := range tc.expectedNew {
if len(newEndpoints[x]) != len(tc.expectedNew[x]) {
t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(tc.expectedNew[x]), x, len(newEndpoints[x]))
} else {
for i := range newEndpoints[x] {
if *(newEndpoints[x][i]) != *(tc.expectedNew[x][i]) {
t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, tc.expectedNew[x][i], *(newEndpoints[x][i]))
}
}
}
}
if len(activeEndpoints) != len(tc.expectedActive) {
t.Errorf("[%d] expected %d active, got %d: %v", tci, len(tc.expectedActive), len(activeEndpoints), activeEndpoints)
}
for _, x := range tc.expectedActive {
if activeEndpoints[x] != true {
t.Errorf("[%d] expected active[%v], but didn't find it: %v", tci, x, activeEndpoints)
}
}
if len(staleConnections) != len(tc.expectedStale) {
t.Errorf("[%d] expected %d stale, got %d: %v", tci, len(tc.expectedStale), len(staleConnections), staleConnections)
}
for _, x := range tc.expectedStale {
if staleConnections[x] != true {
t.Errorf("[%d] expected stale[%v], but didn't find it: %v", tci, x, staleConnections)
}
}
}
}
func makeTestEndpoints(namespace, name string, eptFunc func(*api.Endpoints)) api.Endpoints {
ept := api.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
}
eptFunc(&ept)
return ept
}
func makeServicePortName(ns, name, port string) proxy.ServicePortName {
return proxy.ServicePortName{
NamespacedName: types.NamespacedName{
Namespace: ns,
Name: name,
},
Port: port,
}
}
// TODO(thockin): add *more* tests for syncProxyRules() or break it down further and test the pieces.