diff --git a/pkg/neg/syncers/endpoints_calculator_test.go b/pkg/neg/syncers/endpoints_calculator_test.go new file mode 100644 index 0000000000..195a457155 --- /dev/null +++ b/pkg/neg/syncers/endpoints_calculator_test.go @@ -0,0 +1,209 @@ +/* +Copyright 2020 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 syncers + +import ( + "fmt" + "reflect" + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + listers "k8s.io/client-go/listers/core/v1" + negtypes "k8s.io/ingress-gce/pkg/neg/types" + "k8s.io/legacy-cloud-providers/gce" +) + +// TestLocalGetEndpointSet verifies the GetEndpointSet method implemented by the LocalL4ILBEndpointsCalculator. +// The L7 implementation is tested in TestToZoneNetworkEndpointMapUtil. +func TestLocalGetEndpointSet(t *testing.T) { + t.Parallel() + _, transactionSyncer := newL4ILBTestTransactionSyncer(negtypes.NewAdapter(gce.NewFakeGCECloud(gce.DefaultTestClusterValues())), false) + nodeNames := []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6} + for i := 0; i < len(nodeNames); i++ { + err := transactionSyncer.nodeLister.Add(&v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + //Namespace: testServiceNamespace, + Name: nodeNames[i], + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeInternalIP, + Address: fmt.Sprintf("1.2.3.%d", i+1), + }, + }, + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionTrue, + }, + }, + }, + }) + if err != nil { + t.Errorf("Failed to add node %s to syncer's nodeLister, err %v", nodeNames[i], err) + } + } + zoneGetter := negtypes.NewFakeZoneGetter() + nodeLister := listers.NewNodeLister(transactionSyncer.nodeLister) + + testCases := []struct { + desc string + endpoints *v1.Endpoints + endpointSets map[string]negtypes.NetworkEndpointSet + networkEndpointType negtypes.NetworkEndpointType + }{ + { + desc: "default endpoints", + endpoints: getDefaultEndpoint(), + // only 4 out of 6 nodes are picked since there are > 4 endpoints, but they are found only on 4 nodes. + endpointSets: map[string]negtypes.NetworkEndpointSet{ + negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), + negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}), + }, + networkEndpointType: negtypes.VmPrimaryIpEndpointType, + }, + { + desc: "no endpoints", + endpoints: &v1.Endpoints{}, + // No nodes are picked as there are no service endpoints. + endpointSets: nil, + networkEndpointType: negtypes.VmPrimaryIpEndpointType, + }, + } + svcKey := fmt.Sprintf("%s/%s", testServiceName, testServiceNamespace) + ec := NewLocalL4ILBEndpointsCalculator(nodeLister, zoneGetter, svcKey) + for _, tc := range testCases { + retSet, _, err := ec.CalculateEndpoints(tc.endpoints, nil) + if err != nil { + t.Errorf("For case %q, expect nil error, but got %v.", tc.desc, err) + } + if !reflect.DeepEqual(retSet, tc.endpointSets) { + t.Errorf("For case %q, expecting endpoint set %v, but got %v.", tc.desc, tc.endpointSets, retSet) + } + } +} + +// TestClusterGetEndpointSet verifies the GetEndpointSet method implemented by the ClusterL4ILBEndpointsCalculator. +func TestClusterGetEndpointSet(t *testing.T) { + t.Parallel() + _, transactionSyncer := newL4ILBTestTransactionSyncer(negtypes.NewAdapter(gce.NewFakeGCECloud(gce.DefaultTestClusterValues())), true) + nodeNames := []string{testInstance1, testInstance2, testInstance3, testInstance4, testInstance5, testInstance6} + for i := 0; i < len(nodeNames); i++ { + err := transactionSyncer.nodeLister.Add(&v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: nodeNames[i], + }, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeInternalIP, + Address: fmt.Sprintf("1.2.3.%d", i+1), + }, + }, + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionTrue, + }, + }, + }, + }) + if err != nil { + t.Errorf("Failed to add node %s to syncer's nodeLister, err %v", nodeNames[i], err) + } + } + zoneGetter := negtypes.NewFakeZoneGetter() + nodeLister := listers.NewNodeLister(transactionSyncer.nodeLister) + testCases := []struct { + desc string + endpoints *v1.Endpoints + endpointSets map[string]negtypes.NetworkEndpointSet + networkEndpointType negtypes.NetworkEndpointType + }{ + { + desc: "default endpoints", + endpoints: getDefaultEndpoint(), + // all nodes are picked since, in this mode, endpoints running do not need to run on the selected node. + endpointSets: map[string]negtypes.NetworkEndpointSet{ + negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), + negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}, + negtypes.NetworkEndpoint{IP: "1.2.3.5", Node: testInstance5}, negtypes.NetworkEndpoint{IP: "1.2.3.6", Node: testInstance6}), + }, + networkEndpointType: negtypes.VmPrimaryIpEndpointType, + }, + { + desc: "no endpoints", + // all nodes are picked since, in this mode, endpoints running do not need to run on the selected node. + // Even when there are no service endpoints, nodes are selected at random. + endpoints: &v1.Endpoints{}, + endpointSets: map[string]negtypes.NetworkEndpointSet{ + negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), + negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}, + negtypes.NetworkEndpoint{IP: "1.2.3.5", Node: testInstance5}, negtypes.NetworkEndpoint{IP: "1.2.3.6", Node: testInstance6}), + }, + networkEndpointType: negtypes.VmPrimaryIpEndpointType, + }, + } + svcKey := fmt.Sprintf("%s/%s", testServiceName, testServiceNamespace) + ec := NewClusterL4ILBEndpointsCalculator(nodeLister, zoneGetter, svcKey) + for _, tc := range testCases { + retSet, _, err := ec.CalculateEndpoints(tc.endpoints, nil) + if err != nil { + t.Errorf("For case %q, expect nil error, but got %v.", tc.desc, err) + } + if !reflect.DeepEqual(retSet, tc.endpointSets) { + t.Errorf("For case %q, expecting endpoint set %v, but got %v.", tc.desc, tc.endpointSets, retSet) + } + } +} + +// TestGetPerZoneSubsetCount verifies the perZoneSubsetCount method. +func TestGetPerZoneSubsetCount(t *testing.T) { + t.Parallel() + zoneCount := 3 + result := 0 + tcs := []struct { + desc string + randomize bool + startCount int + endCount int + expectedCount int + }{ + {desc: "start with endpoints, drop to none, ExternalTrafficPolicy:Local", startCount: 5, endCount: 0, expectedCount: 0}, + {desc: "no endpoints, ExternalTrafficPolicy:Local", startCount: 0, endCount: 0, expectedCount: 0}, + {desc: "valid endpoints increase, ExternalTrafficPolicy:Local", startCount: 5, endCount: 10, expectedCount: 10 / zoneCount}, + // If total number of nodes is less than the number of zones, per zone count will be 1. + {desc: "valid endpoints decrease, ExternalTrafficPolicy:Local", startCount: 5, endCount: 2, expectedCount: 1}, + {desc: "valid endpoints > limit, ExternalTrafficPolicy:Local", startCount: 5, endCount: 258, expectedCount: maxSubsetSizeLocal / zoneCount}, + {desc: "start with endpoints, drop to none, ExternalTrafficPolicy:Cluster", randomize: true, startCount: 5, endCount: 0, expectedCount: maxSubsetSizeDefault / zoneCount}, + {desc: "no endpoints, random true, ExternalTrafficPolicy:Cluster", randomize: true, startCount: 0, endCount: 0, expectedCount: maxSubsetSizeDefault / zoneCount}, + {desc: "valid endpoints increase, ExternalTrafficPolicy:Cluster", randomize: true, startCount: 5, endCount: 10, expectedCount: maxSubsetSizeDefault / zoneCount}, + {desc: "valid endpoints decrease, ExternalTrafficPolicy:Cluster", randomize: true, startCount: 5, endCount: 2, expectedCount: maxSubsetSizeDefault / zoneCount}, + } + for _, tc := range tcs { + if tc.randomize { + result = NewClusterL4ILBEndpointsCalculator(nil, nil, "test").getPerZoneSubsetCount(zoneCount, tc.endCount) + } else { + result = NewLocalL4ILBEndpointsCalculator(nil, nil, "test").getPerZoneSubsetCount(zoneCount, tc.endCount) + } + if result != tc.expectedCount { + t.Errorf("For test case '%s', expected subsetCount of %d, but got %d", tc.desc, tc.expectedCount, result) + } + } +} diff --git a/pkg/neg/syncers/subsets_test.go b/pkg/neg/syncers/subsets_test.go index 29830b401f..3b1f252e4b 100644 --- a/pkg/neg/syncers/subsets_test.go +++ b/pkg/neg/syncers/subsets_test.go @@ -117,33 +117,6 @@ func TestNoRemovals(t *testing.T) { } } -func TestGetSubsetCount(t *testing.T) { - t.Parallel() - zoneCount := 3 - tcs := []struct { - desc string - randomize bool - startCount int - endCount int - expectedCount int - }{ - {desc: "start with endpoints, drop to none", startCount: 5, endCount: 0, expectedCount: zoneCount}, - {desc: "no endpoints", startCount: 0, endCount: 0, expectedCount: zoneCount}, - {desc: "valid endpoints increase", startCount: 5, endCount: 10, expectedCount: 10}, - {desc: "valid endpoints decrease", startCount: 5, endCount: 2, expectedCount: 2}, - {desc: "start with endpoints, drop to none, random true", randomize: true, startCount: 5, endCount: 0, expectedCount: 5}, - {desc: "no endpoints, random true", randomize: true, startCount: 0, endCount: 0, expectedCount: zoneCount}, - {desc: "valid endpoints increase, random true", randomize: true, startCount: 5, endCount: 10, expectedCount: 10}, - {desc: "valid endpoints decrease, random true", randomize: true, startCount: 5, endCount: 2, expectedCount: 5}, - } - for _, tc := range tcs { - result := getSubsetCount(tc.startCount, tc.endCount, zoneCount, tc.randomize) - if result != tc.expectedCount { - t.Errorf("For test case '%s', expected subsetCount of %d, but got %d", tc.desc, tc.expectedCount, result) - } - } -} - func validateSubset(subset []*v1.Node, nodes []*v1.Node) bool { for _, val := range subset { found := false diff --git a/pkg/neg/syncers/transaction_test.go b/pkg/neg/syncers/transaction_test.go index d6ad6dffc2..1fdc25601f 100644 --- a/pkg/neg/syncers/transaction_test.go +++ b/pkg/neg/syncers/transaction_test.go @@ -47,6 +47,8 @@ const ( testZone2 = "zone2" testInstance3 = "instance3" testInstance4 = "instance4" + testInstance5 = "instance5" + testInstance6 = "instance6" testNamespace = "ns" testService = "svc" ) diff --git a/pkg/neg/syncers/utils_test.go b/pkg/neg/syncers/utils_test.go index 1ab12701dd..bb83c15022 100644 --- a/pkg/neg/syncers/utils_test.go +++ b/pkg/neg/syncers/utils_test.go @@ -28,7 +28,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - listers "k8s.io/client-go/listers/core/v1" "k8s.io/ingress-gce/pkg/composite" negtypes "k8s.io/ingress-gce/pkg/neg/types" "k8s.io/legacy-cloud-providers/gce" @@ -386,66 +385,6 @@ func TestEnsureNetworkEndpointGroup(t *testing.T) { } } -func TestToZonePrimaryIPNetworkEndpointMapUtil(t *testing.T) { - t.Parallel() - // Syncer for externalTrafficPolicy: Local case - _, transactionSyncer := newL4ILBTestTransactionSyncer(negtypes.NewAdapter(gce.NewFakeGCECloud(gce.DefaultTestClusterValues())), false) - nodeNames := []string{testInstance1, testInstance2, testInstance3, testInstance4} - for i := 0; i < len(nodeNames); i++ { - err := transactionSyncer.nodeLister.Add(&v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: testServiceNamespace, - Name: nodeNames[i], - }, - Status: v1.NodeStatus{ - Addresses: []v1.NodeAddress{ - { - Type: v1.NodeInternalIP, - Address: fmt.Sprintf("1.2.3.%d", i+1), - }, - }, - Conditions: []v1.NodeCondition{ - { - Type: v1.NodeReady, - Status: v1.ConditionTrue, - }, - }, - }, - }) - if err != nil { - t.Errorf("Failed to add node %s to syncer's nodeLister, err %v", nodeNames[i], err) - } - } - zoneGetter := negtypes.NewFakeZoneGetter() - nodeLister := listers.NewNodeLister(transactionSyncer.nodeLister) - - testCases := []struct { - desc string - endpointSets map[string]negtypes.NetworkEndpointSet - networkEndpointType negtypes.NetworkEndpointType - }{ - { - desc: "default endpoints", - // all nodes are picked since there are > 4 endpoints - endpointSets: map[string]negtypes.NetworkEndpointSet{ - negtypes.TestZone1: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.1", Node: testInstance1}, negtypes.NetworkEndpoint{IP: "1.2.3.2", Node: testInstance2}), - negtypes.TestZone2: negtypes.NewNetworkEndpointSet(negtypes.NetworkEndpoint{IP: "1.2.3.3", Node: testInstance3}, negtypes.NetworkEndpoint{IP: "1.2.3.4", Node: testInstance4}), - }, - networkEndpointType: negtypes.VmPrimaryIpEndpointType, - }, - } - svcKey := fmt.Sprintf("%s/%s", testServiceName, testServiceNamespace) - for _, tc := range testCases { - retSet, err := toZonePrimaryIPEndpointMap(getDefaultEndpoint(), nodeLister, zoneGetter, true, nil, svcKey) - if err != nil { - t.Errorf("For case %q, expect nil error, but got %v.", tc.desc, err) - } - if !reflect.DeepEqual(retSet, tc.endpointSets) { - t.Errorf("For case %q, expecting endpoint set %v, but got %v.", tc.desc, tc.endpointSets, retSet) - } - } -} - func TestToZoneNetworkEndpointMapUtil(t *testing.T) { t.Parallel() _, transactionSyncer := newTestTransactionSyncer(negtypes.NewAdapter(gce.NewFakeGCECloud(gce.DefaultTestClusterValues())), negtypes.VmIpPortEndpointType) diff --git a/pkg/neg/types/fakes.go b/pkg/neg/types/fakes.go index e6b22ebe58..21afc728b5 100644 --- a/pkg/neg/types/fakes.go +++ b/pkg/neg/types/fakes.go @@ -34,6 +34,8 @@ const ( TestInstance2 = "instance2" TestInstance3 = "instance3" TestInstance4 = "instance4" + TestInstance5 = "instance5" + TestInstance6 = "instance6" ) type fakeZoneGetter struct { @@ -44,7 +46,7 @@ func NewFakeZoneGetter() *fakeZoneGetter { return &fakeZoneGetter{ zoneInstanceMap: map[string]sets.String{ TestZone1: sets.NewString(TestInstance1, TestInstance2), - TestZone2: sets.NewString(TestInstance3, TestInstance4), + TestZone2: sets.NewString(TestInstance3, TestInstance4, TestInstance5, TestInstance6), }, } }