Merge pull request #56880 from MrHohn/kube-proxy-ipv6-fix

Automatic merge from submit-queue (batch tested with PRs 53689, 56880, 55856, 59289, 60249). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Harden kube-proxy for unmatched IP versions

**What this PR does / why we need it**:
This PR makes kube-proxy omits & logs & emits event for unmatched IP versions configuration (IPv6 address in IPv4 mode or IPv4 address in IPv6 mode). 

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #57219

**Special notes for your reviewer**:

**Release note**:

```release-note
Fix the issue in kube-proxy iptables/ipvs mode to properly handle incorrect IP version.
```
This commit is contained in:
Kubernetes Submit Queue 2018-02-28 00:00:29 -08:00 committed by GitHub
commit f45f4a4ec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1563 additions and 1146 deletions

View File

@ -19,6 +19,7 @@ go_library(
"//pkg/proxy/iptables:go_default_library", "//pkg/proxy/iptables:go_default_library",
"//pkg/util/conntrack:go_default_library", "//pkg/util/conntrack:go_default_library",
"//pkg/util/iptables:go_default_library", "//pkg/util/iptables:go_default_library",
"//pkg/util/net:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",

View File

@ -31,6 +31,7 @@ import (
iptablesproxy "k8s.io/kubernetes/pkg/proxy/iptables" iptablesproxy "k8s.io/kubernetes/pkg/proxy/iptables"
"k8s.io/kubernetes/pkg/util/conntrack" "k8s.io/kubernetes/pkg/util/conntrack"
utiliptables "k8s.io/kubernetes/pkg/util/iptables" utiliptables "k8s.io/kubernetes/pkg/util/iptables"
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/utils/exec" "k8s.io/utils/exec"
) )
@ -165,7 +166,7 @@ func (hm *hostportManager) Add(id string, podPortMapping *PodPortMapping, natInt
// clean up opened host port if encounter any error // clean up opened host port if encounter any error
return utilerrors.NewAggregate([]error{err, hm.closeHostports(hostportMappings)}) return utilerrors.NewAggregate([]error{err, hm.closeHostports(hostportMappings)})
} }
isIpv6 := conntrack.IsIPv6(podPortMapping.IP) isIpv6 := utilnet.IsIPv6(podPortMapping.IP)
// Remove conntrack entries just after adding the new iptables rules. If the conntrack entry is removed along with // Remove conntrack entries just after adding the new iptables rules. If the conntrack entry is removed along with
// the IP tables rule, it can be the case that the packets received by the node after iptables rule removal will // the IP tables rule, it can be the case that the packets received by the node after iptables rule removal will

View File

@ -16,11 +16,15 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/pkg/proxy", importpath = "k8s.io/kubernetes/pkg/proxy",
deps = [ deps = [
"//pkg/api/service:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//pkg/proxy/util:go_default_library", "//pkg/proxy/util:go_default_library",
"//pkg/util/net:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
], ],
) )
@ -57,7 +61,6 @@ go_test(
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/api/service:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library", "//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -17,16 +17,67 @@ limitations under the License.
package proxy package proxy
import ( import (
"net"
"reflect" "reflect"
"strconv"
"sync" "sync"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
utilnet "k8s.io/kubernetes/pkg/util/net"
) )
// BaseEndpointInfo contains base information that defines an endpoint.
// This could be used directly by proxier while processing endpoints,
// or can be used for constructing a more specific EndpointInfo struct
// defined by the proxier if needed.
type BaseEndpointInfo struct {
Endpoint string // TODO: should be an endpointString type
// IsLocal indicates whether the endpoint is running in same host as kube-proxy.
IsLocal bool
}
var _ Endpoint = &BaseEndpointInfo{}
// String is part of proxy.Endpoint interface.
func (info *BaseEndpointInfo) String() string {
return info.Endpoint
}
// GetIsLocal is part of proxy.Endpoint interface.
func (info *BaseEndpointInfo) GetIsLocal() bool {
return info.IsLocal
}
// IP returns just the IP part of the endpoint, it's a part of proxy.Endpoint interface.
func (info *BaseEndpointInfo) IP() string {
return utilproxy.IPPart(info.Endpoint)
}
// Port returns just the Port part of the endpoint.
func (info *BaseEndpointInfo) Port() (int, error) {
return utilproxy.PortPart(info.Endpoint)
}
// Equal is part of proxy.Endpoint interface.
func (info *BaseEndpointInfo) Equal(other Endpoint) bool {
return info.String() == other.String() && info.GetIsLocal() == other.GetIsLocal()
}
func newBaseEndpointInfo(IP string, port int, isLocal bool) *BaseEndpointInfo {
return &BaseEndpointInfo{
Endpoint: net.JoinHostPort(IP, strconv.Itoa(port)),
IsLocal: isLocal,
}
}
type makeEndpointFunc func(info *BaseEndpointInfo) Endpoint
// EndpointChangeTracker carries state about uncommitted changes to an arbitrary number of // EndpointChangeTracker carries state about uncommitted changes to an arbitrary number of
// Endpoints, keyed by their namespace and name. // Endpoints, keyed by their namespace and name.
type EndpointChangeTracker struct { type EndpointChangeTracker struct {
@ -36,13 +87,21 @@ type EndpointChangeTracker struct {
hostname string hostname string
// items maps a service to is endpointsChange. // items maps a service to is endpointsChange.
items map[types.NamespacedName]*endpointsChange items map[types.NamespacedName]*endpointsChange
// makeEndpointInfo allows proxier to inject customized information when processing endpoint.
makeEndpointInfo makeEndpointFunc
// isIPv6Mode indicates if change tracker is under IPv6/IPv4 mode. Nil means not applicable.
isIPv6Mode *bool
recorder record.EventRecorder
} }
// NewEndpointChangeTracker initializes an EndpointsChangeMap // NewEndpointChangeTracker initializes an EndpointsChangeMap
func NewEndpointChangeTracker(hostname string) *EndpointChangeTracker { func NewEndpointChangeTracker(hostname string, makeEndpointInfo makeEndpointFunc, isIPv6Mode *bool, recorder record.EventRecorder) *EndpointChangeTracker {
return &EndpointChangeTracker{ return &EndpointChangeTracker{
hostname: hostname, hostname: hostname,
items: make(map[types.NamespacedName]*endpointsChange), items: make(map[types.NamespacedName]*endpointsChange),
makeEndpointInfo: makeEndpointInfo,
isIPv6Mode: isIPv6Mode,
recorder: recorder,
} }
} }
@ -54,7 +113,7 @@ func NewEndpointChangeTracker(hostname string) *EndpointChangeTracker {
// - pass <oldEndpoints, endpoints> as the <previous, current> pair. // - pass <oldEndpoints, endpoints> as the <previous, current> pair.
// Delete item // Delete item
// - pass <endpoints, nil> as the <previous, current> pair. // - pass <endpoints, nil> as the <previous, current> pair.
func (ect *EndpointChangeTracker) Update(previous, current *api.Endpoints, makeEndpoints func(IP string, port int, isLocal bool) Endpoint) bool { func (ect *EndpointChangeTracker) Update(previous, current *api.Endpoints) bool {
endpoints := current endpoints := current
if endpoints == nil { if endpoints == nil {
endpoints = previous endpoints = previous
@ -71,10 +130,10 @@ func (ect *EndpointChangeTracker) Update(previous, current *api.Endpoints, makeE
change, exists := ect.items[namespacedName] change, exists := ect.items[namespacedName]
if !exists { if !exists {
change = &endpointsChange{} change = &endpointsChange{}
change.previous = endpointsToEndpointsMap(previous, ect.hostname, makeEndpoints) change.previous = ect.endpointsToEndpointsMap(previous)
ect.items[namespacedName] = change ect.items[namespacedName] = change
} }
change.current = endpointsToEndpointsMap(current, ect.hostname, makeEndpoints) change.current = ect.endpointsToEndpointsMap(current)
// if change.previous equal to change.current, it means no change // if change.previous equal to change.current, it means no change
if reflect.DeepEqual(change.previous, change.current) { if reflect.DeepEqual(change.previous, change.current) {
delete(ect.items, namespacedName) delete(ect.items, namespacedName)
@ -118,14 +177,14 @@ func UpdateEndpointsMap(endpointsMap EndpointsMap, changes *EndpointChangeTracke
return result return result
} }
// EndpointsMap maps a service to one of its endpoint. // EndpointsMap maps a service name to a list of all its Endpoints.
type EndpointsMap map[ServicePortName][]Endpoint type EndpointsMap map[ServicePortName][]Endpoint
// endpointsToEndpointsMap translates single Endpoints object to EndpointsMap. // endpointsToEndpointsMap translates single Endpoints object to EndpointsMap.
// This function is used for incremental updated of endpointsMap. // This function is used for incremental updated of endpointsMap.
// //
// NOTE: endpoints object should NOT be modified. // NOTE: endpoints object should NOT be modified.
func endpointsToEndpointsMap(endpoints *api.Endpoints, hostname string, makeEndpoints func(IP string, port int, isLocal bool) Endpoint) EndpointsMap { func (ect *EndpointChangeTracker) endpointsToEndpointsMap(endpoints *api.Endpoints) EndpointsMap {
if endpoints == nil { if endpoints == nil {
return nil return nil
} }
@ -151,9 +210,21 @@ func endpointsToEndpointsMap(endpoints *api.Endpoints, hostname string, makeEndp
glog.Warningf("ignoring invalid endpoint port %s with empty host", port.Name) glog.Warningf("ignoring invalid endpoint port %s with empty host", port.Name)
continue continue
} }
isLocal := addr.NodeName != nil && *addr.NodeName == hostname // Filter out the incorrect IP version case.
epInfo := makeEndpoints(addr.IP, int(port.Port), isLocal) // Any endpoint port that contains incorrect IP version will be ignored.
endpointsMap[svcPortName] = append(endpointsMap[svcPortName], epInfo) if ect.isIPv6Mode != nil && utilnet.IsIPv6String(addr.IP) != *ect.isIPv6Mode {
// Emit event on the corresponding service which had a different
// IP version than the endpoint.
utilproxy.LogAndEmitIncorrectIPVersionEvent(ect.recorder, "endpoints", addr.IP, endpoints.Name, endpoints.Namespace, "")
continue
}
isLocal := addr.NodeName != nil && *addr.NodeName == ect.hostname
baseEndpointInfo := newBaseEndpointInfo(addr.IP, int(port.Port), isLocal)
if ect.makeEndpointInfo != nil {
endpointsMap[svcPortName] = append(endpointsMap[svcPortName], ect.makeEndpointInfo(baseEndpointInfo))
} else {
endpointsMap[svcPortName] = append(endpointsMap[svcPortName], baseEndpointInfo)
}
} }
if glog.V(3) { if glog.V(3) {
newEPList := []string{} newEPList := []string{}
@ -203,7 +274,7 @@ func GetLocalEndpointIPs(endpointsMap EndpointsMap) map[types.NamespacedName]set
localIPs := make(map[types.NamespacedName]sets.String) localIPs := make(map[types.NamespacedName]sets.String)
for svcPortName, epList := range endpointsMap { for svcPortName, epList := range endpointsMap {
for _, ep := range epList { for _, ep := range epList {
if ep.IsLocal() { if ep.GetIsLocal() {
nsn := svcPortName.NamespacedName nsn := svcPortName.NamespacedName
if localIPs[nsn] == nil { if localIPs[nsn] == nil {
localIPs[nsn] = sets.NewString() localIPs[nsn] = sets.NewString()

View File

@ -17,9 +17,7 @@ limitations under the License.
package proxy package proxy
import ( import (
"net"
"reflect" "reflect"
"strconv"
"testing" "testing"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
@ -30,48 +28,16 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
type fakeEndpointsInfo struct {
endpoint string
isLocal bool
}
func newFakeEndpointsInfo(IP string, port int, isLocal bool) Endpoint {
return &fakeEndpointsInfo{
endpoint: net.JoinHostPort(IP, strconv.Itoa(port)),
isLocal: isLocal,
}
}
func (f *fakeEndpointsInfo) String() string {
return f.endpoint
}
func (f *fakeEndpointsInfo) IsLocal() bool {
return f.isLocal
}
func (f *fakeEndpointsInfo) IP() string {
// Must be IP:port
host, _, _ := net.SplitHostPort(f.endpoint)
return host
}
func (f *fakeEndpointsInfo) Equal(other Endpoint) bool {
return f.String() == other.String() &&
f.IsLocal() == other.IsLocal() &&
f.IP() == other.IP()
}
func (proxier *FakeProxier) addEndpoints(endpoints *api.Endpoints) { func (proxier *FakeProxier) addEndpoints(endpoints *api.Endpoints) {
proxier.endpointsChanges.Update(nil, endpoints, newFakeEndpointsInfo) proxier.endpointsChanges.Update(nil, endpoints)
} }
func (proxier *FakeProxier) updateEndpoints(oldEndpoints, endpoints *api.Endpoints) { func (proxier *FakeProxier) updateEndpoints(oldEndpoints, endpoints *api.Endpoints) {
proxier.endpointsChanges.Update(oldEndpoints, endpoints, newFakeEndpointsInfo) proxier.endpointsChanges.Update(oldEndpoints, endpoints)
} }
func (proxier *FakeProxier) deleteEndpoints(endpoints *api.Endpoints) { func (proxier *FakeProxier) deleteEndpoints(endpoints *api.Endpoints) {
proxier.endpointsChanges.Update(endpoints, nil, newFakeEndpointsInfo) proxier.endpointsChanges.Update(endpoints, nil)
} }
func TestGetLocalEndpointIPs(t *testing.T) { func TestGetLocalEndpointIPs(t *testing.T) {
@ -86,7 +52,7 @@ func TestGetLocalEndpointIPs(t *testing.T) {
// Case[1]: unnamed port // Case[1]: unnamed port
endpointsMap: EndpointsMap{ endpointsMap: EndpointsMap{
makeServicePortName("ns1", "ep1", ""): []Endpoint{ makeServicePortName("ns1", "ep1", ""): []Endpoint{
&fakeEndpointsInfo{endpoint: "1.1.1.1:11", isLocal: false}, &BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expected: map[types.NamespacedName]sets.String{}, expected: map[types.NamespacedName]sets.String{},
@ -94,7 +60,7 @@ func TestGetLocalEndpointIPs(t *testing.T) {
// Case[2]: unnamed port local // Case[2]: unnamed port local
endpointsMap: EndpointsMap{ endpointsMap: EndpointsMap{
makeServicePortName("ns1", "ep1", ""): []Endpoint{ makeServicePortName("ns1", "ep1", ""): []Endpoint{
&fakeEndpointsInfo{endpoint: "1.1.1.1:11", isLocal: true}, &BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expected: map[types.NamespacedName]sets.String{ expected: map[types.NamespacedName]sets.String{
@ -104,12 +70,12 @@ func TestGetLocalEndpointIPs(t *testing.T) {
// Case[3]: named local and non-local ports for the same IP. // Case[3]: named local and non-local ports for the same IP.
endpointsMap: EndpointsMap{ endpointsMap: EndpointsMap{
makeServicePortName("ns1", "ep1", "p11"): []Endpoint{ makeServicePortName("ns1", "ep1", "p11"): []Endpoint{
&fakeEndpointsInfo{endpoint: "1.1.1.1:11", isLocal: false}, &BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false},
&fakeEndpointsInfo{endpoint: "1.1.1.2:11", isLocal: true}, &BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): []Endpoint{ makeServicePortName("ns1", "ep1", "p12"): []Endpoint{
&fakeEndpointsInfo{endpoint: "1.1.1.1:12", isLocal: false}, &BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false},
&fakeEndpointsInfo{endpoint: "1.1.1.2:12", isLocal: true}, &BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expected: map[types.NamespacedName]sets.String{ expected: map[types.NamespacedName]sets.String{
@ -119,21 +85,21 @@ func TestGetLocalEndpointIPs(t *testing.T) {
// Case[4]: named local and non-local ports for different IPs. // Case[4]: named local and non-local ports for different IPs.
endpointsMap: EndpointsMap{ endpointsMap: EndpointsMap{
makeServicePortName("ns1", "ep1", "p11"): []Endpoint{ makeServicePortName("ns1", "ep1", "p11"): []Endpoint{
&fakeEndpointsInfo{endpoint: "1.1.1.1:11", isLocal: false}, &BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns2", "ep2", "p22"): []Endpoint{ makeServicePortName("ns2", "ep2", "p22"): []Endpoint{
&fakeEndpointsInfo{endpoint: "2.2.2.2:22", isLocal: true}, &BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true},
&fakeEndpointsInfo{endpoint: "2.2.2.22:22", isLocal: true}, &BaseEndpointInfo{Endpoint: "2.2.2.22:22", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p23"): []Endpoint{ makeServicePortName("ns2", "ep2", "p23"): []Endpoint{
&fakeEndpointsInfo{endpoint: "2.2.2.3:23", isLocal: true}, &BaseEndpointInfo{Endpoint: "2.2.2.3:23", IsLocal: true},
}, },
makeServicePortName("ns4", "ep4", "p44"): []Endpoint{ makeServicePortName("ns4", "ep4", "p44"): []Endpoint{
&fakeEndpointsInfo{endpoint: "4.4.4.4:44", isLocal: true}, &BaseEndpointInfo{Endpoint: "4.4.4.4:44", IsLocal: true},
&fakeEndpointsInfo{endpoint: "4.4.4.5:44", isLocal: false}, &BaseEndpointInfo{Endpoint: "4.4.4.5:44", IsLocal: false},
}, },
makeServicePortName("ns4", "ep4", "p45"): []Endpoint{ makeServicePortName("ns4", "ep4", "p45"): []Endpoint{
&fakeEndpointsInfo{endpoint: "4.4.4.6:45", isLocal: true}, &BaseEndpointInfo{Endpoint: "4.4.4.6:45", IsLocal: true},
}, },
}, },
expected: map[types.NamespacedName]sets.String{ expected: map[types.NamespacedName]sets.String{
@ -164,184 +130,262 @@ func makeTestEndpoints(namespace, name string, eptFunc func(*api.Endpoints)) *ap
} }
// This is a coarse test, but it offers some modicum of confidence as the code is evolved. // This is a coarse test, but it offers some modicum of confidence as the code is evolved.
func Test_endpointsToEndpointsMap(t *testing.T) { func TestEndpointsToEndpointsMap(t *testing.T) {
testCases := []struct { epTracker := NewEndpointChangeTracker("test-hostname", nil, nil, nil)
newEndpoints *api.Endpoints
expected map[ServicePortName][]*fakeEndpointsInfo
}{{
// Case[0]: nothing
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {}),
expected: map[ServicePortName][]*fakeEndpointsInfo{},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false},
},
},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", "port"): {
{endpoint: "1.1.1.1:11", isLocal: false},
},
},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false},
},
},
}, {
// Case[4]: remove port
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {}),
expected: map[ServicePortName][]*fakeEndpointsInfo{},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{endpoint: "1.1.1.1:11", isLocal: false},
{endpoint: "2.2.2.2:11", isLocal: false},
},
makeServicePortName("ns1", "ep1", "p2"): {
{endpoint: "1.1.1.1:22", isLocal: false},
{endpoint: "2.2.2.2:22", isLocal: false},
},
},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{endpoint: "1.1.1.1:11", isLocal: false},
},
},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", "p2"): {
{endpoint: "1.1.1.1:11", isLocal: false},
},
},
}, {
// 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,
}},
},
}
}),
expected: map[ServicePortName][]*fakeEndpointsInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{endpoint: "1.1.1.1:22", isLocal: false},
},
},
}}
for tci, tc := range testCases { trueVal := true
falseVal := false
testCases := []struct {
desc string
newEndpoints *api.Endpoints
expected map[ServicePortName][]*BaseEndpointInfo
isIPv6Mode *bool
}{
{
desc: "nothing",
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {}),
expected: map[ServicePortName][]*BaseEndpointInfo{},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
},
},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "port"): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
},
},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
},
},
},
{
desc: "remove port",
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {}),
expected: map[ServicePortName][]*BaseEndpointInfo{},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
{Endpoint: "2.2.2.2:11", IsLocal: false},
},
makeServicePortName("ns1", "ep1", "p2"): {
{Endpoint: "1.1.1.1:22", IsLocal: false},
{Endpoint: "2.2.2.2:22", IsLocal: false},
},
},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
},
},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p2"): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
},
},
},
{
desc: "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,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{Endpoint: "1.1.1.1:22", IsLocal: false},
},
},
},
{
desc: "should omit IPv6 address in IPv4 mode",
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}, {
IP: "2001:db8:85a3:0:0:8a2e:370:7334",
}},
Ports: []api.EndpointPort{{
Name: "p1",
Port: 11,
}, {
Name: "p2",
Port: 22,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{Endpoint: "1.1.1.1:11", IsLocal: false},
},
makeServicePortName("ns1", "ep1", "p2"): {
{Endpoint: "1.1.1.1:22", IsLocal: false},
},
},
isIPv6Mode: &falseVal,
},
{
desc: "should omit IPv4 address in IPv6 mode",
newEndpoints: makeTestEndpoints("ns1", "ep1", func(ept *api.Endpoints) {
ept.Subsets = []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{{
IP: "1.1.1.1",
}, {
IP: "2001:db8:85a3:0:0:8a2e:370:7334",
}},
Ports: []api.EndpointPort{{
Name: "p1",
Port: 11,
}, {
Name: "p2",
Port: 22,
}},
},
}
}),
expected: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p1"): {
{Endpoint: "[2001:db8:85a3:0:0:8a2e:370:7334]:11", IsLocal: false},
},
makeServicePortName("ns1", "ep1", "p2"): {
{Endpoint: "[2001:db8:85a3:0:0:8a2e:370:7334]:22", IsLocal: false},
},
},
isIPv6Mode: &trueVal,
},
}
for _, tc := range testCases {
epTracker.isIPv6Mode = tc.isIPv6Mode
// outputs // outputs
newEndpoints := endpointsToEndpointsMap(tc.newEndpoints, "host", newFakeEndpointsInfo) newEndpoints := epTracker.endpointsToEndpointsMap(tc.newEndpoints)
if len(newEndpoints) != len(tc.expected) { if len(newEndpoints) != len(tc.expected) {
t.Errorf("[%d] expected %d new, got %d: %v", tci, len(tc.expected), len(newEndpoints), spew.Sdump(newEndpoints)) t.Errorf("[%s] expected %d new, got %d: %v", tc.desc, len(tc.expected), len(newEndpoints), spew.Sdump(newEndpoints))
} }
for x := range tc.expected { for x := range tc.expected {
if len(newEndpoints[x]) != len(tc.expected[x]) { if len(newEndpoints[x]) != len(tc.expected[x]) {
t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(tc.expected[x]), x, len(newEndpoints[x])) t.Errorf("[%s] expected %d endpoints for %v, got %d", tc.desc, len(tc.expected[x]), x, len(newEndpoints[x]))
} else { } else {
for i := range newEndpoints[x] { for i := range newEndpoints[x] {
ep := newEndpoints[x][i].(*fakeEndpointsInfo) ep := newEndpoints[x][i].(*BaseEndpointInfo)
if *ep != *(tc.expected[x][i]) { if *ep != *(tc.expected[x][i]) {
t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, tc.expected[x][i], *ep) t.Errorf("[%s] expected new[%v][%d] to be %v, got %v", tc.desc, x, i, tc.expected[x][i], *ep)
} }
} }
} }
@ -661,15 +705,15 @@ func TestUpdateEndpointsMap(t *testing.T) {
// or non-nil) and must be of equal length. // or non-nil) and must be of equal length.
previousEndpoints []*api.Endpoints previousEndpoints []*api.Endpoints
currentEndpoints []*api.Endpoints currentEndpoints []*api.Endpoints
oldEndpoints map[ServicePortName][]*fakeEndpointsInfo oldEndpoints map[ServicePortName][]*BaseEndpointInfo
expectedResult map[ServicePortName][]*fakeEndpointsInfo expectedResult map[ServicePortName][]*BaseEndpointInfo
expectedStaleEndpoints []ServiceEndpoint expectedStaleEndpoints []ServiceEndpoint
expectedStaleServiceNames map[ServicePortName]bool expectedStaleServiceNames map[ServicePortName]bool
expectedHealthchecks map[types.NamespacedName]int expectedHealthchecks map[types.NamespacedName]int
}{{ }{{
// Case[0]: nothing // Case[0]: nothing
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{}, oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{},
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{}, expectedResult: map[ServicePortName][]*BaseEndpointInfo{},
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
expectedStaleServiceNames: map[ServicePortName]bool{}, expectedStaleServiceNames: map[ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{}, expectedHealthchecks: map[types.NamespacedName]int{},
@ -681,14 +725,14 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPort), makeTestEndpoints("ns1", "ep1", unnamedPort),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -702,14 +746,14 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortLocal), makeTestEndpoints("ns1", "ep1", namedPortLocal),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -725,20 +769,20 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsets), makeTestEndpoints("ns1", "ep1", multipleSubsets),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -752,26 +796,26 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsMultiplePortsLocal), makeTestEndpoints("ns1", "ep1", multipleSubsetsMultiplePortsLocal),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: true}, {Endpoint: "1.1.1.1:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: true}, {Endpoint: "1.1.1.1:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -789,56 +833,56 @@ func TestUpdateEndpointsMap(t *testing.T) {
makeTestEndpoints("ns1", "ep1", multipleSubsetsIPsPorts1), makeTestEndpoints("ns1", "ep1", multipleSubsetsIPsPorts1),
makeTestEndpoints("ns2", "ep2", multipleSubsetsIPsPorts2), makeTestEndpoints("ns2", "ep2", multipleSubsetsIPsPorts2),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
{endpoint: "1.1.1.4:13", isLocal: true}, {Endpoint: "1.1.1.4:13", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p14"): { makeServicePortName("ns1", "ep1", "p14"): {
{endpoint: "1.1.1.3:14", isLocal: false}, {Endpoint: "1.1.1.3:14", IsLocal: false},
{endpoint: "1.1.1.4:14", isLocal: true}, {Endpoint: "1.1.1.4:14", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p21"): { makeServicePortName("ns2", "ep2", "p21"): {
{endpoint: "2.2.2.1:21", isLocal: false}, {Endpoint: "2.2.2.1:21", IsLocal: false},
{endpoint: "2.2.2.2:21", isLocal: true}, {Endpoint: "2.2.2.2:21", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.1:22", isLocal: false}, {Endpoint: "2.2.2.1:22", IsLocal: false},
{endpoint: "2.2.2.2:22", isLocal: true}, {Endpoint: "2.2.2.2:22", IsLocal: true},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
{endpoint: "1.1.1.4:13", isLocal: true}, {Endpoint: "1.1.1.4:13", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p14"): { makeServicePortName("ns1", "ep1", "p14"): {
{endpoint: "1.1.1.3:14", isLocal: false}, {Endpoint: "1.1.1.3:14", IsLocal: false},
{endpoint: "1.1.1.4:14", isLocal: true}, {Endpoint: "1.1.1.4:14", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p21"): { makeServicePortName("ns2", "ep2", "p21"): {
{endpoint: "2.2.2.1:21", isLocal: false}, {Endpoint: "2.2.2.1:21", IsLocal: false},
{endpoint: "2.2.2.2:21", isLocal: true}, {Endpoint: "2.2.2.2:21", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.1:22", isLocal: false}, {Endpoint: "2.2.2.1:22", IsLocal: false},
{endpoint: "2.2.2.2:22", isLocal: true}, {Endpoint: "2.2.2.2:22", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -855,10 +899,10 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPortLocal), makeTestEndpoints("ns1", "ep1", unnamedPortLocal),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{}, oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{},
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -876,12 +920,12 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
nil, nil,
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{}, expectedResult: map[ServicePortName][]*BaseEndpointInfo{},
expectedStaleEndpoints: []ServiceEndpoint{{ expectedStaleEndpoints: []ServiceEndpoint{{
Endpoint: "1.1.1.1:11", Endpoint: "1.1.1.1:11",
ServicePortName: makeServicePortName("ns1", "ep1", ""), ServicePortName: makeServicePortName("ns1", "ep1", ""),
@ -896,19 +940,19 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortsLocalNoLocal), makeTestEndpoints("ns1", "ep1", namedPortsLocalNoLocal),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -926,19 +970,19 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort), makeTestEndpoints("ns1", "ep1", namedPort),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{{ expectedStaleEndpoints: []ServiceEndpoint{{
@ -961,17 +1005,17 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsWithLocal), makeTestEndpoints("ns1", "ep1", multipleSubsetsWithLocal),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -989,17 +1033,17 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort), makeTestEndpoints("ns1", "ep1", namedPort),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{{ expectedStaleEndpoints: []ServiceEndpoint{{
@ -1016,14 +1060,14 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortRenamed), makeTestEndpoints("ns1", "ep1", namedPortRenamed),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11-2"): { makeServicePortName("ns1", "ep1", "p11-2"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{{ expectedStaleEndpoints: []ServiceEndpoint{{
@ -1042,14 +1086,14 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortRenumbered), makeTestEndpoints("ns1", "ep1", namedPortRenumbered),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:22", isLocal: false}, {Endpoint: "1.1.1.1:22", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{{ expectedStaleEndpoints: []ServiceEndpoint{{
@ -1072,41 +1116,41 @@ func TestUpdateEndpointsMap(t *testing.T) {
makeTestEndpoints("ns3", "ep3", complexAfter3), makeTestEndpoints("ns3", "ep3", complexAfter3),
makeTestEndpoints("ns4", "ep4", complexAfter4), makeTestEndpoints("ns4", "ep4", complexAfter4),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{ oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.2:22", isLocal: true}, {Endpoint: "2.2.2.2:22", IsLocal: true},
{endpoint: "2.2.2.22:22", isLocal: true}, {Endpoint: "2.2.2.22:22", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p23"): { makeServicePortName("ns2", "ep2", "p23"): {
{endpoint: "2.2.2.3:23", isLocal: true}, {Endpoint: "2.2.2.3:23", IsLocal: true},
}, },
makeServicePortName("ns4", "ep4", "p44"): { makeServicePortName("ns4", "ep4", "p44"): {
{endpoint: "4.4.4.4:44", isLocal: true}, {Endpoint: "4.4.4.4:44", IsLocal: true},
{endpoint: "4.4.4.5:44", isLocal: true}, {Endpoint: "4.4.4.5:44", IsLocal: true},
}, },
makeServicePortName("ns4", "ep4", "p45"): { makeServicePortName("ns4", "ep4", "p45"): {
{endpoint: "4.4.4.6:45", isLocal: true}, {Endpoint: "4.4.4.6:45", IsLocal: true},
}, },
}, },
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.11:11", isLocal: false}, {Endpoint: "1.1.1.11:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p122"): { makeServicePortName("ns1", "ep1", "p122"): {
{endpoint: "1.1.1.2:122", isLocal: false}, {Endpoint: "1.1.1.2:122", IsLocal: false},
}, },
makeServicePortName("ns3", "ep3", "p33"): { makeServicePortName("ns3", "ep3", "p33"): {
{endpoint: "3.3.3.3:33", isLocal: false}, {Endpoint: "3.3.3.3:33", IsLocal: false},
}, },
makeServicePortName("ns4", "ep4", "p44"): { makeServicePortName("ns4", "ep4", "p44"): {
{endpoint: "4.4.4.4:44", isLocal: true}, {Endpoint: "4.4.4.4:44", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{{ expectedStaleEndpoints: []ServiceEndpoint{{
@ -1141,10 +1185,10 @@ func TestUpdateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPort), makeTestEndpoints("ns1", "ep1", unnamedPort),
}, },
oldEndpoints: map[ServicePortName][]*fakeEndpointsInfo{}, oldEndpoints: map[ServicePortName][]*BaseEndpointInfo{},
expectedResult: map[ServicePortName][]*fakeEndpointsInfo{ expectedResult: map[ServicePortName][]*BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []ServiceEndpoint{}, expectedStaleEndpoints: []ServiceEndpoint{},
@ -1224,7 +1268,7 @@ func TestUpdateEndpointsMap(t *testing.T) {
} }
} }
func compareEndpointsMaps(t *testing.T, tci int, newMap EndpointsMap, expected map[ServicePortName][]*fakeEndpointsInfo) { func compareEndpointsMaps(t *testing.T, tci int, newMap EndpointsMap, expected map[ServicePortName][]*BaseEndpointInfo) {
if len(newMap) != len(expected) { if len(newMap) != len(expected) {
t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap) t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap)
} }
@ -1233,7 +1277,7 @@ func compareEndpointsMaps(t *testing.T, tci int, newMap EndpointsMap, expected m
t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x])) t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x]))
} else { } else {
for i := range expected[x] { for i := range expected[x] {
newEp, ok := newMap[x][i].(*fakeEndpointsInfo) newEp, ok := newMap[x][i].(*BaseEndpointInfo)
if !ok { if !ok {
t.Errorf("Failed to cast endpointsInfo") t.Errorf("Failed to cast endpointsInfo")
continue continue

View File

@ -13,9 +13,7 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/pkg/proxy/iptables", importpath = "k8s.io/kubernetes/pkg/proxy/iptables",
deps = [ deps = [
"//pkg/api/service:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//pkg/proxy:go_default_library", "//pkg/proxy:go_default_library",
"//pkg/proxy/healthcheck:go_default_library", "//pkg/proxy/healthcheck:go_default_library",
"//pkg/proxy/metrics:go_default_library", "//pkg/proxy/metrics:go_default_library",
@ -23,6 +21,7 @@ go_library(
"//pkg/util/async:go_default_library", "//pkg/util/async:go_default_library",
"//pkg/util/conntrack:go_default_library", "//pkg/util/conntrack:go_default_library",
"//pkg/util/iptables:go_default_library", "//pkg/util/iptables:go_default_library",
"//pkg/util/net:go_default_library",
"//pkg/util/sysctl:go_default_library", "//pkg/util/sysctl:go_default_library",
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",

View File

@ -38,9 +38,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
apiservice "k8s.io/kubernetes/pkg/api/service"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/proxy" "k8s.io/kubernetes/pkg/proxy"
"k8s.io/kubernetes/pkg/proxy/healthcheck" "k8s.io/kubernetes/pkg/proxy/healthcheck"
"k8s.io/kubernetes/pkg/proxy/metrics" "k8s.io/kubernetes/pkg/proxy/metrics"
@ -48,6 +46,7 @@ import (
"k8s.io/kubernetes/pkg/util/async" "k8s.io/kubernetes/pkg/util/async"
"k8s.io/kubernetes/pkg/util/conntrack" "k8s.io/kubernetes/pkg/util/conntrack"
utiliptables "k8s.io/kubernetes/pkg/util/iptables" utiliptables "k8s.io/kubernetes/pkg/util/iptables"
utilnet "k8s.io/kubernetes/pkg/util/net"
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl" utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
utilversion "k8s.io/kubernetes/pkg/util/version" utilversion "k8s.io/kubernetes/pkg/util/version"
utilexec "k8s.io/utils/exec" utilexec "k8s.io/utils/exec"
@ -141,17 +140,7 @@ const sysctlBridgeCallIPTables = "net/bridge/bridge-nf-call-iptables"
// internal struct for string service information // internal struct for string service information
type serviceInfo struct { type serviceInfo struct {
clusterIP net.IP *proxy.BaseServiceInfo
port int
protocol api.Protocol
nodePort int
loadBalancerStatus api.LoadBalancerStatus
sessionAffinityType api.ServiceAffinity
stickyMaxAgeSeconds int
externalIPs []string
loadBalancerSourceRanges []string
onlyNodeLocalEndpoints bool
healthCheckNodePort int
// The following fields are computed and stored for performance reasons. // The following fields are computed and stored for performance reasons.
serviceNameString string serviceNameString string
servicePortChainName utiliptables.Chain servicePortChainName utiliptables.Chain
@ -160,47 +149,13 @@ type serviceInfo struct {
} }
// returns a new proxy.ServicePort which abstracts a serviceInfo // returns a new proxy.ServicePort which abstracts a serviceInfo
func newServiceInfo(port *api.ServicePort, service *api.Service) proxy.ServicePort { func newServiceInfo(port *api.ServicePort, service *api.Service, baseInfo *proxy.BaseServiceInfo) proxy.ServicePort {
onlyNodeLocalEndpoints := false info := &serviceInfo{BaseServiceInfo: baseInfo}
if apiservice.RequestsOnlyLocalTraffic(service) {
onlyNodeLocalEndpoints = true
}
var stickyMaxAgeSeconds int
if service.Spec.SessionAffinity == api.ServiceAffinityClientIP {
// Kube-apiserver side guarantees SessionAffinityConfig won't be nil when session affinity type is ClientIP
stickyMaxAgeSeconds = int(*service.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds)
}
info := &serviceInfo{
clusterIP: net.ParseIP(service.Spec.ClusterIP),
port: int(port.Port),
protocol: port.Protocol,
nodePort: int(port.NodePort),
// Deep-copy in case the service instance changes
loadBalancerStatus: *helper.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer),
sessionAffinityType: service.Spec.SessionAffinity,
stickyMaxAgeSeconds: stickyMaxAgeSeconds,
externalIPs: make([]string, len(service.Spec.ExternalIPs)),
loadBalancerSourceRanges: make([]string, len(service.Spec.LoadBalancerSourceRanges)),
onlyNodeLocalEndpoints: onlyNodeLocalEndpoints,
}
copy(info.loadBalancerSourceRanges, service.Spec.LoadBalancerSourceRanges)
copy(info.externalIPs, service.Spec.ExternalIPs)
svcName := types.NamespacedName{Namespace: service.Namespace, Name: service.Name}
svcPortName := proxy.ServicePortName{NamespacedName: svcName, Port: port.Name}
if apiservice.NeedsHealthCheck(service) {
p := service.Spec.HealthCheckNodePort
if p == 0 {
glog.Errorf("Service %q has no healthcheck nodeport", svcName.String())
} else {
info.healthCheckNodePort = int(p)
}
}
// Store the following for performance reasons. // Store the following for performance reasons.
protocol := strings.ToLower(string(info.protocol)) svcName := types.NamespacedName{Namespace: service.Namespace, Name: service.Name}
svcPortName := proxy.ServicePortName{NamespacedName: svcName, Port: port.Name}
protocol := strings.ToLower(string(info.Protocol))
info.serviceNameString = svcPortName.String() info.serviceNameString = svcPortName.String()
info.servicePortChainName = servicePortChainName(info.serviceNameString, protocol) info.servicePortChainName = servicePortChainName(info.serviceNameString, protocol)
info.serviceFirewallChainName = serviceFirewallChainName(info.serviceNameString, protocol) info.serviceFirewallChainName = serviceFirewallChainName(info.serviceNameString, protocol)
@ -209,37 +164,9 @@ func newServiceInfo(port *api.ServicePort, service *api.Service) proxy.ServicePo
return info return info
} }
// ClusterIP is part of proxy.ServicePort interface.
func (info *serviceInfo) ClusterIP() string {
return info.clusterIP.String()
}
// Port is part of proxy.ServicePort interface.
func (info *serviceInfo) Port() int {
return info.port
}
// Protocol is part of proxy.ServicePort interface.
func (info *serviceInfo) Protocol() api.Protocol {
return info.protocol
}
// String is part of proxy.ServicePort interface.
func (info *serviceInfo) String() string {
return fmt.Sprintf("%s:%d/%s", info.clusterIP, info.port, info.protocol)
}
// HealthCheckNodePort is part of proxy.ServicePort interface.
func (info *serviceInfo) HealthCheckNodePort() int {
return info.healthCheckNodePort
}
var _ proxy.ServicePort = &serviceInfo{}
// internal struct for endpoints information // internal struct for endpoints information
type endpointsInfo struct { type endpointsInfo struct {
endpoint string // TODO: should be an endpointString type *proxy.BaseEndpointInfo
isLocal bool
// The following fields we lazily compute and store here for performance // The following fields we lazily compute and store here for performance
// reasons. If the protocol is the same as you expect it to be, then the // reasons. If the protocol is the same as you expect it to be, then the
// chainName can be reused, otherwise it should be recomputed. // chainName can be reused, otherwise it should be recomputed.
@ -248,52 +175,32 @@ type endpointsInfo struct {
} }
// returns a new proxy.Endpoint which abstracts a endpointsInfo // returns a new proxy.Endpoint which abstracts a endpointsInfo
func newEndpointsInfo(IP string, port int, isLocal bool) proxy.Endpoint { func newEndpointInfo(baseInfo *proxy.BaseEndpointInfo) proxy.Endpoint {
return &endpointsInfo{ return &endpointsInfo{BaseEndpointInfo: baseInfo}
endpoint: net.JoinHostPort(IP, strconv.Itoa(port)),
isLocal: isLocal,
}
} }
// IsLocal is part of proxy.Endpoint interface. // Equal overrides the Equal() function imlemented by proxy.BaseEndpointInfo.
func (e *endpointsInfo) IsLocal() bool {
return e.isLocal
}
// IP is part of proxy.Endpoint interface.
func (e *endpointsInfo) IP() string {
return utilproxy.IPPart(e.endpoint)
}
// Equal is part of proxy.Endpoint interface.
func (e *endpointsInfo) Equal(other proxy.Endpoint) bool { func (e *endpointsInfo) Equal(other proxy.Endpoint) bool {
o, ok := other.(*endpointsInfo) o, ok := other.(*endpointsInfo)
if !ok { if !ok {
glog.Errorf("Failed to cast endpointsInfo") glog.Errorf("Failed to cast endpointsInfo")
return false return false
} }
return e.endpoint == o.endpoint && return e.Endpoint == o.Endpoint &&
e.isLocal == o.isLocal && e.IsLocal == o.IsLocal &&
e.protocol == o.protocol && e.protocol == o.protocol &&
e.chainName == o.chainName e.chainName == o.chainName
} }
// String is part of proxy.Endpoint interface.
func (e *endpointsInfo) String() string {
return e.endpoint
}
// Returns the endpoint chain name for a given endpointsInfo. // Returns the endpoint chain name for a given endpointsInfo.
func (e *endpointsInfo) endpointChain(svcNameString, protocol string) utiliptables.Chain { func (e *endpointsInfo) endpointChain(svcNameString, protocol string) utiliptables.Chain {
if e.protocol != protocol { if e.protocol != protocol {
e.protocol = protocol e.protocol = protocol
e.chainName = servicePortEndpointChainName(svcNameString, protocol, e.endpoint) e.chainName = servicePortEndpointChainName(svcNameString, protocol, e.Endpoint)
} }
return e.chainName return e.chainName
} }
var _ proxy.Endpoint = &endpointsInfo{}
// Proxier is an iptables based proxy for connections between a localhost:lport // Proxier is an iptables based proxy for connections between a localhost:lport
// and services that provide the actual backends. // and services that provide the actual backends.
type Proxier struct { type Proxier struct {
@ -402,16 +309,19 @@ func NewProxier(ipt utiliptables.Interface,
if len(clusterCIDR) == 0 { if len(clusterCIDR) == 0 {
glog.Warningf("clusterCIDR not specified, unable to distinguish between internal and external traffic") glog.Warningf("clusterCIDR not specified, unable to distinguish between internal and external traffic")
} else if utilnet.IsIPv6CIDR(clusterCIDR) != ipt.IsIpv6() {
return nil, fmt.Errorf("clusterCIDR %s has incorrect IP version: expect isIPv6=%t", clusterCIDR, ipt.IsIpv6())
} }
healthChecker := healthcheck.NewServer(hostname, recorder, nil, nil) // use default implementations of deps healthChecker := healthcheck.NewServer(hostname, recorder, nil, nil) // use default implementations of deps
isIPv6 := ipt.IsIpv6()
proxier := &Proxier{ proxier := &Proxier{
portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable), portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable),
serviceMap: make(proxy.ServiceMap), serviceMap: make(proxy.ServiceMap),
serviceChanges: proxy.NewServiceChangeTracker(), serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, &isIPv6, recorder),
endpointsMap: make(proxy.EndpointsMap), endpointsMap: make(proxy.EndpointsMap),
endpointsChanges: proxy.NewEndpointChangeTracker(hostname), endpointsChanges: proxy.NewEndpointChangeTracker(hostname, newEndpointInfo, &isIPv6, recorder),
iptables: ipt, iptables: ipt,
masqueradeAll: masqueradeAll, masqueradeAll: masqueradeAll,
masqueradeMark: masqueradeMark, masqueradeMark: masqueradeMark,
@ -592,21 +502,18 @@ func (proxier *Proxier) isInitialized() bool {
} }
func (proxier *Proxier) OnServiceAdd(service *api.Service) { func (proxier *Proxier) OnServiceAdd(service *api.Service) {
if proxier.serviceChanges.Update(nil, service, newServiceInfo) && proxier.isInitialized() { proxier.OnServiceUpdate(nil, service)
proxier.syncRunner.Run()
}
} }
func (proxier *Proxier) OnServiceUpdate(oldService, service *api.Service) { func (proxier *Proxier) OnServiceUpdate(oldService, service *api.Service) {
if proxier.serviceChanges.Update(oldService, service, newServiceInfo) && proxier.isInitialized() { if proxier.serviceChanges.Update(oldService, service) && proxier.isInitialized() {
proxier.syncRunner.Run() proxier.syncRunner.Run()
} }
} }
func (proxier *Proxier) OnServiceDelete(service *api.Service) { func (proxier *Proxier) OnServiceDelete(service *api.Service) {
if proxier.serviceChanges.Update(service, nil, newServiceInfo) && proxier.isInitialized() { proxier.OnServiceUpdate(service, nil)
proxier.syncRunner.Run()
}
} }
func (proxier *Proxier) OnServiceSynced() { func (proxier *Proxier) OnServiceSynced() {
@ -620,21 +527,17 @@ func (proxier *Proxier) OnServiceSynced() {
} }
func (proxier *Proxier) OnEndpointsAdd(endpoints *api.Endpoints) { func (proxier *Proxier) OnEndpointsAdd(endpoints *api.Endpoints) {
if proxier.endpointsChanges.Update(nil, endpoints, newEndpointsInfo) && proxier.isInitialized() { proxier.OnEndpointsUpdate(nil, endpoints)
proxier.syncRunner.Run()
}
} }
func (proxier *Proxier) OnEndpointsUpdate(oldEndpoints, endpoints *api.Endpoints) { func (proxier *Proxier) OnEndpointsUpdate(oldEndpoints, endpoints *api.Endpoints) {
if proxier.endpointsChanges.Update(oldEndpoints, endpoints, newEndpointsInfo) && proxier.isInitialized() { if proxier.endpointsChanges.Update(oldEndpoints, endpoints) && proxier.isInitialized() {
proxier.syncRunner.Run() proxier.syncRunner.Run()
} }
} }
func (proxier *Proxier) OnEndpointsDelete(endpoints *api.Endpoints) { func (proxier *Proxier) OnEndpointsDelete(endpoints *api.Endpoints) {
if proxier.endpointsChanges.Update(endpoints, nil, newEndpointsInfo) && proxier.isInitialized() { proxier.OnEndpointsUpdate(endpoints, nil)
proxier.syncRunner.Run()
}
} }
func (proxier *Proxier) OnEndpointsSynced() { func (proxier *Proxier) OnEndpointsSynced() {
@ -693,9 +596,9 @@ func servicePortEndpointChainName(servicePortName string, protocol string, endpo
// TODO: move it to util // TODO: move it to util
func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) { func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) {
for _, epSvcPair := range connectionMap { for _, epSvcPair := range connectionMap {
if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && svcInfo.Protocol() == api.ProtocolUDP { if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && svcInfo.GetProtocol() == api.ProtocolUDP {
endpointIP := utilproxy.IPPart(epSvcPair.Endpoint) endpointIP := utilproxy.IPPart(epSvcPair.Endpoint)
err := conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIP(), endpointIP, v1.ProtocolUDP) err := conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIPString(), endpointIP, v1.ProtocolUDP)
if err != nil { if err != nil {
glog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err) glog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err)
} }
@ -730,9 +633,9 @@ func (proxier *Proxier) syncProxyRules() {
staleServices := serviceUpdateResult.UDPStaleClusterIP staleServices := serviceUpdateResult.UDPStaleClusterIP
// merge stale services gathered from updateEndpointsMap // merge stale services gathered from updateEndpointsMap
for _, svcPortName := range endpointUpdateResult.StaleServiceNames { for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && svcInfo.Protocol() == api.ProtocolUDP { if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && svcInfo.GetProtocol() == api.ProtocolUDP {
glog.V(2).Infof("Stale udp service %v -> %s", svcPortName, svcInfo.ClusterIP()) glog.V(2).Infof("Stale udp service %v -> %s", svcPortName, svcInfo.ClusterIPString())
staleServices.Insert(svcInfo.ClusterIP()) staleServices.Insert(svcInfo.ClusterIPString())
} }
} }
@ -851,8 +754,8 @@ func (proxier *Proxier) syncProxyRules() {
glog.Errorf("Failed to cast serviceInfo %q", svcName.String()) glog.Errorf("Failed to cast serviceInfo %q", svcName.String())
continue continue
} }
isIPv6 := conntrack.IsIPv6(svcInfo.clusterIP) isIPv6 := utilnet.IsIPv6(svcInfo.ClusterIP)
protocol := strings.ToLower(string(svcInfo.protocol)) protocol := strings.ToLower(string(svcInfo.Protocol))
svcNameString := svcInfo.serviceNameString svcNameString := svcInfo.serviceNameString
hasEndpoints := len(proxier.endpointsMap[svcName]) > 0 hasEndpoints := len(proxier.endpointsMap[svcName]) > 0
@ -868,7 +771,7 @@ func (proxier *Proxier) syncProxyRules() {
} }
svcXlbChain := svcInfo.serviceLBChainName svcXlbChain := svcInfo.serviceLBChainName
if svcInfo.onlyNodeLocalEndpoints { if svcInfo.OnlyNodeLocalEndpoints {
// Only for services request OnlyLocal traffic // Only for services request OnlyLocal traffic
// create the per-service LB chain, retaining counters if possible. // create the per-service LB chain, retaining counters if possible.
if lbChain, ok := existingNATChains[svcXlbChain]; ok { if lbChain, ok := existingNATChains[svcXlbChain]; ok {
@ -885,8 +788,8 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain), "-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s cluster IP"`, svcNameString), "-m", "comment", "--comment", fmt.Sprintf(`"%s cluster IP"`, svcNameString),
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"-d", utilproxy.ToCIDR(svcInfo.clusterIP), "-d", utilproxy.ToCIDR(svcInfo.ClusterIP),
"--dport", strconv.Itoa(svcInfo.port), "--dport", strconv.Itoa(svcInfo.Port),
) )
if proxier.masqueradeAll { if proxier.masqueradeAll {
writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...)
@ -904,14 +807,14 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeServicesChain), "-A", string(kubeServicesChain),
"-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString), "-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString),
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"-d", utilproxy.ToCIDR(svcInfo.clusterIP), "-d", utilproxy.ToCIDR(svcInfo.ClusterIP),
"--dport", strconv.Itoa(svcInfo.port), "--dport", strconv.Itoa(svcInfo.Port),
"-j", "REJECT", "-j", "REJECT",
) )
} }
// Capture externalIPs. // Capture externalIPs.
for _, externalIP := range svcInfo.externalIPs { for _, externalIP := range svcInfo.ExternalIPs {
// If the "external" IP happens to be an IP that is local to this // If the "external" IP happens to be an IP that is local to this
// machine, hold the local port open so no other process can open it // machine, hold the local port open so no other process can open it
// (because the socket might open but it would never work). // (because the socket might open but it would never work).
@ -921,7 +824,7 @@ func (proxier *Proxier) syncProxyRules() {
lp := utilproxy.LocalPort{ lp := utilproxy.LocalPort{
Description: "externalIP for " + svcNameString, Description: "externalIP for " + svcNameString,
IP: externalIP, IP: externalIP,
Port: svcInfo.Port(), Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
} }
if proxier.portsMap[lp] != nil { if proxier.portsMap[lp] != nil {
@ -952,7 +855,7 @@ func (proxier *Proxier) syncProxyRules() {
"-m", "comment", "--comment", fmt.Sprintf(`"%s external IP"`, svcNameString), "-m", "comment", "--comment", fmt.Sprintf(`"%s external IP"`, svcNameString),
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"-d", utilproxy.ToCIDR(net.ParseIP(externalIP)), "-d", utilproxy.ToCIDR(net.ParseIP(externalIP)),
"--dport", strconv.Itoa(svcInfo.port), "--dport", strconv.Itoa(svcInfo.Port),
) )
// We have to SNAT packets to external IPs. // We have to SNAT packets to external IPs.
writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...)
@ -975,7 +878,7 @@ func (proxier *Proxier) syncProxyRules() {
"-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString), "-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString),
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"-d", utilproxy.ToCIDR(net.ParseIP(externalIP)), "-d", utilproxy.ToCIDR(net.ParseIP(externalIP)),
"--dport", strconv.Itoa(svcInfo.port), "--dport", strconv.Itoa(svcInfo.Port),
"-j", "REJECT", "-j", "REJECT",
) )
} }
@ -984,7 +887,7 @@ func (proxier *Proxier) syncProxyRules() {
// Capture load-balancer ingress. // Capture load-balancer ingress.
if hasEndpoints { if hasEndpoints {
fwChain := svcInfo.serviceFirewallChainName fwChain := svcInfo.serviceFirewallChainName
for _, ingress := range svcInfo.loadBalancerStatus.Ingress { for _, ingress := range svcInfo.LoadBalancerStatus.Ingress {
if ingress.IP != "" { if ingress.IP != "" {
// create service firewall chain // create service firewall chain
if chain, ok := existingNATChains[fwChain]; ok { if chain, ok := existingNATChains[fwChain]; ok {
@ -1002,7 +905,7 @@ func (proxier *Proxier) syncProxyRules() {
"-m", "comment", "--comment", fmt.Sprintf(`"%s loadbalancer IP"`, svcNameString), "-m", "comment", "--comment", fmt.Sprintf(`"%s loadbalancer IP"`, svcNameString),
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"-d", utilproxy.ToCIDR(net.ParseIP(ingress.IP)), "-d", utilproxy.ToCIDR(net.ParseIP(ingress.IP)),
"--dport", strconv.Itoa(svcInfo.port), "--dport", strconv.Itoa(svcInfo.Port),
) )
// jump to service firewall chain // jump to service firewall chain
writeLine(proxier.natRules, append(args, "-j", string(fwChain))...) writeLine(proxier.natRules, append(args, "-j", string(fwChain))...)
@ -1016,18 +919,18 @@ func (proxier *Proxier) syncProxyRules() {
chosenChain := svcXlbChain chosenChain := svcXlbChain
// If we are proxying globally, we need to masquerade in case we cross nodes. // If we are proxying globally, we need to masquerade in case we cross nodes.
// If we are proxying only locally, we can retain the source IP. // If we are proxying only locally, we can retain the source IP.
if !svcInfo.onlyNodeLocalEndpoints { if !svcInfo.OnlyNodeLocalEndpoints {
writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...)
chosenChain = svcChain chosenChain = svcChain
} }
if len(svcInfo.loadBalancerSourceRanges) == 0 { if len(svcInfo.LoadBalancerSourceRanges) == 0 {
// allow all sources, so jump directly to the KUBE-SVC or KUBE-XLB chain // allow all sources, so jump directly to the KUBE-SVC or KUBE-XLB chain
writeLine(proxier.natRules, append(args, "-j", string(chosenChain))...) writeLine(proxier.natRules, append(args, "-j", string(chosenChain))...)
} else { } else {
// firewall filter based on each source range // firewall filter based on each source range
allowFromNode := false allowFromNode := false
for _, src := range svcInfo.loadBalancerSourceRanges { for _, src := range svcInfo.LoadBalancerSourceRanges {
writeLine(proxier.natRules, append(args, "-s", src, "-j", string(chosenChain))...) writeLine(proxier.natRules, append(args, "-s", src, "-j", string(chosenChain))...)
// ignore error because it has been validated // ignore error because it has been validated
_, cidr, _ := net.ParseCIDR(src) _, cidr, _ := net.ParseCIDR(src)
@ -1054,13 +957,13 @@ func (proxier *Proxier) syncProxyRules() {
// Capture nodeports. If we had more than 2 rules it might be // Capture nodeports. If we had more than 2 rules it might be
// worthwhile to make a new per-service chain for nodeport rules, but // worthwhile to make a new per-service chain for nodeport rules, but
// with just 2 rules it ends up being a waste and a cognitive burden. // with just 2 rules it ends up being a waste and a cognitive burden.
if svcInfo.nodePort != 0 { if svcInfo.NodePort != 0 {
// Hold the local port open so no other process can open it // Hold the local port open so no other process can open it
// (because the socket might open but it would never work). // (because the socket might open but it would never work).
lp := utilproxy.LocalPort{ lp := utilproxy.LocalPort{
Description: "nodePort for " + svcNameString, Description: "nodePort for " + svcNameString,
IP: "", IP: "",
Port: svcInfo.nodePort, Port: svcInfo.NodePort,
Protocol: protocol, Protocol: protocol,
} }
if proxier.portsMap[lp] != nil { if proxier.portsMap[lp] != nil {
@ -1090,9 +993,9 @@ func (proxier *Proxier) syncProxyRules() {
"-A", string(kubeNodePortsChain), "-A", string(kubeNodePortsChain),
"-m", "comment", "--comment", svcNameString, "-m", "comment", "--comment", svcNameString,
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"--dport", strconv.Itoa(svcInfo.nodePort), "--dport", strconv.Itoa(svcInfo.NodePort),
) )
if !svcInfo.onlyNodeLocalEndpoints { if !svcInfo.OnlyNodeLocalEndpoints {
// Nodeports need SNAT, unless they're local. // Nodeports need SNAT, unless they're local.
writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...)
// Jump to the service chain. // Jump to the service chain.
@ -1115,7 +1018,7 @@ func (proxier *Proxier) syncProxyRules() {
"-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString), "-m", "comment", "--comment", fmt.Sprintf(`"%s has no endpoints"`, svcNameString),
"-m", "addrtype", "--dst-type", "LOCAL", "-m", "addrtype", "--dst-type", "LOCAL",
"-m", protocol, "-p", protocol, "-m", protocol, "-p", protocol,
"--dport", strconv.Itoa(svcInfo.nodePort), "--dport", strconv.Itoa(svcInfo.NodePort),
"-j", "REJECT", "-j", "REJECT",
) )
} }
@ -1151,13 +1054,13 @@ func (proxier *Proxier) syncProxyRules() {
} }
// First write session affinity rules, if applicable. // First write session affinity rules, if applicable.
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
for _, endpointChain := range endpointChains { for _, endpointChain := range endpointChains {
writeLine(proxier.natRules, writeLine(proxier.natRules,
"-A", string(svcChain), "-A", string(svcChain),
"-m", "comment", "--comment", svcNameString, "-m", "comment", "--comment", svcNameString,
"-m", "recent", "--name", string(endpointChain), "-m", "recent", "--name", string(endpointChain),
"--rcheck", "--seconds", strconv.Itoa(svcInfo.stickyMaxAgeSeconds), "--reap", "--rcheck", "--seconds", strconv.Itoa(svcInfo.StickyMaxAgeSeconds), "--reap",
"-j", string(endpointChain)) "-j", string(endpointChain))
} }
} }
@ -1196,16 +1099,16 @@ func (proxier *Proxier) syncProxyRules() {
"-s", utilproxy.ToCIDR(net.ParseIP(epIP)), "-s", utilproxy.ToCIDR(net.ParseIP(epIP)),
"-j", string(KubeMarkMasqChain))...) "-j", string(KubeMarkMasqChain))...)
// Update client-affinity lists. // Update client-affinity lists.
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
args = append(args, "-m", "recent", "--name", string(endpointChain), "--set") args = append(args, "-m", "recent", "--name", string(endpointChain), "--set")
} }
// DNAT to final destination. // DNAT to final destination.
args = append(args, "-m", protocol, "-p", protocol, "-j", "DNAT", "--to-destination", endpoints[i].endpoint) args = append(args, "-m", protocol, "-p", protocol, "-j", "DNAT", "--to-destination", endpoints[i].Endpoint)
writeLine(proxier.natRules, args...) writeLine(proxier.natRules, args...)
} }
// The logic below this applies only if this service is marked as OnlyLocal // The logic below this applies only if this service is marked as OnlyLocal
if !svcInfo.onlyNodeLocalEndpoints { if !svcInfo.OnlyNodeLocalEndpoints {
continue continue
} }
@ -1214,7 +1117,7 @@ func (proxier *Proxier) syncProxyRules() {
localEndpoints := make([]*endpointsInfo, 0) localEndpoints := make([]*endpointsInfo, 0)
localEndpointChains := make([]utiliptables.Chain, 0) localEndpointChains := make([]utiliptables.Chain, 0)
for i := range endpointChains { for i := range endpointChains {
if endpoints[i].isLocal { if endpoints[i].IsLocal {
// These slices parallel each other; must be kept in sync // These slices parallel each other; must be kept in sync
localEndpoints = append(localEndpoints, endpoints[i]) localEndpoints = append(localEndpoints, endpoints[i])
localEndpointChains = append(localEndpointChains, endpointChains[i]) localEndpointChains = append(localEndpointChains, endpointChains[i])
@ -1247,13 +1150,13 @@ func (proxier *Proxier) syncProxyRules() {
writeLine(proxier.natRules, args...) writeLine(proxier.natRules, args...)
} else { } else {
// First write session affinity rules only over local endpoints, if applicable. // First write session affinity rules only over local endpoints, if applicable.
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
for _, endpointChain := range localEndpointChains { for _, endpointChain := range localEndpointChains {
writeLine(proxier.natRules, writeLine(proxier.natRules,
"-A", string(svcXlbChain), "-A", string(svcXlbChain),
"-m", "comment", "--comment", svcNameString, "-m", "comment", "--comment", svcNameString,
"-m", "recent", "--name", string(endpointChain), "-m", "recent", "--name", string(endpointChain),
"--rcheck", "--seconds", strconv.Itoa(svcInfo.stickyMaxAgeSeconds), "--reap", "--rcheck", "--seconds", strconv.Itoa(svcInfo.StickyMaxAgeSeconds), "--reap",
"-j", string(endpointChain)) "-j", string(endpointChain))
} }
} }
@ -1316,7 +1219,7 @@ func (proxier *Proxier) syncProxyRules() {
break break
} }
// Ignore IP addresses with incorrect version // Ignore IP addresses with incorrect version
if isIPv6 && !conntrack.IsIPv6String(address) || !isIPv6 && conntrack.IsIPv6String(address) { if isIPv6 && !utilnet.IsIPv6String(address) || !isIPv6 && utilnet.IsIPv6String(address) {
glog.Errorf("IP address %s has incorrect IP version", address) glog.Errorf("IP address %s has incorrect IP version", address)
continue continue
} }

View File

@ -179,12 +179,14 @@ func TestGetChainLinesMultipleTables(t *testing.T) {
func newFakeServiceInfo(service proxy.ServicePortName, ip net.IP, port int, protocol api.Protocol, onlyNodeLocalEndpoints bool) *serviceInfo { func newFakeServiceInfo(service proxy.ServicePortName, ip net.IP, port int, protocol api.Protocol, onlyNodeLocalEndpoints bool) *serviceInfo {
return &serviceInfo{ return &serviceInfo{
sessionAffinityType: api.ServiceAffinityNone, // default BaseServiceInfo: &proxy.BaseServiceInfo{
stickyMaxAgeSeconds: int(api.DefaultClientIPServiceAffinitySeconds), // default SessionAffinityType: api.ServiceAffinityNone, // default
clusterIP: ip, StickyMaxAgeSeconds: int(api.DefaultClientIPServiceAffinitySeconds), // default
port: port, ClusterIP: ip,
protocol: protocol, Port: port,
onlyNodeLocalEndpoints: onlyNodeLocalEndpoints, Protocol: protocol,
OnlyNodeLocalEndpoints: onlyNodeLocalEndpoints,
},
} }
} }
@ -391,9 +393,9 @@ func NewFakeProxier(ipt utiliptables.Interface) *Proxier {
p := &Proxier{ p := &Proxier{
exec: &fakeexec.FakeExec{}, exec: &fakeexec.FakeExec{},
serviceMap: make(proxy.ServiceMap), serviceMap: make(proxy.ServiceMap),
serviceChanges: proxy.NewServiceChangeTracker(), serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, nil, nil),
endpointsMap: make(proxy.EndpointsMap), endpointsMap: make(proxy.EndpointsMap),
endpointsChanges: proxy.NewEndpointChangeTracker(testHostname), endpointsChanges: proxy.NewEndpointChangeTracker(testHostname, newEndpointInfo, nil, nil),
iptables: ipt, iptables: ipt,
clusterCIDR: "10.0.0.0/24", clusterCIDR: "10.0.0.0/24",
hostname: testHostname, hostname: testHostname,
@ -821,7 +823,7 @@ func TestExternalIPsReject(t *testing.T) {
kubeSvcRules := ipt.GetRules(string(kubeExternalServicesChain)) kubeSvcRules := ipt.GetRules(string(kubeExternalServicesChain))
if !hasJump(kubeSvcRules, iptablestest.Reject, svcExternalIPs, svcPort) { if !hasJump(kubeSvcRules, iptablestest.Reject, svcExternalIPs, svcPort) {
errorf(fmt.Sprintf("Failed to a %v rule for externalIP %v with no endpoints", iptablestest.Reject, svcPortName), kubeSvcRules, t) errorf(fmt.Sprintf("Failed to find a %v rule for externalIP %v with no endpoints", iptablestest.Reject, svcPortName), kubeSvcRules, t)
} }
} }
@ -1379,7 +1381,10 @@ func compareEndpointsMaps(t *testing.T, tci int, newMap proxy.EndpointsMap, expe
t.Errorf("Failed to cast endpointsInfo") t.Errorf("Failed to cast endpointsInfo")
continue continue
} }
if *newEp != *(expected[x][i]) { if newEp.Endpoint != expected[x][i].Endpoint ||
newEp.IsLocal != expected[x][i].IsLocal ||
newEp.protocol != expected[x][i].protocol ||
newEp.chainName != expected[x][i].chainName {
t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, expected[x][i], newEp) t.Errorf("[%d] expected new[%v][%d] to be %v, got %v", tci, x, i, expected[x][i], newEp)
} }
} }
@ -1721,12 +1726,12 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1742,12 +1747,12 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1765,18 +1770,18 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1792,24 +1797,24 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1829,54 +1834,54 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{endpoint: "1.1.1.2:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{endpoint: "1.1.1.2:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
{endpoint: "1.1.1.4:13", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:13", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p14"): { makeServicePortName("ns1", "ep1", "p14"): {
{endpoint: "1.1.1.3:14", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:14", IsLocal: false}},
{endpoint: "1.1.1.4:14", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:14", IsLocal: true}},
}, },
makeServicePortName("ns2", "ep2", "p21"): { makeServicePortName("ns2", "ep2", "p21"): {
{endpoint: "2.2.2.1:21", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:21", IsLocal: false}},
{endpoint: "2.2.2.2:21", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:21", IsLocal: true}},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.1:22", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:22", IsLocal: false}},
{endpoint: "2.2.2.2:22", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{endpoint: "1.1.1.2:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{endpoint: "1.1.1.2:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:13", IsLocal: false}},
{endpoint: "1.1.1.4:13", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:13", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p14"): { makeServicePortName("ns1", "ep1", "p14"): {
{endpoint: "1.1.1.3:14", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.3:14", IsLocal: false}},
{endpoint: "1.1.1.4:14", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.4:14", IsLocal: true}},
}, },
makeServicePortName("ns2", "ep2", "p21"): { makeServicePortName("ns2", "ep2", "p21"): {
{endpoint: "2.2.2.1:21", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:21", IsLocal: false}},
{endpoint: "2.2.2.2:21", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:21", IsLocal: true}},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.1:22", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.1:22", IsLocal: false}},
{endpoint: "2.2.2.2:22", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1896,7 +1901,7 @@ func Test_updateEndpointsMap(t *testing.T) {
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{}, oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1916,7 +1921,7 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: true}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{}, expectedResult: map[proxy.ServicePortName][]*endpointsInfo{},
@ -1936,17 +1941,17 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{endpoint: "1.1.1.2:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{endpoint: "1.1.1.2:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1966,17 +1971,17 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{endpoint: "1.1.1.2:11", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:11", IsLocal: true}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:12", IsLocal: false}},
{endpoint: "1.1.1.2:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -2001,15 +2006,15 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: true}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -2029,15 +2034,15 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -2056,12 +2061,12 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11-2"): { makeServicePortName("ns1", "ep1", "p11-2"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -2082,12 +2087,12 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:22", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:22", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -2112,39 +2117,39 @@ func Test_updateEndpointsMap(t *testing.T) {
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.2:22", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.2:22", IsLocal: true}},
{endpoint: "2.2.2.22:22", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.22:22", IsLocal: true}},
}, },
makeServicePortName("ns2", "ep2", "p23"): { makeServicePortName("ns2", "ep2", "p23"): {
{endpoint: "2.2.2.3:23", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "2.2.2.3:23", IsLocal: true}},
}, },
makeServicePortName("ns4", "ep4", "p44"): { makeServicePortName("ns4", "ep4", "p44"): {
{endpoint: "4.4.4.4:44", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.4:44", IsLocal: true}},
{endpoint: "4.4.4.5:44", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.5:44", IsLocal: true}},
}, },
makeServicePortName("ns4", "ep4", "p45"): { makeServicePortName("ns4", "ep4", "p45"): {
{endpoint: "4.4.4.6:45", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.6:45", IsLocal: true}},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
{endpoint: "1.1.1.11:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.11:11", IsLocal: false}},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:12", IsLocal: false}},
}, },
makeServicePortName("ns1", "ep1", "p122"): { makeServicePortName("ns1", "ep1", "p122"): {
{endpoint: "1.1.1.2:122", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.2:122", IsLocal: false}},
}, },
makeServicePortName("ns3", "ep3", "p33"): { makeServicePortName("ns3", "ep3", "p33"): {
{endpoint: "3.3.3.3:33", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "3.3.3.3:33", IsLocal: false}},
}, },
makeServicePortName("ns4", "ep4", "p44"): { makeServicePortName("ns4", "ep4", "p44"): {
{endpoint: "4.4.4.4:44", isLocal: true}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "4.4.4.4:44", IsLocal: true}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -2182,7 +2187,7 @@ func Test_updateEndpointsMap(t *testing.T) {
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{}, oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*endpointsInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {BaseEndpointInfo: &proxy.BaseEndpointInfo{Endpoint: "1.1.1.1:11", IsLocal: false}},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},

View File

@ -78,9 +78,7 @@ go_library(
}), }),
importpath = "k8s.io/kubernetes/pkg/proxy/ipvs", importpath = "k8s.io/kubernetes/pkg/proxy/ipvs",
deps = [ deps = [
"//pkg/api/service:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//pkg/proxy:go_default_library", "//pkg/proxy:go_default_library",
"//pkg/proxy/healthcheck:go_default_library", "//pkg/proxy/healthcheck:go_default_library",
"//pkg/proxy/metrics:go_default_library", "//pkg/proxy/metrics:go_default_library",
@ -90,6 +88,7 @@ go_library(
"//pkg/util/ipset:go_default_library", "//pkg/util/ipset:go_default_library",
"//pkg/util/iptables:go_default_library", "//pkg/util/iptables:go_default_library",
"//pkg/util/ipvs:go_default_library", "//pkg/util/ipvs:go_default_library",
"//pkg/util/net:go_default_library",
"//pkg/util/sysctl:go_default_library", "//pkg/util/sysctl:go_default_library",
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",

View File

@ -37,9 +37,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
apiservice "k8s.io/kubernetes/pkg/api/service"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/proxy" "k8s.io/kubernetes/pkg/proxy"
"k8s.io/kubernetes/pkg/proxy/healthcheck" "k8s.io/kubernetes/pkg/proxy/healthcheck"
"k8s.io/kubernetes/pkg/proxy/metrics" "k8s.io/kubernetes/pkg/proxy/metrics"
@ -49,6 +47,7 @@ import (
utilipset "k8s.io/kubernetes/pkg/util/ipset" utilipset "k8s.io/kubernetes/pkg/util/ipset"
utiliptables "k8s.io/kubernetes/pkg/util/iptables" utiliptables "k8s.io/kubernetes/pkg/util/iptables"
utilipvs "k8s.io/kubernetes/pkg/util/ipvs" utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
utilnet "k8s.io/kubernetes/pkg/util/net"
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl" utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
utilexec "k8s.io/utils/exec" utilexec "k8s.io/utils/exec"
) )
@ -291,8 +290,14 @@ func NewProxier(ipt utiliptables.Interface,
nodeIP = net.ParseIP("127.0.0.1") nodeIP = net.ParseIP("127.0.0.1")
} }
isIPv6 := utilnet.IsIPv6(nodeIP)
glog.V(2).Infof("nodeIP: %v, isIPv6: %v", nodeIP, isIPv6)
if len(clusterCIDR) == 0 { if len(clusterCIDR) == 0 {
glog.Warningf("clusterCIDR not specified, unable to distinguish between internal and external traffic") glog.Warningf("clusterCIDR not specified, unable to distinguish between internal and external traffic")
} else if utilnet.IsIPv6CIDR(clusterCIDR) != isIPv6 {
return nil, fmt.Errorf("clusterCIDR %s has incorrect IP version: expect isIPv6=%t", clusterCIDR, isIPv6)
} }
if len(scheduler) == 0 { if len(scheduler) == 0 {
@ -302,16 +307,12 @@ func NewProxier(ipt utiliptables.Interface,
healthChecker := healthcheck.NewServer(hostname, recorder, nil, nil) // use default implementations of deps healthChecker := healthcheck.NewServer(hostname, recorder, nil, nil) // use default implementations of deps
isIPv6 := conntrack.IsIPv6(nodeIP)
glog.V(2).Infof("nodeIP: %v, isIPv6: %v", nodeIP, isIPv6)
proxier := &Proxier{ proxier := &Proxier{
portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable), portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable),
serviceMap: make(proxy.ServiceMap), serviceMap: make(proxy.ServiceMap),
serviceChanges: proxy.NewServiceChangeTracker(), serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, &isIPv6, recorder),
endpointsMap: make(proxy.EndpointsMap), endpointsMap: make(proxy.EndpointsMap),
endpointsChanges: proxy.NewEndpointChangeTracker(hostname), endpointsChanges: proxy.NewEndpointChangeTracker(hostname, nil, &isIPv6, recorder),
syncPeriod: syncPeriod, syncPeriod: syncPeriod,
minSyncPeriod: minSyncPeriod, minSyncPeriod: minSyncPeriod,
iptables: ipt, iptables: ipt,
@ -353,140 +354,23 @@ func NewProxier(ipt utiliptables.Interface,
// internal struct for string service information // internal struct for string service information
type serviceInfo struct { type serviceInfo struct {
clusterIP net.IP *proxy.BaseServiceInfo
port int
protocol api.Protocol
nodePort int
loadBalancerStatus api.LoadBalancerStatus
sessionAffinityType api.ServiceAffinity
stickyMaxAgeSeconds int
externalIPs []string
loadBalancerSourceRanges []string
onlyNodeLocalEndpoints bool
healthCheckNodePort int
// The following fields are computed and stored for performance reasons. // The following fields are computed and stored for performance reasons.
serviceNameString string serviceNameString string
} }
// returns a new proxy.ServicePort which abstracts a serviceInfo // returns a new proxy.ServicePort which abstracts a serviceInfo
func newServiceInfo(port *api.ServicePort, service *api.Service) proxy.ServicePort { func newServiceInfo(port *api.ServicePort, service *api.Service, baseInfo *proxy.BaseServiceInfo) proxy.ServicePort {
onlyNodeLocalEndpoints := false info := &serviceInfo{BaseServiceInfo: baseInfo}
if apiservice.RequestsOnlyLocalTraffic(service) {
onlyNodeLocalEndpoints = true
}
var stickyMaxAgeSeconds int
if service.Spec.SessionAffinity == api.ServiceAffinityClientIP {
stickyMaxAgeSeconds = int(*service.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds)
}
info := &serviceInfo{
clusterIP: net.ParseIP(service.Spec.ClusterIP),
port: int(port.Port),
protocol: port.Protocol,
nodePort: int(port.NodePort),
// Deep-copy in case the service instance changes
loadBalancerStatus: *helper.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer),
sessionAffinityType: service.Spec.SessionAffinity,
stickyMaxAgeSeconds: stickyMaxAgeSeconds,
externalIPs: make([]string, len(service.Spec.ExternalIPs)),
loadBalancerSourceRanges: make([]string, len(service.Spec.LoadBalancerSourceRanges)),
onlyNodeLocalEndpoints: onlyNodeLocalEndpoints,
}
copy(info.loadBalancerSourceRanges, service.Spec.LoadBalancerSourceRanges)
copy(info.externalIPs, service.Spec.ExternalIPs)
svcName := types.NamespacedName{Namespace: service.Namespace, Name: service.Name}
svcPortName := proxy.ServicePortName{NamespacedName: svcName, Port: port.Name}
if apiservice.NeedsHealthCheck(service) {
p := service.Spec.HealthCheckNodePort
if p == 0 {
glog.Errorf("Service %q has no healthcheck nodeport", svcName.String())
} else {
info.healthCheckNodePort = int(p)
}
}
// Store the following for performance reasons. // Store the following for performance reasons.
svcName := types.NamespacedName{Namespace: service.Namespace, Name: service.Name}
svcPortName := proxy.ServicePortName{NamespacedName: svcName, Port: port.Name}
info.serviceNameString = svcPortName.String() info.serviceNameString = svcPortName.String()
return info return info
} }
// ClusterIP is part of ServicePort interface.
func (info *serviceInfo) ClusterIP() string {
return info.clusterIP.String()
}
// Port is part of ServicePort interface.
func (info *serviceInfo) Port() int {
return info.port
}
// Protocol is part of ServicePort interface.
func (info *serviceInfo) Protocol() api.Protocol {
return info.protocol
}
// String is part of ServicePort interface.
func (info *serviceInfo) String() string {
return fmt.Sprintf("%s:%d/%s", info.clusterIP, info.port, info.protocol)
}
// HealthCheckNodePort is part of ServicePort interface.
func (info *serviceInfo) HealthCheckNodePort() int {
return info.healthCheckNodePort
}
var _ proxy.ServicePort = &serviceInfo{}
// internal struct for endpoints information
type endpointsInfo struct {
endpoint string // TODO: should be an endpointString type
isLocal bool
}
// returns a new proxy.Endpoint which abstracts a endpointsInfo
func newEndpointsInfo(IP string, port int, isLocal bool) proxy.Endpoint {
return &endpointsInfo{
endpoint: net.JoinHostPort(IP, strconv.Itoa(port)),
isLocal: isLocal,
}
}
// IsLocal is part of proxy.Endpoint interface.
func (e *endpointsInfo) IsLocal() bool {
return e.isLocal
}
// String is part of proxy.Endpoint interface.
func (e *endpointsInfo) String() string {
return fmt.Sprintf("%v", e.endpoint)
}
// IP returns just the IP part of the endpoint, it's a part of proxy.Endpoints interface.
func (e *endpointsInfo) IP() string {
return utilproxy.IPPart(e.endpoint)
}
// PortPart returns just the Port part of the endpoint.
func (e *endpointsInfo) PortPart() (int, error) {
return utilproxy.PortPart(e.endpoint)
}
// Equal is part of proxy.Endpoint interface.
func (e *endpointsInfo) Equal(other proxy.Endpoint) bool {
o, ok := other.(*endpointsInfo)
if !ok {
glog.Errorf("Failed to cast endpointsInfo")
return false
}
return e.endpoint == o.endpoint &&
e.isLocal == o.isLocal
}
var _ proxy.Endpoint = &endpointsInfo{}
// KernelHandler can handle the current installed kernel modules. // KernelHandler can handle the current installed kernel modules.
type KernelHandler interface { type KernelHandler interface {
GetModules() ([]string, error) GetModules() ([]string, error)
@ -668,23 +552,19 @@ func (proxier *Proxier) isInitialized() bool {
// OnServiceAdd is called whenever creation of new service object is observed. // OnServiceAdd is called whenever creation of new service object is observed.
func (proxier *Proxier) OnServiceAdd(service *api.Service) { func (proxier *Proxier) OnServiceAdd(service *api.Service) {
if proxier.serviceChanges.Update(nil, service, newServiceInfo) && proxier.isInitialized() { proxier.OnServiceUpdate(nil, service)
proxier.syncRunner.Run()
}
} }
// OnServiceUpdate is called whenever modification of an existing service object is observed. // OnServiceUpdate is called whenever modification of an existing service object is observed.
func (proxier *Proxier) OnServiceUpdate(oldService, service *api.Service) { func (proxier *Proxier) OnServiceUpdate(oldService, service *api.Service) {
if proxier.serviceChanges.Update(oldService, service, newServiceInfo) && proxier.isInitialized() { if proxier.serviceChanges.Update(oldService, service) && proxier.isInitialized() {
proxier.syncRunner.Run() proxier.syncRunner.Run()
} }
} }
// OnServiceDelete is called whenever deletion of an existing service object is observed. // OnServiceDelete is called whenever deletion of an existing service object is observed.
func (proxier *Proxier) OnServiceDelete(service *api.Service) { func (proxier *Proxier) OnServiceDelete(service *api.Service) {
if proxier.serviceChanges.Update(service, nil, newServiceInfo) && proxier.isInitialized() { proxier.OnServiceUpdate(service, nil)
proxier.syncRunner.Run()
}
} }
// OnServiceSynced is called once all the initial even handlers were called and the state is fully propagated to local cache. // OnServiceSynced is called once all the initial even handlers were called and the state is fully propagated to local cache.
@ -700,23 +580,19 @@ func (proxier *Proxier) OnServiceSynced() {
// OnEndpointsAdd is called whenever creation of new endpoints object is observed. // OnEndpointsAdd is called whenever creation of new endpoints object is observed.
func (proxier *Proxier) OnEndpointsAdd(endpoints *api.Endpoints) { func (proxier *Proxier) OnEndpointsAdd(endpoints *api.Endpoints) {
if proxier.endpointsChanges.Update(nil, endpoints, newEndpointsInfo) && proxier.isInitialized() { proxier.OnEndpointsUpdate(nil, endpoints)
proxier.syncRunner.Run()
}
} }
// OnEndpointsUpdate is called whenever modification of an existing endpoints object is observed. // OnEndpointsUpdate is called whenever modification of an existing endpoints object is observed.
func (proxier *Proxier) OnEndpointsUpdate(oldEndpoints, endpoints *api.Endpoints) { func (proxier *Proxier) OnEndpointsUpdate(oldEndpoints, endpoints *api.Endpoints) {
if proxier.endpointsChanges.Update(oldEndpoints, endpoints, newEndpointsInfo) && proxier.isInitialized() { if proxier.endpointsChanges.Update(oldEndpoints, endpoints) && proxier.isInitialized() {
proxier.syncRunner.Run() proxier.syncRunner.Run()
} }
} }
// OnEndpointsDelete is called whenever deletion of an existing endpoints object is observed. // OnEndpointsDelete is called whenever deletion of an existing endpoints object is observed.
func (proxier *Proxier) OnEndpointsDelete(endpoints *api.Endpoints) { func (proxier *Proxier) OnEndpointsDelete(endpoints *api.Endpoints) {
if proxier.endpointsChanges.Update(endpoints, nil, newEndpointsInfo) && proxier.isInitialized() { proxier.OnEndpointsUpdate(endpoints, nil)
proxier.syncRunner.Run()
}
} }
// OnEndpointsSynced is called once all the initial event handlers were called and the state is fully propagated to local cache. // OnEndpointsSynced is called once all the initial event handlers were called and the state is fully propagated to local cache.
@ -757,9 +633,9 @@ func (proxier *Proxier) syncProxyRules() {
staleServices := serviceUpdateResult.UDPStaleClusterIP staleServices := serviceUpdateResult.UDPStaleClusterIP
// merge stale services gathered from updateEndpointsMap // merge stale services gathered from updateEndpointsMap
for _, svcPortName := range endpointUpdateResult.StaleServiceNames { for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && svcInfo.Protocol() == api.ProtocolUDP { if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && svcInfo.GetProtocol() == api.ProtocolUDP {
glog.V(2).Infof("Stale udp service %v -> %s", svcPortName, svcInfo.ClusterIP()) glog.V(2).Infof("Stale udp service %v -> %s", svcPortName, svcInfo.ClusterIPString())
staleServices.Insert(svcInfo.ClusterIP()) staleServices.Insert(svcInfo.ClusterIPString())
} }
} }
@ -868,20 +744,20 @@ func (proxier *Proxier) syncProxyRules() {
glog.Errorf("Failed to cast serviceInfo %q", svcName.String()) glog.Errorf("Failed to cast serviceInfo %q", svcName.String())
continue continue
} }
protocol := strings.ToLower(string(svcInfo.protocol)) protocol := strings.ToLower(string(svcInfo.Protocol))
// Precompute svcNameString; with many services the many calls // Precompute svcNameString; with many services the many calls
// to ServicePortName.String() show up in CPU profiles. // to ServicePortName.String() show up in CPU profiles.
svcNameString := svcName.String() svcNameString := svcName.String()
// Handle traffic that loops back to the originator with SNAT. // Handle traffic that loops back to the originator with SNAT.
for _, e := range proxier.endpointsMap[svcName] { for _, e := range proxier.endpointsMap[svcName] {
ep, ok := e.(*endpointsInfo) ep, ok := e.(*proxy.BaseEndpointInfo)
if !ok { if !ok {
glog.Errorf("Failed to cast endpointsInfo %q", e.String()) glog.Errorf("Failed to cast BaseEndpointInfo %q", e.String())
continue continue
} }
epIP := ep.IP() epIP := ep.IP()
epPort, err := ep.PortPart() epPort, err := ep.Port()
// Error parsing this endpoint has been logged. Skip to next endpoint. // Error parsing this endpoint has been logged. Skip to next endpoint.
if epIP == "" || err != nil { if epIP == "" || err != nil {
continue continue
@ -903,8 +779,8 @@ func (proxier *Proxier) syncProxyRules() {
// Capture the clusterIP. // Capture the clusterIP.
// ipset call // ipset call
entry := &utilipset.Entry{ entry := &utilipset.Entry{
IP: svcInfo.clusterIP.String(), IP: svcInfo.ClusterIP.String(),
Port: svcInfo.port, Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
SetType: utilipset.HashIPPort, SetType: utilipset.HashIPPort,
} }
@ -920,15 +796,15 @@ func (proxier *Proxier) syncProxyRules() {
} }
// ipvs call // ipvs call
serv := &utilipvs.VirtualServer{ serv := &utilipvs.VirtualServer{
Address: svcInfo.clusterIP, Address: svcInfo.ClusterIP,
Port: uint16(svcInfo.port), Port: uint16(svcInfo.Port),
Protocol: string(svcInfo.protocol), Protocol: string(svcInfo.Protocol),
Scheduler: proxier.ipvsScheduler, Scheduler: proxier.ipvsScheduler,
} }
// Set session affinity flag and timeout for IPVS service // Set session affinity flag and timeout for IPVS service
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
serv.Flags |= utilipvs.FlagPersistent serv.Flags |= utilipvs.FlagPersistent
serv.Timeout = uint32(svcInfo.stickyMaxAgeSeconds) serv.Timeout = uint32(svcInfo.StickyMaxAgeSeconds)
} }
// We need to bind ClusterIP to dummy interface, so set `bindAddr` parameter to `true` in syncService() // We need to bind ClusterIP to dummy interface, so set `bindAddr` parameter to `true` in syncService()
if err := proxier.syncService(svcNameString, serv, true); err == nil { if err := proxier.syncService(svcNameString, serv, true); err == nil {
@ -943,14 +819,14 @@ func (proxier *Proxier) syncProxyRules() {
} }
// Capture externalIPs. // Capture externalIPs.
for _, externalIP := range svcInfo.externalIPs { for _, externalIP := range svcInfo.ExternalIPs {
if local, err := utilproxy.IsLocalIP(externalIP); err != nil { if local, err := utilproxy.IsLocalIP(externalIP); err != nil {
glog.Errorf("can't determine if IP is local, assuming not: %v", err) glog.Errorf("can't determine if IP is local, assuming not: %v", err)
} else if local { } else if local {
lp := utilproxy.LocalPort{ lp := utilproxy.LocalPort{
Description: "externalIP for " + svcNameString, Description: "externalIP for " + svcNameString,
IP: externalIP, IP: externalIP,
Port: svcInfo.port, Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
} }
if proxier.portsMap[lp] != nil { if proxier.portsMap[lp] != nil {
@ -978,7 +854,7 @@ func (proxier *Proxier) syncProxyRules() {
// ipset call // ipset call
entry := &utilipset.Entry{ entry := &utilipset.Entry{
IP: externalIP, IP: externalIP,
Port: svcInfo.port, Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
SetType: utilipset.HashIPPort, SetType: utilipset.HashIPPort,
} }
@ -992,18 +868,18 @@ func (proxier *Proxier) syncProxyRules() {
// ipvs call // ipvs call
serv := &utilipvs.VirtualServer{ serv := &utilipvs.VirtualServer{
Address: net.ParseIP(externalIP), Address: net.ParseIP(externalIP),
Port: uint16(svcInfo.port), Port: uint16(svcInfo.Port),
Protocol: string(svcInfo.protocol), Protocol: string(svcInfo.Protocol),
Scheduler: proxier.ipvsScheduler, Scheduler: proxier.ipvsScheduler,
} }
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
serv.Flags |= utilipvs.FlagPersistent serv.Flags |= utilipvs.FlagPersistent
serv.Timeout = uint32(svcInfo.stickyMaxAgeSeconds) serv.Timeout = uint32(svcInfo.StickyMaxAgeSeconds)
} }
// There is no need to bind externalIP to dummy interface, so set parameter `bindAddr` to `false`. // There is no need to bind externalIP to dummy interface, so set parameter `bindAddr` to `false`.
if err := proxier.syncService(svcNameString, serv, false); err == nil { if err := proxier.syncService(svcNameString, serv, false); err == nil {
activeIPVSServices[serv.String()] = true activeIPVSServices[serv.String()] = true
if err := proxier.syncEndpoint(svcName, svcInfo.onlyNodeLocalEndpoints, serv); err != nil { if err := proxier.syncEndpoint(svcName, svcInfo.OnlyNodeLocalEndpoints, serv); err != nil {
glog.Errorf("Failed to sync endpoint for service: %v, err: %v", serv, err) glog.Errorf("Failed to sync endpoint for service: %v, err: %v", serv, err)
} }
} else { } else {
@ -1012,12 +888,12 @@ func (proxier *Proxier) syncProxyRules() {
} }
// Capture load-balancer ingress. // Capture load-balancer ingress.
for _, ingress := range svcInfo.loadBalancerStatus.Ingress { for _, ingress := range svcInfo.LoadBalancerStatus.Ingress {
if ingress.IP != "" { if ingress.IP != "" {
// ipset call // ipset call
entry = &utilipset.Entry{ entry = &utilipset.Entry{
IP: ingress.IP, IP: ingress.IP,
Port: svcInfo.port, Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
SetType: utilipset.HashIPPort, SetType: utilipset.HashIPPort,
} }
@ -1025,14 +901,14 @@ func (proxier *Proxier) syncProxyRules() {
// proxier.kubeServiceAccessSet.activeEntries.Insert(entry.String()) // proxier.kubeServiceAccessSet.activeEntries.Insert(entry.String())
// If we are proxying globally, we need to masquerade in case we cross nodes. // If we are proxying globally, we need to masquerade in case we cross nodes.
// If we are proxying only locally, we can retain the source IP. // If we are proxying only locally, we can retain the source IP.
if !svcInfo.onlyNodeLocalEndpoints { if !svcInfo.OnlyNodeLocalEndpoints {
if valid := proxier.lbMasqSet.validateEntry(entry); !valid { if valid := proxier.lbMasqSet.validateEntry(entry); !valid {
glog.Errorf("%s", fmt.Sprintf(EntryInvalidErr, entry, proxier.lbMasqSet.Name)) glog.Errorf("%s", fmt.Sprintf(EntryInvalidErr, entry, proxier.lbMasqSet.Name))
continue continue
} }
proxier.lbMasqSet.activeEntries.Insert(entry.String()) proxier.lbMasqSet.activeEntries.Insert(entry.String())
} }
if len(svcInfo.loadBalancerSourceRanges) != 0 { if len(svcInfo.LoadBalancerSourceRanges) != 0 {
// The service firewall rules are created based on ServiceSpec.loadBalancerSourceRanges field. // The service firewall rules are created based on ServiceSpec.loadBalancerSourceRanges field.
// This currently works for loadbalancers that preserves source ips. // This currently works for loadbalancers that preserves source ips.
// For loadbalancers which direct traffic to service NodePort, the firewall rules will not apply. // For loadbalancers which direct traffic to service NodePort, the firewall rules will not apply.
@ -1043,11 +919,11 @@ func (proxier *Proxier) syncProxyRules() {
proxier.lbIngressSet.activeEntries.Insert(entry.String()) proxier.lbIngressSet.activeEntries.Insert(entry.String())
allowFromNode := false allowFromNode := false
for _, src := range svcInfo.loadBalancerSourceRanges { for _, src := range svcInfo.LoadBalancerSourceRanges {
// ipset call // ipset call
entry = &utilipset.Entry{ entry = &utilipset.Entry{
IP: ingress.IP, IP: ingress.IP,
Port: svcInfo.port, Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
Net: src, Net: src,
SetType: utilipset.HashIPPortNet, SetType: utilipset.HashIPPortNet,
@ -1071,7 +947,7 @@ func (proxier *Proxier) syncProxyRules() {
if allowFromNode { if allowFromNode {
entry = &utilipset.Entry{ entry = &utilipset.Entry{
IP: ingress.IP, IP: ingress.IP,
Port: svcInfo.port, Port: svcInfo.Port,
Protocol: protocol, Protocol: protocol,
IP2: ingress.IP, IP2: ingress.IP,
SetType: utilipset.HashIPPortIP, SetType: utilipset.HashIPPortIP,
@ -1088,18 +964,18 @@ func (proxier *Proxier) syncProxyRules() {
// ipvs call // ipvs call
serv := &utilipvs.VirtualServer{ serv := &utilipvs.VirtualServer{
Address: net.ParseIP(ingress.IP), Address: net.ParseIP(ingress.IP),
Port: uint16(svcInfo.port), Port: uint16(svcInfo.Port),
Protocol: string(svcInfo.protocol), Protocol: string(svcInfo.Protocol),
Scheduler: proxier.ipvsScheduler, Scheduler: proxier.ipvsScheduler,
} }
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
serv.Flags |= utilipvs.FlagPersistent serv.Flags |= utilipvs.FlagPersistent
serv.Timeout = uint32(svcInfo.stickyMaxAgeSeconds) serv.Timeout = uint32(svcInfo.StickyMaxAgeSeconds)
} }
// There is no need to bind LB ingress.IP to dummy interface, so set parameter `bindAddr` to `false`. // There is no need to bind LB ingress.IP to dummy interface, so set parameter `bindAddr` to `false`.
if err := proxier.syncService(svcNameString, serv, false); err == nil { if err := proxier.syncService(svcNameString, serv, false); err == nil {
activeIPVSServices[serv.String()] = true activeIPVSServices[serv.String()] = true
if err := proxier.syncEndpoint(svcName, svcInfo.onlyNodeLocalEndpoints, serv); err != nil { if err := proxier.syncEndpoint(svcName, svcInfo.OnlyNodeLocalEndpoints, serv); err != nil {
glog.Errorf("Failed to sync endpoint for service: %v, err: %v", serv, err) glog.Errorf("Failed to sync endpoint for service: %v, err: %v", serv, err)
} }
} else { } else {
@ -1108,11 +984,11 @@ func (proxier *Proxier) syncProxyRules() {
} }
} }
if svcInfo.nodePort != 0 { if svcInfo.NodePort != 0 {
lp := utilproxy.LocalPort{ lp := utilproxy.LocalPort{
Description: "nodePort for " + svcNameString, Description: "nodePort for " + svcNameString,
IP: "", IP: "",
Port: svcInfo.nodePort, Port: svcInfo.NodePort,
Protocol: protocol, Protocol: protocol,
} }
if proxier.portsMap[lp] != nil { if proxier.portsMap[lp] != nil {
@ -1125,7 +1001,7 @@ func (proxier *Proxier) syncProxyRules() {
continue continue
} }
if lp.Protocol == "udp" { if lp.Protocol == "udp" {
isIPv6 := conntrack.IsIPv6(svcInfo.clusterIP) isIPv6 := utilnet.IsIPv6(svcInfo.ClusterIP)
conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, clientv1.ProtocolUDP) conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, clientv1.ProtocolUDP)
} }
replacementPortsMap[lp] = socket replacementPortsMap[lp] = socket
@ -1133,10 +1009,10 @@ func (proxier *Proxier) syncProxyRules() {
// Nodeports need SNAT, unless they're local. // Nodeports need SNAT, unless they're local.
// ipset call // ipset call
if !svcInfo.onlyNodeLocalEndpoints { if !svcInfo.OnlyNodeLocalEndpoints {
entry = &utilipset.Entry{ entry = &utilipset.Entry{
// No need to provide ip info // No need to provide ip info
Port: svcInfo.nodePort, Port: svcInfo.NodePort,
Protocol: protocol, Protocol: protocol,
SetType: utilipset.BitmapPort, SetType: utilipset.BitmapPort,
} }
@ -1182,18 +1058,18 @@ func (proxier *Proxier) syncProxyRules() {
// ipvs call // ipvs call
serv := &utilipvs.VirtualServer{ serv := &utilipvs.VirtualServer{
Address: nodeIP, Address: nodeIP,
Port: uint16(svcInfo.nodePort), Port: uint16(svcInfo.NodePort),
Protocol: string(svcInfo.protocol), Protocol: string(svcInfo.Protocol),
Scheduler: proxier.ipvsScheduler, Scheduler: proxier.ipvsScheduler,
} }
if svcInfo.sessionAffinityType == api.ServiceAffinityClientIP { if svcInfo.SessionAffinityType == api.ServiceAffinityClientIP {
serv.Flags |= utilipvs.FlagPersistent serv.Flags |= utilipvs.FlagPersistent
serv.Timeout = uint32(svcInfo.stickyMaxAgeSeconds) serv.Timeout = uint32(svcInfo.StickyMaxAgeSeconds)
} }
// There is no need to bind Node IP to dummy interface, so set parameter `bindAddr` to `false`. // There is no need to bind Node IP to dummy interface, so set parameter `bindAddr` to `false`.
if err := proxier.syncService(svcNameString, serv, false); err == nil { if err := proxier.syncService(svcNameString, serv, false); err == nil {
activeIPVSServices[serv.String()] = true activeIPVSServices[serv.String()] = true
if err := proxier.syncEndpoint(svcName, svcInfo.onlyNodeLocalEndpoints, serv); err != nil { if err := proxier.syncEndpoint(svcName, svcInfo.OnlyNodeLocalEndpoints, serv); err != nil {
glog.Errorf("Failed to sync endpoint for service: %v, err: %v", serv, err) glog.Errorf("Failed to sync endpoint for service: %v, err: %v", serv, err)
} }
} else { } else {
@ -1383,9 +1259,9 @@ func (proxier *Proxier) syncProxyRules() {
// This assumes the proxier mutex is held // This assumes the proxier mutex is held
func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) { func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) {
for _, epSvcPair := range connectionMap { for _, epSvcPair := range connectionMap {
if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && svcInfo.Protocol() == api.ProtocolUDP { if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && svcInfo.GetProtocol() == api.ProtocolUDP {
endpointIP := utilproxy.IPPart(epSvcPair.Endpoint) endpointIP := utilproxy.IPPart(epSvcPair.Endpoint)
err := conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIP(), endpointIP, clientv1.ProtocolUDP) err := conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIPString(), endpointIP, clientv1.ProtocolUDP)
if err != nil { if err != nil {
glog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err) glog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err)
} }
@ -1447,14 +1323,9 @@ func (proxier *Proxier) syncEndpoint(svcPortName proxy.ServicePortName, onlyNode
curEndpoints.Insert(des.String()) curEndpoints.Insert(des.String())
} }
for _, eps := range proxier.endpointsMap[svcPortName] { for _, epInfo := range proxier.endpointsMap[svcPortName] {
epInfo, ok := eps.(*endpointsInfo) if !onlyNodeLocalEndpoints || onlyNodeLocalEndpoints && epInfo.GetIsLocal() {
if !ok { newEndpoints.Insert(epInfo.String())
glog.Errorf("Failed to cast endpointsInfo")
continue
}
if !onlyNodeLocalEndpoints || onlyNodeLocalEndpoints && epInfo.isLocal {
newEndpoints.Insert(epInfo.endpoint)
} }
} }

View File

@ -33,7 +33,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing" netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing"
proxyutil "k8s.io/kubernetes/pkg/proxy/util" utilproxy "k8s.io/kubernetes/pkg/proxy/util"
proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing" proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing"
utilipset "k8s.io/kubernetes/pkg/util/ipset" utilipset "k8s.io/kubernetes/pkg/util/ipset"
ipsettest "k8s.io/kubernetes/pkg/util/ipset/testing" ipsettest "k8s.io/kubernetes/pkg/util/ipset/testing"
@ -67,12 +67,12 @@ func newFakeHealthChecker() *fakeHealthChecker {
// fakePortOpener implements portOpener. // fakePortOpener implements portOpener.
type fakePortOpener struct { type fakePortOpener struct {
openPorts []*proxyutil.LocalPort openPorts []*utilproxy.LocalPort
} }
// OpenLocalPort fakes out the listen() and bind() used by syncProxyRules // OpenLocalPort fakes out the listen() and bind() used by syncProxyRules
// to lock a local port. // to lock a local port.
func (f *fakePortOpener) OpenLocalPort(lp *proxyutil.LocalPort) (proxyutil.Closeable, error) { func (f *fakePortOpener) OpenLocalPort(lp *utilproxy.LocalPort) (utilproxy.Closeable, error) {
f.openPorts = append(f.openPorts, lp) f.openPorts = append(f.openPorts, lp)
return nil, nil return nil, nil
} }
@ -121,16 +121,16 @@ func NewFakeProxier(ipt utiliptables.Interface, ipvs utilipvs.Interface, ipset u
return &Proxier{ return &Proxier{
exec: fexec, exec: fexec,
serviceMap: make(proxy.ServiceMap), serviceMap: make(proxy.ServiceMap),
serviceChanges: proxy.NewServiceChangeTracker(), serviceChanges: proxy.NewServiceChangeTracker(newServiceInfo, nil, nil),
endpointsMap: make(proxy.EndpointsMap), endpointsMap: make(proxy.EndpointsMap),
endpointsChanges: proxy.NewEndpointChangeTracker(testHostname), endpointsChanges: proxy.NewEndpointChangeTracker(testHostname, nil, nil, nil),
iptables: ipt, iptables: ipt,
ipvs: ipvs, ipvs: ipvs,
ipset: ipset, ipset: ipset,
clusterCIDR: "10.0.0.0/24", clusterCIDR: "10.0.0.0/24",
hostname: testHostname, hostname: testHostname,
portsMap: make(map[proxyutil.LocalPort]proxyutil.Closeable), portsMap: make(map[utilproxy.LocalPort]utilproxy.Closeable),
portMapper: &fakePortOpener{[]*proxyutil.LocalPort{}}, portMapper: &fakePortOpener{[]*utilproxy.LocalPort{}},
healthChecker: newFakeHealthChecker(), healthChecker: newFakeHealthChecker(),
ipvsScheduler: DefaultScheduler, ipvsScheduler: DefaultScheduler,
ipGetter: &fakeIPGetter{nodeIPs: nodeIPs}, ipGetter: &fakeIPGetter{nodeIPs: nodeIPs},
@ -1581,15 +1581,15 @@ func Test_updateEndpointsMap(t *testing.T) {
// or non-nil) and must be of equal length. // or non-nil) and must be of equal length.
previousEndpoints []*api.Endpoints previousEndpoints []*api.Endpoints
currentEndpoints []*api.Endpoints currentEndpoints []*api.Endpoints
oldEndpoints map[proxy.ServicePortName][]*endpointsInfo oldEndpoints map[proxy.ServicePortName][]*proxy.BaseEndpointInfo
expectedResult map[proxy.ServicePortName][]*endpointsInfo expectedResult map[proxy.ServicePortName][]*proxy.BaseEndpointInfo
expectedStaleEndpoints []proxy.ServiceEndpoint expectedStaleEndpoints []proxy.ServiceEndpoint
expectedStaleServiceNames map[proxy.ServicePortName]bool expectedStaleServiceNames map[proxy.ServicePortName]bool
expectedHealthchecks map[types.NamespacedName]int expectedHealthchecks map[types.NamespacedName]int
}{{ }{{
// Case[0]: nothing // Case[0]: nothing
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{}, oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{}, expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{},
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
expectedStaleServiceNames: map[proxy.ServicePortName]bool{}, expectedStaleServiceNames: map[proxy.ServicePortName]bool{},
expectedHealthchecks: map[types.NamespacedName]int{}, expectedHealthchecks: map[types.NamespacedName]int{},
@ -1601,14 +1601,14 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPort), makeTestEndpoints("ns1", "ep1", unnamedPort),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1622,14 +1622,14 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortLocal), makeTestEndpoints("ns1", "ep1", namedPortLocal),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1645,20 +1645,20 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsets), makeTestEndpoints("ns1", "ep1", multipleSubsets),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1672,26 +1672,26 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsMultiplePortsLocal), makeTestEndpoints("ns1", "ep1", multipleSubsetsMultiplePortsLocal),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: true}, {Endpoint: "1.1.1.1:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: true}, {Endpoint: "1.1.1.1:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1709,56 +1709,56 @@ func Test_updateEndpointsMap(t *testing.T) {
makeTestEndpoints("ns1", "ep1", multipleSubsetsIPsPorts1), makeTestEndpoints("ns1", "ep1", multipleSubsetsIPsPorts1),
makeTestEndpoints("ns2", "ep2", multipleSubsetsIPsPorts2), makeTestEndpoints("ns2", "ep2", multipleSubsetsIPsPorts2),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
{endpoint: "1.1.1.4:13", isLocal: true}, {Endpoint: "1.1.1.4:13", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p14"): { makeServicePortName("ns1", "ep1", "p14"): {
{endpoint: "1.1.1.3:14", isLocal: false}, {Endpoint: "1.1.1.3:14", IsLocal: false},
{endpoint: "1.1.1.4:14", isLocal: true}, {Endpoint: "1.1.1.4:14", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p21"): { makeServicePortName("ns2", "ep2", "p21"): {
{endpoint: "2.2.2.1:21", isLocal: false}, {Endpoint: "2.2.2.1:21", IsLocal: false},
{endpoint: "2.2.2.2:21", isLocal: true}, {Endpoint: "2.2.2.2:21", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.1:22", isLocal: false}, {Endpoint: "2.2.2.1:22", IsLocal: false},
{endpoint: "2.2.2.2:22", isLocal: true}, {Endpoint: "2.2.2.2:22", IsLocal: true},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p13"): { makeServicePortName("ns1", "ep1", "p13"): {
{endpoint: "1.1.1.3:13", isLocal: false}, {Endpoint: "1.1.1.3:13", IsLocal: false},
{endpoint: "1.1.1.4:13", isLocal: true}, {Endpoint: "1.1.1.4:13", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p14"): { makeServicePortName("ns1", "ep1", "p14"): {
{endpoint: "1.1.1.3:14", isLocal: false}, {Endpoint: "1.1.1.3:14", IsLocal: false},
{endpoint: "1.1.1.4:14", isLocal: true}, {Endpoint: "1.1.1.4:14", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p21"): { makeServicePortName("ns2", "ep2", "p21"): {
{endpoint: "2.2.2.1:21", isLocal: false}, {Endpoint: "2.2.2.1:21", IsLocal: false},
{endpoint: "2.2.2.2:21", isLocal: true}, {Endpoint: "2.2.2.2:21", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.1:22", isLocal: false}, {Endpoint: "2.2.2.1:22", IsLocal: false},
{endpoint: "2.2.2.2:22", isLocal: true}, {Endpoint: "2.2.2.2:22", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1775,10 +1775,10 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPortLocal), makeTestEndpoints("ns1", "ep1", unnamedPortLocal),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{}, oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1796,12 +1796,12 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
nil, nil,
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: true}, {Endpoint: "1.1.1.1:11", IsLocal: true},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{}, expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{},
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
Endpoint: "1.1.1.1:11", Endpoint: "1.1.1.1:11",
ServicePortName: makeServicePortName("ns1", "ep1", ""), ServicePortName: makeServicePortName("ns1", "ep1", ""),
@ -1816,19 +1816,19 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortsLocalNoLocal), makeTestEndpoints("ns1", "ep1", namedPortsLocalNoLocal),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1846,19 +1846,19 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort), makeTestEndpoints("ns1", "ep1", namedPort),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.2:11", isLocal: true}, {Endpoint: "1.1.1.2:11", IsLocal: true},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.1:12", isLocal: false}, {Endpoint: "1.1.1.1:12", IsLocal: false},
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -1881,17 +1881,17 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", multipleSubsetsWithLocal), makeTestEndpoints("ns1", "ep1", multipleSubsetsWithLocal),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: true}, {Endpoint: "1.1.1.2:12", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -1909,17 +1909,17 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPort), makeTestEndpoints("ns1", "ep1", namedPort),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -1936,14 +1936,14 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortRenamed), makeTestEndpoints("ns1", "ep1", namedPortRenamed),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11-2"): { makeServicePortName("ns1", "ep1", "p11-2"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -1962,14 +1962,14 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", namedPortRenumbered), makeTestEndpoints("ns1", "ep1", namedPortRenumbered),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:22", isLocal: false}, {Endpoint: "1.1.1.1:22", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -1992,41 +1992,41 @@ func Test_updateEndpointsMap(t *testing.T) {
makeTestEndpoints("ns3", "ep3", complexAfter3), makeTestEndpoints("ns3", "ep3", complexAfter3),
makeTestEndpoints("ns4", "ep4", complexAfter4), makeTestEndpoints("ns4", "ep4", complexAfter4),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{ oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
makeServicePortName("ns2", "ep2", "p22"): { makeServicePortName("ns2", "ep2", "p22"): {
{endpoint: "2.2.2.2:22", isLocal: true}, {Endpoint: "2.2.2.2:22", IsLocal: true},
{endpoint: "2.2.2.22:22", isLocal: true}, {Endpoint: "2.2.2.22:22", IsLocal: true},
}, },
makeServicePortName("ns2", "ep2", "p23"): { makeServicePortName("ns2", "ep2", "p23"): {
{endpoint: "2.2.2.3:23", isLocal: true}, {Endpoint: "2.2.2.3:23", IsLocal: true},
}, },
makeServicePortName("ns4", "ep4", "p44"): { makeServicePortName("ns4", "ep4", "p44"): {
{endpoint: "4.4.4.4:44", isLocal: true}, {Endpoint: "4.4.4.4:44", IsLocal: true},
{endpoint: "4.4.4.5:44", isLocal: true}, {Endpoint: "4.4.4.5:44", IsLocal: true},
}, },
makeServicePortName("ns4", "ep4", "p45"): { makeServicePortName("ns4", "ep4", "p45"): {
{endpoint: "4.4.4.6:45", isLocal: true}, {Endpoint: "4.4.4.6:45", IsLocal: true},
}, },
}, },
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", "p11"): { makeServicePortName("ns1", "ep1", "p11"): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
{endpoint: "1.1.1.11:11", isLocal: false}, {Endpoint: "1.1.1.11:11", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p12"): { makeServicePortName("ns1", "ep1", "p12"): {
{endpoint: "1.1.1.2:12", isLocal: false}, {Endpoint: "1.1.1.2:12", IsLocal: false},
}, },
makeServicePortName("ns1", "ep1", "p122"): { makeServicePortName("ns1", "ep1", "p122"): {
{endpoint: "1.1.1.2:122", isLocal: false}, {Endpoint: "1.1.1.2:122", IsLocal: false},
}, },
makeServicePortName("ns3", "ep3", "p33"): { makeServicePortName("ns3", "ep3", "p33"): {
{endpoint: "3.3.3.3:33", isLocal: false}, {Endpoint: "3.3.3.3:33", IsLocal: false},
}, },
makeServicePortName("ns4", "ep4", "p44"): { makeServicePortName("ns4", "ep4", "p44"): {
{endpoint: "4.4.4.4:44", isLocal: true}, {Endpoint: "4.4.4.4:44", IsLocal: true},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{{ expectedStaleEndpoints: []proxy.ServiceEndpoint{{
@ -2061,10 +2061,10 @@ func Test_updateEndpointsMap(t *testing.T) {
currentEndpoints: []*api.Endpoints{ currentEndpoints: []*api.Endpoints{
makeTestEndpoints("ns1", "ep1", unnamedPort), makeTestEndpoints("ns1", "ep1", unnamedPort),
}, },
oldEndpoints: map[proxy.ServicePortName][]*endpointsInfo{}, oldEndpoints: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{},
expectedResult: map[proxy.ServicePortName][]*endpointsInfo{ expectedResult: map[proxy.ServicePortName][]*proxy.BaseEndpointInfo{
makeServicePortName("ns1", "ep1", ""): { makeServicePortName("ns1", "ep1", ""): {
{endpoint: "1.1.1.1:11", isLocal: false}, {Endpoint: "1.1.1.1:11", IsLocal: false},
}, },
}, },
expectedStaleEndpoints: []proxy.ServiceEndpoint{}, expectedStaleEndpoints: []proxy.ServiceEndpoint{},
@ -2148,7 +2148,7 @@ func Test_updateEndpointsMap(t *testing.T) {
} }
} }
func compareEndpointsMaps(t *testing.T, tci int, newMap proxy.EndpointsMap, expected map[proxy.ServicePortName][]*endpointsInfo) { func compareEndpointsMaps(t *testing.T, tci int, newMap proxy.EndpointsMap, expected map[proxy.ServicePortName][]*proxy.BaseEndpointInfo) {
if len(newMap) != len(expected) { if len(newMap) != len(expected) {
t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap) t.Errorf("[%d] expected %d results, got %d: %v", tci, len(expected), len(newMap), newMap)
} }
@ -2157,9 +2157,9 @@ func compareEndpointsMaps(t *testing.T, tci int, newMap proxy.EndpointsMap, expe
t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x])) t.Errorf("[%d] expected %d endpoints for %v, got %d", tci, len(expected[x]), x, len(newMap[x]))
} else { } else {
for i := range expected[x] { for i := range expected[x] {
newEp, ok := newMap[x][i].(*endpointsInfo) newEp, ok := newMap[x][i].(*proxy.BaseEndpointInfo)
if !ok { if !ok {
t.Errorf("Failed to cast endpointsInfo") t.Errorf("Failed to cast proxy.BaseEndpointInfo")
continue continue
} }
if *newEp != *(expected[x][i]) { if *newEp != *(expected[x][i]) {

View File

@ -17,17 +17,120 @@ limitations under the License.
package proxy package proxy
import ( import (
"fmt"
"net"
"reflect" "reflect"
"strings"
"sync" "sync"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
apiservice "k8s.io/kubernetes/pkg/api/service"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
proxyutil "k8s.io/kubernetes/pkg/proxy/util" "k8s.io/kubernetes/pkg/apis/core/helper"
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
utilnet "k8s.io/kubernetes/pkg/util/net"
) )
// BaseServiceInfo contains base information that defines a service.
// This could be used directly by proxier while processing services,
// or can be used for constructing a more specific ServiceInfo struct
// defined by the proxier if needed.
type BaseServiceInfo struct {
ClusterIP net.IP
Port int
Protocol api.Protocol
NodePort int
LoadBalancerStatus api.LoadBalancerStatus
SessionAffinityType api.ServiceAffinity
StickyMaxAgeSeconds int
ExternalIPs []string
LoadBalancerSourceRanges []string
HealthCheckNodePort int
OnlyNodeLocalEndpoints bool
}
var _ ServicePort = &BaseServiceInfo{}
// String is part of ServicePort interface.
func (info *BaseServiceInfo) String() string {
return fmt.Sprintf("%s:%d/%s", info.ClusterIP, info.Port, info.Protocol)
}
// ClusterIPString is part of ServicePort interface.
func (info *BaseServiceInfo) ClusterIPString() string {
return info.ClusterIP.String()
}
// GetProtocol is part of ServicePort interface.
func (info *BaseServiceInfo) GetProtocol() api.Protocol {
return info.Protocol
}
// GetHealthCheckNodePort is part of ServicePort interface.
func (info *BaseServiceInfo) GetHealthCheckNodePort() int {
return info.HealthCheckNodePort
}
func (sct *ServiceChangeTracker) newBaseServiceInfo(port *api.ServicePort, service *api.Service) *BaseServiceInfo {
onlyNodeLocalEndpoints := false
if apiservice.RequestsOnlyLocalTraffic(service) {
onlyNodeLocalEndpoints = true
}
var stickyMaxAgeSeconds int
if service.Spec.SessionAffinity == api.ServiceAffinityClientIP {
// Kube-apiserver side guarantees SessionAffinityConfig won't be nil when session affinity type is ClientIP
stickyMaxAgeSeconds = int(*service.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds)
}
info := &BaseServiceInfo{
ClusterIP: net.ParseIP(service.Spec.ClusterIP),
Port: int(port.Port),
Protocol: port.Protocol,
NodePort: int(port.NodePort),
// Deep-copy in case the service instance changes
LoadBalancerStatus: *helper.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer),
SessionAffinityType: service.Spec.SessionAffinity,
StickyMaxAgeSeconds: stickyMaxAgeSeconds,
OnlyNodeLocalEndpoints: onlyNodeLocalEndpoints,
}
if sct.isIPv6Mode == nil {
info.ExternalIPs = make([]string, len(service.Spec.ExternalIPs))
info.LoadBalancerSourceRanges = make([]string, len(service.Spec.LoadBalancerSourceRanges))
copy(info.LoadBalancerSourceRanges, service.Spec.LoadBalancerSourceRanges)
copy(info.ExternalIPs, service.Spec.ExternalIPs)
} else {
// Filter out the incorrect IP version case.
// If ExternalIPs and LoadBalancerSourceRanges on service contains incorrect IP versions,
// only filter out the incorrect ones.
var incorrectIPs []string
info.ExternalIPs, incorrectIPs = utilnet.FilterIncorrectIPVersion(service.Spec.ExternalIPs, *sct.isIPv6Mode)
if len(incorrectIPs) > 0 {
utilproxy.LogAndEmitIncorrectIPVersionEvent(sct.recorder, "externalIPs", strings.Join(incorrectIPs, ","), service.Namespace, service.Name, service.UID)
}
info.LoadBalancerSourceRanges, incorrectIPs = utilnet.FilterIncorrectCIDRVersion(service.Spec.LoadBalancerSourceRanges, *sct.isIPv6Mode)
if len(incorrectIPs) > 0 {
utilproxy.LogAndEmitIncorrectIPVersionEvent(sct.recorder, "loadBalancerSourceRanges", strings.Join(incorrectIPs, ","), service.Namespace, service.Name, service.UID)
}
}
if apiservice.NeedsHealthCheck(service) {
p := service.Spec.HealthCheckNodePort
if p == 0 {
glog.Errorf("Service %s/%s has no healthcheck nodeport", service.Namespace, service.Name)
} else {
info.HealthCheckNodePort = int(p)
}
}
return info
}
type makeServicePortFunc func(*api.ServicePort, *api.Service, *BaseServiceInfo) ServicePort
// serviceChange contains all changes to services that happened since proxy rules were synced. For a single object, // serviceChange contains all changes to services that happened since proxy rules were synced. For a single object,
// changes are accumulated, i.e. previous is state from before applying the changes, // changes are accumulated, i.e. previous is state from before applying the changes,
// current is state after applying all of the changes. // current is state after applying all of the changes.
@ -43,12 +146,20 @@ type ServiceChangeTracker struct {
lock sync.Mutex lock sync.Mutex
// items maps a service to its serviceChange. // items maps a service to its serviceChange.
items map[types.NamespacedName]*serviceChange items map[types.NamespacedName]*serviceChange
// makeServiceInfo allows proxier to inject customized information when processing service.
makeServiceInfo makeServicePortFunc
// isIPv6Mode indicates if change tracker is under IPv6/IPv4 mode. Nil means not applicable.
isIPv6Mode *bool
recorder record.EventRecorder
} }
// NewServiceChangeTracker initializes a ServiceChangeTracker // NewServiceChangeTracker initializes a ServiceChangeTracker
func NewServiceChangeTracker() *ServiceChangeTracker { func NewServiceChangeTracker(makeServiceInfo makeServicePortFunc, isIPv6Mode *bool, recorder record.EventRecorder) *ServiceChangeTracker {
return &ServiceChangeTracker{ return &ServiceChangeTracker{
items: make(map[types.NamespacedName]*serviceChange), items: make(map[types.NamespacedName]*serviceChange),
makeServiceInfo: makeServiceInfo,
isIPv6Mode: isIPv6Mode,
recorder: recorder,
} }
} }
@ -60,10 +171,7 @@ func NewServiceChangeTracker() *ServiceChangeTracker {
// - pass <oldService, service> as the <previous, current> pair. // - pass <oldService, service> as the <previous, current> pair.
// Delete item // Delete item
// - pass <service, nil> as the <previous, current> pair. // - pass <service, nil> as the <previous, current> pair.
// func (sct *ServiceChangeTracker) Update(previous, current *api.Service) bool {
// makeServicePort() return a proxy.ServicePort based on the given Service and its ServicePort. We inject makeServicePort()
// so that giving caller side a chance to initialize proxy.ServicePort interface.
func (sct *ServiceChangeTracker) Update(previous, current *api.Service, makeServicePort func(servicePort *api.ServicePort, service *api.Service) ServicePort) bool {
svc := current svc := current
if svc == nil { if svc == nil {
svc = previous svc = previous
@ -80,10 +188,10 @@ func (sct *ServiceChangeTracker) Update(previous, current *api.Service, makeServ
change, exists := sct.items[namespacedName] change, exists := sct.items[namespacedName]
if !exists { if !exists {
change = &serviceChange{} change = &serviceChange{}
change.previous = serviceToServiceMap(previous, makeServicePort) change.previous = sct.serviceToServiceMap(previous)
sct.items[namespacedName] = change sct.items[namespacedName] = change
} }
change.current = serviceToServiceMap(current, makeServicePort) change.current = sct.serviceToServiceMap(current)
// if change.previous equal to change.current, it means no change // if change.previous equal to change.current, it means no change
if reflect.DeepEqual(change.previous, change.current) { if reflect.DeepEqual(change.previous, change.current) {
delete(sct.items, namespacedName) delete(sct.items, namespacedName)
@ -110,36 +218,48 @@ func UpdateServiceMap(serviceMap ServiceMap, changes *ServiceChangeTracker) (res
// computing this incrementally similarly to serviceMap. // computing this incrementally similarly to serviceMap.
result.HCServiceNodePorts = make(map[types.NamespacedName]uint16) result.HCServiceNodePorts = make(map[types.NamespacedName]uint16)
for svcPortName, info := range serviceMap { for svcPortName, info := range serviceMap {
if info.HealthCheckNodePort() != 0 { if info.GetHealthCheckNodePort() != 0 {
result.HCServiceNodePorts[svcPortName.NamespacedName] = uint16(info.HealthCheckNodePort()) result.HCServiceNodePorts[svcPortName.NamespacedName] = uint16(info.GetHealthCheckNodePort())
} }
} }
return result return result
} }
// ServiceMap maps a service to its ServicePort information. // ServiceMap maps a service to its ServicePort.
type ServiceMap map[ServicePortName]ServicePort type ServiceMap map[ServicePortName]ServicePort
// serviceToServiceMap translates a single Service object to a ServiceMap. // serviceToServiceMap translates a single Service object to a ServiceMap.
// makeServicePort() return a proxy.ServicePort based on the given Service and its ServicePort. We inject makeServicePort()
// so that giving caller side a chance to initialize proxy.ServicePort interface.
// //
// NOTE: service object should NOT be modified. // NOTE: service object should NOT be modified.
func serviceToServiceMap(service *api.Service, makeServicePort func(servicePort *api.ServicePort, service *api.Service) ServicePort) ServiceMap { func (sct *ServiceChangeTracker) serviceToServiceMap(service *api.Service) ServiceMap {
if service == nil { if service == nil {
return nil return nil
} }
svcName := types.NamespacedName{Namespace: service.Namespace, Name: service.Name} svcName := types.NamespacedName{Namespace: service.Namespace, Name: service.Name}
if proxyutil.ShouldSkipService(svcName, service) { if utilproxy.ShouldSkipService(svcName, service) {
return nil return nil
} }
if len(service.Spec.ClusterIP) != 0 {
// Filter out the incorrect IP version case.
// If ClusterIP on service has incorrect IP version, service itself will be ignored.
if sct.isIPv6Mode != nil && utilnet.IsIPv6String(service.Spec.ClusterIP) != *sct.isIPv6Mode {
utilproxy.LogAndEmitIncorrectIPVersionEvent(sct.recorder, "clusterIP", service.Spec.ClusterIP, service.Namespace, service.Name, service.UID)
return nil
}
}
serviceMap := make(ServiceMap) serviceMap := make(ServiceMap)
for i := range service.Spec.Ports { for i := range service.Spec.Ports {
servicePort := &service.Spec.Ports[i] servicePort := &service.Spec.Ports[i]
svcPortName := ServicePortName{NamespacedName: svcName, Port: servicePort.Name} svcPortName := ServicePortName{NamespacedName: svcName, Port: servicePort.Name}
serviceMap[svcPortName] = makeServicePort(servicePort, service) baseSvcInfo := sct.newBaseServiceInfo(servicePort, service)
if sct.makeServiceInfo != nil {
serviceMap[svcPortName] = sct.makeServiceInfo(servicePort, service, baseSvcInfo)
} else {
serviceMap[svcPortName] = baseSvcInfo
}
} }
return serviceMap return serviceMap
} }
@ -213,8 +333,8 @@ func (sm *ServiceMap) unmerge(other ServiceMap, UDPStaleClusterIP sets.String) {
info, exists := (*sm)[svcPortName] info, exists := (*sm)[svcPortName]
if exists { if exists {
glog.V(1).Infof("Removing service port %q", svcPortName) glog.V(1).Infof("Removing service port %q", svcPortName)
if info.Protocol() == api.ProtocolUDP { if info.GetProtocol() == api.ProtocolUDP {
UDPStaleClusterIP.Insert(info.ClusterIP()) UDPStaleClusterIP.Insert(info.ClusterIPString())
} }
delete(*sm, svcPortName) delete(*sm, svcPortName)
} else { } else {

View File

@ -17,9 +17,7 @@ limitations under the License.
package proxy package proxy
import ( import (
"fmt"
"net" "net"
"reflect"
"testing" "testing"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
@ -27,59 +25,23 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
apiservice "k8s.io/kubernetes/pkg/api/service" "k8s.io/apimachinery/pkg/util/sets"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
const testHostname = "test-hostname" const testHostname = "test-hostname"
// fake implementation for service info. func makeTestServiceInfo(clusterIP string, port int, protocol string, healthcheckNodePort int, svcInfoFuncs ...func(*BaseServiceInfo)) *BaseServiceInfo {
type fakeServiceInfo struct { info := &BaseServiceInfo{
clusterIP net.IP ClusterIP: net.ParseIP(clusterIP),
port int Port: port,
protocol api.Protocol Protocol: api.Protocol(protocol),
healthCheckNodePort int
}
func (f *fakeServiceInfo) String() string {
return fmt.Sprintf("%s:%d/%s", f.clusterIP, f.port, f.protocol)
}
func (f *fakeServiceInfo) ClusterIP() string {
return f.clusterIP.String()
}
func (f *fakeServiceInfo) Protocol() api.Protocol {
return f.protocol
}
func (f *fakeServiceInfo) HealthCheckNodePort() int {
return f.healthCheckNodePort
}
func makeTestServiceInfo(clusterIP string, port int, protocol string, healthcheckNodePort int) *fakeServiceInfo {
info := &fakeServiceInfo{
clusterIP: net.ParseIP(clusterIP),
port: port,
protocol: api.Protocol(protocol),
} }
if healthcheckNodePort != 0 { if healthcheckNodePort != 0 {
info.healthCheckNodePort = healthcheckNodePort info.HealthCheckNodePort = healthcheckNodePort
} }
return info for _, svcInfoFunc := range svcInfoFuncs {
} svcInfoFunc(info)
func newFakeServiceInfo(servicePort *api.ServicePort, service *api.Service) ServicePort {
info := &fakeServiceInfo{
clusterIP: net.ParseIP(service.Spec.ClusterIP),
port: int(servicePort.Port),
protocol: servicePort.Protocol,
}
if apiservice.NeedsHealthCheck(service) {
p := service.Spec.HealthCheckNodePort
if p != 0 {
info.healthCheckNodePort = int(p)
}
} }
return info return info
} }
@ -120,61 +82,74 @@ func makeServicePortName(ns, name, port string) ServicePortName {
} }
} }
func Test_serviceToServiceMap(t *testing.T) { func TestServiceToServiceMap(t *testing.T) {
svcTracker := NewServiceChangeTracker(nil, nil, nil)
trueVal := true
falseVal := false
testClusterIPv4 := "10.0.0.1"
testExternalIPv4 := "8.8.8.8"
testSourceRangeIPv4 := "0.0.0.0/1"
testClusterIPv6 := "2001:db8:85a3:0:0:8a2e:370:7334"
testExternalIPv6 := "2001:db8:85a3:0:0:8a2e:370:7335"
testSourceRangeIPv6 := "2001:db8::/32"
testCases := []struct { testCases := []struct {
service *api.Service desc string
expected map[ServicePortName]*fakeServiceInfo service *api.Service
expected map[ServicePortName]*BaseServiceInfo
isIPv6Mode *bool
}{ }{
{ {
// Case[0]: nothing desc: "nothing",
service: nil, service: nil,
expected: map[ServicePortName]*fakeServiceInfo{}, expected: map[ServicePortName]*BaseServiceInfo{},
}, },
{ {
// Case[1]: headless service desc: "headless service",
service: makeTestService("ns2", "headless", func(svc *api.Service) { service: makeTestService("ns2", "headless", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeClusterIP svc.Spec.Type = api.ServiceTypeClusterIP
svc.Spec.ClusterIP = api.ClusterIPNone svc.Spec.ClusterIP = api.ClusterIPNone
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0) svc.Spec.Ports = addTestPort(svc.Spec.Ports, "rpc", "UDP", 1234, 0, 0)
}), }),
expected: map[ServicePortName]*fakeServiceInfo{}, expected: map[ServicePortName]*BaseServiceInfo{},
}, },
{ {
// Case[2]: headless service without port desc: "headless service without port",
service: makeTestService("ns2", "headless-without-port", func(svc *api.Service) { service: makeTestService("ns2", "headless-without-port", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeClusterIP svc.Spec.Type = api.ServiceTypeClusterIP
svc.Spec.ClusterIP = api.ClusterIPNone svc.Spec.ClusterIP = api.ClusterIPNone
}), }),
expected: map[ServicePortName]*fakeServiceInfo{}, expected: map[ServicePortName]*BaseServiceInfo{},
}, },
{ {
// Case[3]: cluster ip service desc: "cluster ip service",
service: makeTestService("ns2", "cluster-ip", func(svc *api.Service) { service: makeTestService("ns2", "cluster-ip", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeClusterIP svc.Spec.Type = api.ServiceTypeClusterIP
svc.Spec.ClusterIP = "172.16.55.4" svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p1", "UDP", 1234, 4321, 0) svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p1", "UDP", 1234, 4321, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p2", "UDP", 1235, 5321, 0) svc.Spec.Ports = addTestPort(svc.Spec.Ports, "p2", "UDP", 1235, 5321, 0)
}), }),
expected: map[ServicePortName]*fakeServiceInfo{ expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("ns2", "cluster-ip", "p1"): makeTestServiceInfo("172.16.55.4", 1234, "UDP", 0), makeServicePortName("ns2", "cluster-ip", "p1"): makeTestServiceInfo("172.16.55.4", 1234, "UDP", 0),
makeServicePortName("ns2", "cluster-ip", "p2"): makeTestServiceInfo("172.16.55.4", 1235, "UDP", 0), makeServicePortName("ns2", "cluster-ip", "p2"): makeTestServiceInfo("172.16.55.4", 1235, "UDP", 0),
}, },
}, },
{ {
// Case[4]: nodeport service desc: "nodeport service",
service: makeTestService("ns2", "node-port", func(svc *api.Service) { service: makeTestService("ns2", "node-port", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeNodePort svc.Spec.Type = api.ServiceTypeNodePort
svc.Spec.ClusterIP = "172.16.55.10" svc.Spec.ClusterIP = "172.16.55.10"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port1", "UDP", 345, 678, 0) svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port1", "UDP", 345, 678, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port2", "TCP", 344, 677, 0) svc.Spec.Ports = addTestPort(svc.Spec.Ports, "port2", "TCP", 344, 677, 0)
}), }),
expected: map[ServicePortName]*fakeServiceInfo{ expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("ns2", "node-port", "port1"): makeTestServiceInfo("172.16.55.10", 345, "UDP", 0), makeServicePortName("ns2", "node-port", "port1"): makeTestServiceInfo("172.16.55.10", 345, "UDP", 0),
makeServicePortName("ns2", "node-port", "port2"): makeTestServiceInfo("172.16.55.10", 344, "TCP", 0), makeServicePortName("ns2", "node-port", "port2"): makeTestServiceInfo("172.16.55.10", 344, "TCP", 0),
}, },
}, },
{ {
// Case[5]: load balancer service desc: "load balancer service",
service: makeTestService("ns1", "load-balancer", func(svc *api.Service) { service: makeTestService("ns1", "load-balancer", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeLoadBalancer svc.Spec.Type = api.ServiceTypeLoadBalancer
svc.Spec.ClusterIP = "172.16.55.11" svc.Spec.ClusterIP = "172.16.55.11"
@ -187,13 +162,13 @@ func Test_serviceToServiceMap(t *testing.T) {
}, },
} }
}), }),
expected: map[ServicePortName]*fakeServiceInfo{ expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("ns1", "load-balancer", "port3"): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0), makeServicePortName("ns1", "load-balancer", "port3"): makeTestServiceInfo("172.16.55.11", 8675, "UDP", 0),
makeServicePortName("ns1", "load-balancer", "port4"): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0), makeServicePortName("ns1", "load-balancer", "port4"): makeTestServiceInfo("172.16.55.11", 8676, "UDP", 0),
}, },
}, },
{ {
// Case[6]: load balancer service with only local traffic policy desc: "load balancer service with only local traffic policy",
service: makeTestService("ns1", "only-local-load-balancer", func(svc *api.Service) { service: makeTestService("ns1", "only-local-load-balancer", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeLoadBalancer svc.Spec.Type = api.ServiceTypeLoadBalancer
svc.Spec.ClusterIP = "172.16.55.12" svc.Spec.ClusterIP = "172.16.55.12"
@ -208,34 +183,192 @@ func Test_serviceToServiceMap(t *testing.T) {
svc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal svc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyTypeLocal
svc.Spec.HealthCheckNodePort = 345 svc.Spec.HealthCheckNodePort = 345
}), }),
expected: map[ServicePortName]*fakeServiceInfo{ expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("ns1", "only-local-load-balancer", "portx"): makeTestServiceInfo("172.16.55.12", 8677, "UDP", 345), makeServicePortName("ns1", "only-local-load-balancer", "portx"): makeTestServiceInfo("172.16.55.12", 8677, "UDP", 345),
makeServicePortName("ns1", "only-local-load-balancer", "porty"): makeTestServiceInfo("172.16.55.12", 8678, "UDP", 345), makeServicePortName("ns1", "only-local-load-balancer", "porty"): makeTestServiceInfo("172.16.55.12", 8678, "UDP", 345),
}, },
}, },
{ {
// Case[7]: external name service desc: "external name service",
service: makeTestService("ns2", "external-name", func(svc *api.Service) { service: makeTestService("ns2", "external-name", func(svc *api.Service) {
svc.Spec.Type = api.ServiceTypeExternalName svc.Spec.Type = api.ServiceTypeExternalName
svc.Spec.ClusterIP = "172.16.55.4" // Should be ignored svc.Spec.ClusterIP = "172.16.55.4" // Should be ignored
svc.Spec.ExternalName = "foo2.bar.com" svc.Spec.ExternalName = "foo2.bar.com"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "portz", "UDP", 1235, 5321, 0) svc.Spec.Ports = addTestPort(svc.Spec.Ports, "portz", "UDP", 1235, 5321, 0)
}), }),
expected: map[ServicePortName]*fakeServiceInfo{}, expected: map[ServicePortName]*BaseServiceInfo{},
},
{
desc: "service with ipv6 clusterIP under ipv4 mode, service should be filtered",
service: &api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "invalidIPv6InIPV4Mode",
Namespace: "test",
},
Spec: api.ServiceSpec{
ClusterIP: testClusterIPv6,
Ports: []api.ServicePort{
{
Name: "testPort",
Port: int32(12345),
Protocol: api.ProtocolTCP,
},
},
},
},
isIPv6Mode: &falseVal,
},
{
desc: "service with ipv4 clusterIP under ipv6 mode, service should be filtered",
service: &api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "invalidIPv4InIPV6Mode",
Namespace: "test",
},
Spec: api.ServiceSpec{
ClusterIP: testClusterIPv4,
Ports: []api.ServicePort{
{
Name: "testPort",
Port: int32(12345),
Protocol: api.ProtocolTCP,
},
},
},
},
isIPv6Mode: &trueVal,
},
{
desc: "service with ipv4 configurations under ipv4 mode",
service: &api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "validIPv4",
Namespace: "test",
},
Spec: api.ServiceSpec{
ClusterIP: testClusterIPv4,
ExternalIPs: []string{testExternalIPv4},
LoadBalancerSourceRanges: []string{testSourceRangeIPv4},
Ports: []api.ServicePort{
{
Name: "testPort",
Port: int32(12345),
Protocol: api.ProtocolTCP,
},
},
},
},
expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("test", "validIPv4", "testPort"): makeTestServiceInfo(testClusterIPv4, 12345, "TCP", 0, func(info *BaseServiceInfo) {
info.ExternalIPs = []string{testExternalIPv4}
info.LoadBalancerSourceRanges = []string{testSourceRangeIPv4}
}),
},
isIPv6Mode: &falseVal,
},
{
desc: "service with ipv6 configurations under ipv6 mode",
service: &api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "validIPv6",
Namespace: "test",
},
Spec: api.ServiceSpec{
ClusterIP: testClusterIPv6,
ExternalIPs: []string{testExternalIPv6},
LoadBalancerSourceRanges: []string{testSourceRangeIPv6},
Ports: []api.ServicePort{
{
Name: "testPort",
Port: int32(12345),
Protocol: api.ProtocolTCP,
},
},
},
},
expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("test", "validIPv6", "testPort"): makeTestServiceInfo(testClusterIPv6, 12345, "TCP", 0, func(info *BaseServiceInfo) {
info.ExternalIPs = []string{testExternalIPv6}
info.LoadBalancerSourceRanges = []string{testSourceRangeIPv6}
}),
},
isIPv6Mode: &trueVal,
},
{
desc: "service with both ipv4 and ipv6 configurations under ipv4 mode, ipv6 fields should be filtered",
service: &api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "filterIPv6InIPV4Mode",
Namespace: "test",
},
Spec: api.ServiceSpec{
ClusterIP: testClusterIPv4,
ExternalIPs: []string{testExternalIPv4, testExternalIPv6},
LoadBalancerSourceRanges: []string{testSourceRangeIPv4, testSourceRangeIPv6},
Ports: []api.ServicePort{
{
Name: "testPort",
Port: int32(12345),
Protocol: api.ProtocolTCP,
},
},
},
},
expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("test", "filterIPv6InIPV4Mode", "testPort"): makeTestServiceInfo(testClusterIPv4, 12345, "TCP", 0, func(info *BaseServiceInfo) {
info.ExternalIPs = []string{testExternalIPv4}
info.LoadBalancerSourceRanges = []string{testSourceRangeIPv4}
}),
},
isIPv6Mode: &falseVal,
},
{
desc: "service with both ipv4 and ipv6 configurations under ipv6 mode, ipv4 fields should be filtered",
service: &api.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "filterIPv4InIPV6Mode",
Namespace: "test",
},
Spec: api.ServiceSpec{
ClusterIP: testClusterIPv6,
ExternalIPs: []string{testExternalIPv4, testExternalIPv6},
LoadBalancerSourceRanges: []string{testSourceRangeIPv4, testSourceRangeIPv6},
Ports: []api.ServicePort{
{
Name: "testPort",
Port: int32(12345),
Protocol: api.ProtocolTCP,
},
},
},
},
expected: map[ServicePortName]*BaseServiceInfo{
makeServicePortName("test", "filterIPv4InIPV6Mode", "testPort"): makeTestServiceInfo(testClusterIPv6, 12345, "TCP", 0, func(info *BaseServiceInfo) {
info.ExternalIPs = []string{testExternalIPv6}
info.LoadBalancerSourceRanges = []string{testSourceRangeIPv6}
}),
},
isIPv6Mode: &trueVal,
}, },
} }
for tci, tc := range testCases { for _, tc := range testCases {
svcTracker.isIPv6Mode = tc.isIPv6Mode
// outputs // outputs
newServices := serviceToServiceMap(tc.service, newFakeServiceInfo) newServices := svcTracker.serviceToServiceMap(tc.service)
if len(newServices) != len(tc.expected) { if len(newServices) != len(tc.expected) {
t.Errorf("[%d] expected %d new, got %d: %v", tci, len(tc.expected), len(newServices), spew.Sdump(newServices)) t.Errorf("[%s] expected %d new, got %d: %v", tc.desc, len(tc.expected), len(newServices), spew.Sdump(newServices))
} }
for x := range tc.expected { for svcKey, expectedInfo := range tc.expected {
svc := newServices[x].(*fakeServiceInfo) svcInfo := newServices[svcKey].(*BaseServiceInfo)
if !reflect.DeepEqual(svc, tc.expected[x]) { if !svcInfo.ClusterIP.Equal(expectedInfo.ClusterIP) ||
t.Errorf("[%d] expected new[%v]to be %v, got %v", tci, x, tc.expected[x], *svc) svcInfo.Port != expectedInfo.Port ||
svcInfo.Protocol != expectedInfo.Protocol ||
svcInfo.HealthCheckNodePort != expectedInfo.HealthCheckNodePort ||
!sets.NewString(svcInfo.ExternalIPs...).Equal(sets.NewString(expectedInfo.ExternalIPs...)) ||
!sets.NewString(svcInfo.LoadBalancerSourceRanges...).Equal(sets.NewString(expectedInfo.LoadBalancerSourceRanges...)) {
t.Errorf("[%s] expected new[%v]to be %v, got %v", tc.desc, svcKey, expectedInfo, *svcInfo)
} }
} }
} }
@ -252,9 +385,9 @@ type FakeProxier struct {
func newFakeProxier() *FakeProxier { func newFakeProxier() *FakeProxier {
return &FakeProxier{ return &FakeProxier{
serviceMap: make(ServiceMap), serviceMap: make(ServiceMap),
serviceChanges: NewServiceChangeTracker(), serviceChanges: NewServiceChangeTracker(nil, nil, nil),
endpointsMap: make(EndpointsMap), endpointsMap: make(EndpointsMap),
endpointsChanges: NewEndpointChangeTracker(testHostname), endpointsChanges: NewEndpointChangeTracker(testHostname, nil, nil, nil),
} }
} }
@ -265,30 +398,15 @@ func makeServiceMap(fake *FakeProxier, allServices ...*api.Service) {
} }
func (fake *FakeProxier) addService(service *api.Service) { func (fake *FakeProxier) addService(service *api.Service) {
fake.serviceChanges.Update(nil, service, makeServicePort) fake.serviceChanges.Update(nil, service)
} }
func (fake *FakeProxier) updateService(oldService *api.Service, service *api.Service) { func (fake *FakeProxier) updateService(oldService *api.Service, service *api.Service) {
fake.serviceChanges.Update(oldService, service, makeServicePort) fake.serviceChanges.Update(oldService, service)
} }
func (fake *FakeProxier) deleteService(service *api.Service) { func (fake *FakeProxier) deleteService(service *api.Service) {
fake.serviceChanges.Update(service, nil, makeServicePort) fake.serviceChanges.Update(service, nil)
}
func makeServicePort(port *api.ServicePort, service *api.Service) ServicePort {
info := &fakeServiceInfo{
clusterIP: net.ParseIP(service.Spec.ClusterIP),
port: int(port.Port),
protocol: port.Protocol,
}
if apiservice.NeedsHealthCheck(service) {
p := service.Spec.HealthCheckNodePort
if p != 0 {
info.healthCheckNodePort = int(p)
}
}
return info
} }
func TestUpdateServiceMapHeadless(t *testing.T) { func TestUpdateServiceMapHeadless(t *testing.T) {

View File

@ -48,23 +48,26 @@ func (spn ServicePortName) String() string {
type ServicePort interface { type ServicePort interface {
// String returns service string. An example format can be: `IP:Port/Protocol`. // String returns service string. An example format can be: `IP:Port/Protocol`.
String() string String() string
// ClusterIP returns service cluster IP. // ClusterIPString returns service cluster IP in string format.
ClusterIP() string ClusterIPString() string
// Protocol returns service protocol. // GetProtocol returns service protocol.
Protocol() api.Protocol GetProtocol() api.Protocol
// HealthCheckNodePort returns service health check node port if present. If return 0, it means not present. // GetHealthCheckNodePort returns service health check node port if present. If return 0, it means not present.
HealthCheckNodePort() int GetHealthCheckNodePort() int
} }
// Endpoint in an interface which abstracts information about an endpoint. // Endpoint in an interface which abstracts information about an endpoint.
// TODO: Rename functions to be consistent with ServicePort.
type Endpoint interface { type Endpoint interface {
// String returns endpoint string. An example format can be: `IP:Port`. // String returns endpoint string. An example format can be: `IP:Port`.
// We take the returned value as ServiceEndpoint.Endpoint. // We take the returned value as ServiceEndpoint.Endpoint.
String() string String() string
// IsLocal returns true if the endpoint is running in same host as kube-proxy, otherwise returns false. // GetIsLocal returns true if the endpoint is running in same host as kube-proxy, otherwise returns false.
IsLocal() bool GetIsLocal() bool
// IP returns IP part of endpoints. // IP returns IP part of the endpoint.
IP() string IP() string
// Port returns the Port part of the endpoint.
Port() (int, error)
// Equal checks if two endpoints are equal. // Equal checks if two endpoints are equal.
Equal(Endpoint) bool Equal(Endpoint) bool
} }

View File

@ -36,7 +36,7 @@ import (
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
proxyutil "k8s.io/kubernetes/pkg/proxy/util" utilproxy "k8s.io/kubernetes/pkg/proxy/util"
"k8s.io/kubernetes/pkg/util/conntrack" "k8s.io/kubernetes/pkg/util/conntrack"
"k8s.io/kubernetes/pkg/util/iptables" "k8s.io/kubernetes/pkg/util/iptables"
utilexec "k8s.io/utils/exec" utilexec "k8s.io/utils/exec"
@ -588,7 +588,7 @@ func (proxier *Proxier) openPortal(service proxy.ServicePortName, info *ServiceI
} }
func (proxier *Proxier) openOnePortal(portal portal, protocol api.Protocol, proxyIP net.IP, proxyPort int, name proxy.ServicePortName) error { func (proxier *Proxier) openOnePortal(portal portal, protocol api.Protocol, proxyIP net.IP, proxyPort int, name proxy.ServicePortName) error {
if local, err := proxyutil.IsLocalIP(portal.ip.String()); err != nil { if local, err := utilproxy.IsLocalIP(portal.ip.String()); err != nil {
return fmt.Errorf("can't determine if IP %s is local, assuming not: %v", portal.ip, err) return fmt.Errorf("can't determine if IP %s is local, assuming not: %v", portal.ip, err)
} else if local { } else if local {
err := proxier.claimNodePort(portal.ip, portal.port, protocol, name) err := proxier.claimNodePort(portal.ip, portal.port, protocol, name)
@ -767,7 +767,7 @@ func (proxier *Proxier) closePortal(service proxy.ServicePortName, info *Service
func (proxier *Proxier) closeOnePortal(portal portal, protocol api.Protocol, proxyIP net.IP, proxyPort int, name proxy.ServicePortName) []error { func (proxier *Proxier) closeOnePortal(portal portal, protocol api.Protocol, proxyIP net.IP, proxyPort int, name proxy.ServicePortName) []error {
el := []error{} el := []error{}
if local, err := proxyutil.IsLocalIP(portal.ip.String()); err != nil { if local, err := utilproxy.IsLocalIP(portal.ip.String()); err != nil {
el = append(el, fmt.Errorf("can't determine if IP %s is local, assuming not: %v", portal.ip, err)) el = append(el, fmt.Errorf("can't determine if IP %s is local, assuming not: %v", portal.ip, err))
} else if local { } else if local {
if err := proxier.releaseNodePort(portal.ip, portal.port, protocol, name); err != nil { if err := proxier.releaseNodePort(portal.ip, portal.port, protocol, name); err != nil {
@ -967,7 +967,7 @@ func iptablesCommonPortalArgs(destIP net.IP, addPhysicalInterfaceMatch bool, add
} }
if destIP != nil { if destIP != nil {
args = append(args, "-d", proxyutil.ToCIDR(destIP)) args = append(args, "-d", utilproxy.ToCIDR(destIP))
} }
if addPhysicalInterfaceMatch { if addPhysicalInterfaceMatch {

View File

@ -13,10 +13,12 @@ go_library(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library", "//pkg/apis/core/helper:go_default_library",
"//pkg/util/conntrack:go_default_library", "//pkg/util/net:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
], ],
) )

View File

@ -20,11 +20,13 @@ import (
"fmt" "fmt"
"net" "net"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper" "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/util/conntrack" utilnet "k8s.io/kubernetes/pkg/util/net"
"github.com/golang/glog" "github.com/golang/glog"
) )
@ -117,10 +119,10 @@ func GetNodeAddresses(cidrs []string, nw NetworkInterfacer) (sets.String, error)
return nil, fmt.Errorf("error parsing CIDR for interface %s, error: %v", itf.Name, err) return nil, fmt.Errorf("error parsing CIDR for interface %s, error: %v", itf.Name, err)
} }
if ipNet.Contains(ip) { if ipNet.Contains(ip) {
if conntrack.IsIPv6(ip) && !uniqueAddressList.Has(IPv6ZeroCIDR) { if utilnet.IsIPv6(ip) && !uniqueAddressList.Has(IPv6ZeroCIDR) {
uniqueAddressList.Insert(ip.String()) uniqueAddressList.Insert(ip.String())
} }
if !conntrack.IsIPv6(ip) && !uniqueAddressList.Has(IPv4ZeroCIDR) { if !utilnet.IsIPv6(ip) && !uniqueAddressList.Has(IPv4ZeroCIDR) {
uniqueAddressList.Insert(ip.String()) uniqueAddressList.Insert(ip.String())
} }
} }
@ -129,3 +131,18 @@ func GetNodeAddresses(cidrs []string, nw NetworkInterfacer) (sets.String, error)
} }
return uniqueAddressList, nil return uniqueAddressList, nil
} }
// LogAndEmitIncorrectIPVersionEvent logs and emits incorrect IP version event.
func LogAndEmitIncorrectIPVersionEvent(recorder record.EventRecorder, fieldName, fieldValue, svcNamespace, svcName string, svcUID types.UID) {
errMsg := fmt.Sprintf("%s in %s has incorrect IP version", fieldValue, fieldName)
glog.Errorf("%s (service %s/%s).", errMsg, svcNamespace, svcName)
if recorder != nil {
recorder.Eventf(
&v1.ObjectReference{
Kind: "Service",
Name: svcName,
Namespace: svcNamespace,
UID: svcUID,
}, v1.EventTypeWarning, "KubeProxyIncorrectIPVersion", errMsg)
}
}

View File

@ -8,6 +8,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/util/conntrack", importpath = "k8s.io/kubernetes/pkg/util/conntrack",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/util/net:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
@ -20,6 +21,7 @@ go_test(
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/util/net:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library", "//vendor/k8s.io/utils/exec/testing:go_default_library",

View File

@ -18,11 +18,11 @@ package conntrack
import ( import (
"fmt" "fmt"
"net"
"strconv" "strconv"
"strings" "strings"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/utils/exec" "k8s.io/utils/exec"
) )
@ -31,17 +31,6 @@ import (
// NoConnectionToDelete is the error string returned by conntrack when no matching connections are found // NoConnectionToDelete is the error string returned by conntrack when no matching connections are found
const NoConnectionToDelete = "0 flow entries have been deleted" const NoConnectionToDelete = "0 flow entries have been deleted"
// IsIPv6 returns true if the given ip address is a valid ipv6 address
func IsIPv6(netIP net.IP) bool {
return netIP != nil && netIP.To4() == nil
}
// IsIPv6String returns true if the given string is a valid ipv6 address
func IsIPv6String(ip string) bool {
netIP := net.ParseIP(ip)
return IsIPv6(netIP)
}
func protoStr(proto v1.Protocol) string { func protoStr(proto v1.Protocol) string {
return strings.ToLower(string(proto)) return strings.ToLower(string(proto))
} }
@ -56,7 +45,7 @@ func parametersWithFamily(isIPv6 bool, parameters ...string) []string {
// ClearEntriesForIP uses the conntrack tool to delete the conntrack entries // ClearEntriesForIP uses the conntrack tool to delete the conntrack entries
// for the UDP connections specified by the given service IP // for the UDP connections specified by the given service IP
func ClearEntriesForIP(execer exec.Interface, ip string, protocol v1.Protocol) error { func ClearEntriesForIP(execer exec.Interface, ip string, protocol v1.Protocol) error {
parameters := parametersWithFamily(IsIPv6String(ip), "-D", "--orig-dst", ip, "-p", protoStr(protocol)) parameters := parametersWithFamily(utilnet.IsIPv6String(ip), "-D", "--orig-dst", ip, "-p", protoStr(protocol))
err := Exec(execer, parameters...) err := Exec(execer, parameters...)
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) { if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed. // TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
@ -107,7 +96,7 @@ func ClearEntriesForPort(execer exec.Interface, port int, isIPv6 bool, protocol
// ClearEntriesForNAT uses the conntrack tool to delete the conntrack entries // ClearEntriesForNAT uses the conntrack tool to delete the conntrack entries
// for connections specified by the {origin, dest} IP pair. // for connections specified by the {origin, dest} IP pair.
func ClearEntriesForNAT(execer exec.Interface, origin, dest string, protocol v1.Protocol) error { func ClearEntriesForNAT(execer exec.Interface, origin, dest string, protocol v1.Protocol) error {
parameters := parametersWithFamily(IsIPv6String(origin), "-D", "--orig-dst", origin, "--dst-nat", dest, parameters := parametersWithFamily(utilnet.IsIPv6String(origin), "-D", "--orig-dst", origin, "--dst-nat", dest,
"-p", protoStr(protocol)) "-p", protoStr(protocol))
err := Exec(execer, parameters...) err := Exec(execer, parameters...)
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) { if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {

View File

@ -18,11 +18,11 @@ package conntrack
import ( import (
"fmt" "fmt"
"net"
"strings" "strings"
"testing" "testing"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/utils/exec" "k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing" fakeexec "k8s.io/utils/exec/testing"
) )
@ -119,7 +119,7 @@ func TestClearUDPConntrackForIP(t *testing.T) {
if err := ClearEntriesForIP(&fexec, tc.ip, v1.ProtocolUDP); err != nil { if err := ClearEntriesForIP(&fexec, tc.ip, v1.ProtocolUDP); err != nil {
t.Errorf("%s test case:, Unexpected error: %v", tc.name, err) t.Errorf("%s test case:, Unexpected error: %v", tc.name, err)
} }
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.ip) + familyParamStr(IsIPv6String(tc.ip)) expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.ip) + familyParamStr(utilnet.IsIPv6String(tc.ip))
execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ") execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ")
if expectCommand != execCommand { if expectCommand != execCommand {
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand) t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
@ -223,7 +223,7 @@ func TestDeleteUDPConnections(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("%s test case: unexpected error: %v", tc.name, err) t.Errorf("%s test case: unexpected error: %v", tc.name, err)
} }
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.origin, tc.dest) + familyParamStr(IsIPv6String(tc.origin)) expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.origin, tc.dest) + familyParamStr(utilnet.IsIPv6String(tc.origin))
execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ") execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ")
if expectCommand != execCommand { if expectCommand != execCommand {
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand) t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
@ -234,99 +234,3 @@ func TestDeleteUDPConnections(t *testing.T) {
t.Errorf("Expect command executed %d times, but got %d", svcCount, fexec.CommandCalls) t.Errorf("Expect command executed %d times, but got %d", svcCount, fexec.CommandCalls)
} }
} }
func TestIsIPv6String(t *testing.T) {
testCases := []struct {
ip string
expectIPv6 bool
}{
{
ip: "127.0.0.1",
expectIPv6: false,
},
{
ip: "192.168.0.0",
expectIPv6: false,
},
{
ip: "1.2.3.4",
expectIPv6: false,
},
{
ip: "bad ip",
expectIPv6: false,
},
{
ip: "::1",
expectIPv6: true,
},
{
ip: "fd00::600d:f00d",
expectIPv6: true,
},
{
ip: "2001:db8::5",
expectIPv6: true,
},
}
for i := range testCases {
isIPv6 := IsIPv6String(testCases[i].ip)
if isIPv6 != testCases[i].expectIPv6 {
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
}
}
}
func TestIsIPv6(t *testing.T) {
testCases := []struct {
ip net.IP
expectIPv6 bool
}{
{
ip: net.IPv4zero,
expectIPv6: false,
},
{
ip: net.IPv4bcast,
expectIPv6: false,
},
{
ip: net.ParseIP("127.0.0.1"),
expectIPv6: false,
},
{
ip: net.ParseIP("10.20.40.40"),
expectIPv6: false,
},
{
ip: net.ParseIP("172.17.3.0"),
expectIPv6: false,
},
{
ip: nil,
expectIPv6: false,
},
{
ip: net.IPv6loopback,
expectIPv6: true,
},
{
ip: net.IPv6zero,
expectIPv6: true,
},
{
ip: net.ParseIP("fd00::600d:f00d"),
expectIPv6: true,
},
{
ip: net.ParseIP("2001:db8::5"),
expectIPv6: true,
},
}
for i := range testCases {
isIPv6 := IsIPv6(testCases[i].ip)
if isIPv6 != testCases[i].expectIPv6 {
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
}
}
}

View File

@ -1,3 +1,5 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
filegroup( filegroup(
@ -15,3 +17,15 @@ filegroup(
], ],
tags = ["automanaged"], tags = ["automanaged"],
) )
go_library(
name = "go_default_library",
srcs = ["net.go"],
importpath = "k8s.io/kubernetes/pkg/util/net",
)
go_test(
name = "go_default_test",
srcs = ["net_test.go"],
embed = [":go_default_library"],
)

4
pkg/util/net/OWNERS Normal file
View File

@ -0,0 +1,4 @@
reviewers:
- sig-network-reviewers
approvers:
- sig-network-approvers

61
pkg/util/net/net.go Normal file
View File

@ -0,0 +1,61 @@
/*
Copyright 2018 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 net
import (
"net"
)
// IsIPv6 returns if netIP is IPv6.
func IsIPv6(netIP net.IP) bool {
return netIP != nil && netIP.To4() == nil
}
// IsIPv6String returns if ip is IPv6.
func IsIPv6String(ip string) bool {
netIP := net.ParseIP(ip)
return IsIPv6(netIP)
}
// IsIPv6CIDR returns if cidr is IPv6.
// This assumes cidr is a valid CIDR.
func IsIPv6CIDR(cidr string) bool {
ip, _, _ := net.ParseCIDR(cidr)
return IsIPv6(ip)
}
// FilterIncorrectIPVersion filters out the incorrect IP version case from a slice of IP strings.
func FilterIncorrectIPVersion(ipStrings []string, isIPv6Mode bool) ([]string, []string) {
return filterWithCondition(ipStrings, isIPv6Mode, IsIPv6String)
}
// FilterIncorrectCIDRVersion filters out the incorrect IP version case from a slice of CIDR strings.
func FilterIncorrectCIDRVersion(ipStrings []string, isIPv6Mode bool) ([]string, []string) {
return filterWithCondition(ipStrings, isIPv6Mode, IsIPv6CIDR)
}
func filterWithCondition(strs []string, expectedCondition bool, conditionFunc func(string) bool) ([]string, []string) {
var corrects, incorrects []string
for _, str := range strs {
if conditionFunc(str) != expectedCondition {
incorrects = append(incorrects, str)
} else {
corrects = append(corrects, str)
}
}
return corrects, incorrects
}

286
pkg/util/net/net_test.go Normal file
View File

@ -0,0 +1,286 @@
/*
Copyright 2018 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 net
import (
"net"
"reflect"
"testing"
)
func TestIsIPv6String(t *testing.T) {
testCases := []struct {
ip string
expectIPv6 bool
}{
{
ip: "127.0.0.1",
expectIPv6: false,
},
{
ip: "192.168.0.0",
expectIPv6: false,
},
{
ip: "1.2.3.4",
expectIPv6: false,
},
{
ip: "bad ip",
expectIPv6: false,
},
{
ip: "::1",
expectIPv6: true,
},
{
ip: "fd00::600d:f00d",
expectIPv6: true,
},
{
ip: "2001:db8::5",
expectIPv6: true,
},
}
for i := range testCases {
isIPv6 := IsIPv6String(testCases[i].ip)
if isIPv6 != testCases[i].expectIPv6 {
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
}
}
}
func TestIsIPv6(t *testing.T) {
testCases := []struct {
ip net.IP
expectIPv6 bool
}{
{
ip: net.IPv4zero,
expectIPv6: false,
},
{
ip: net.IPv4bcast,
expectIPv6: false,
},
{
ip: net.ParseIP("127.0.0.1"),
expectIPv6: false,
},
{
ip: net.ParseIP("10.20.40.40"),
expectIPv6: false,
},
{
ip: net.ParseIP("172.17.3.0"),
expectIPv6: false,
},
{
ip: nil,
expectIPv6: false,
},
{
ip: net.IPv6loopback,
expectIPv6: true,
},
{
ip: net.IPv6zero,
expectIPv6: true,
},
{
ip: net.ParseIP("fd00::600d:f00d"),
expectIPv6: true,
},
{
ip: net.ParseIP("2001:db8::5"),
expectIPv6: true,
},
}
for i := range testCases {
isIPv6 := IsIPv6(testCases[i].ip)
if isIPv6 != testCases[i].expectIPv6 {
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
}
}
}
func TestIsIPv6CIDR(t *testing.T) {
testCases := []struct {
desc string
cidr string
expectResult bool
}{
{
desc: "ipv4 CIDR 1",
cidr: "10.0.0.0/8",
expectResult: false,
},
{
desc: "ipv4 CIDR 2",
cidr: "192.168.0.0/16",
expectResult: false,
},
{
desc: "ipv6 CIDR 1",
cidr: "::/1",
expectResult: true,
},
{
desc: "ipv6 CIDR 2",
cidr: "2000::/10",
expectResult: true,
},
{
desc: "ipv6 CIDR 3",
cidr: "2001:db8::/32",
expectResult: true,
},
}
for _, tc := range testCases {
res := IsIPv6CIDR(tc.cidr)
if res != tc.expectResult {
t.Errorf("%v: want IsIPv6CIDR=%v, got %v", tc.desc, tc.expectResult, res)
}
}
}
func TestFilterIncorrectIPVersion(t *testing.T) {
testCases := []struct {
desc string
isIPv6 bool
ipStrings []string
expectCorrects []string
expectIncorrects []string
}{
{
desc: "all ipv4 strings in ipv4 mode",
isIPv6: false,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectIncorrects: nil,
},
{
desc: "all ipv6 strings in ipv4 mode",
isIPv6: false,
ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: nil,
expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
},
{
desc: "mixed versions in ipv4 mode",
isIPv6: false,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
},
{
desc: "all ipv4 strings in ipv6 mode",
isIPv6: true,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
expectCorrects: nil,
expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
},
{
desc: "all ipv6 strings in ipv6 mode",
isIPv6: true,
ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectIncorrects: nil,
},
{
desc: "mixed versions in ipv6 mode",
isIPv6: true,
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"},
expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
},
}
for _, tc := range testCases {
corrects, incorrects := FilterIncorrectIPVersion(tc.ipStrings, tc.isIPv6)
if !reflect.DeepEqual(tc.expectCorrects, corrects) {
t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects)
}
if !reflect.DeepEqual(tc.expectIncorrects, incorrects) {
t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects)
}
}
}
func TestFilterIncorrectCIDRVersion(t *testing.T) {
testCases := []struct {
desc string
isIPv6 bool
cidrStrings []string
expectCorrects []string
expectIncorrects []string
}{
{
desc: "all ipv4 strings in ipv4 mode",
isIPv6: false,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectIncorrects: nil,
},
{
desc: "all ipv6 strings in ipv4 mode",
isIPv6: false,
cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: nil,
expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
},
{
desc: "mixed versions in ipv4 mode",
isIPv6: false,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
},
{
desc: "all ipv4 strings in ipv6 mode",
isIPv6: true,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"},
expectCorrects: nil,
expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
},
{
desc: "all ipv6 strings in ipv6 mode",
isIPv6: true,
cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectIncorrects: nil,
},
{
desc: "mixed versions in ipv6 mode",
isIPv6: true,
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
},
}
for _, tc := range testCases {
corrects, incorrects := FilterIncorrectCIDRVersion(tc.cidrStrings, tc.isIPv6)
if !reflect.DeepEqual(tc.expectCorrects, corrects) {
t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects)
}
if !reflect.DeepEqual(tc.expectIncorrects, incorrects) {
t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects)
}
}
}