fix review
Signed-off-by: Patrik Cyvoct <patrik@ptrk.io>
This commit is contained in:
parent
d562b6924a
commit
0153b96ab8
@ -3509,10 +3509,10 @@ type LoadBalancerIngress struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Hostname string
|
Hostname string
|
||||||
|
|
||||||
// RouteType specifies the type of route to use for this ingress
|
// IPMode specifies the IP mode to use for this ingress
|
||||||
// Defaults to `VIP`
|
// Defaults to `VIP` if IP is set, null otherwise
|
||||||
// +optional
|
// +optional
|
||||||
RouteType LoadBalancerRouteType
|
IPMode *LoadBalancerIPMode
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -3520,14 +3520,16 @@ const (
|
|||||||
MaxServiceTopologyKeys = 16
|
MaxServiceTopologyKeys = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadBalancerRouteType represents the type of route available for a LoadBalancer ingress
|
// LoadBalancerIPMode represents the mode of the LoadBalancer ingress IP
|
||||||
type LoadBalancerRouteType string
|
type LoadBalancerIPMode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// LoadBalancerRouteTypeVIP is the type of route used by a LoadBalancer where dstIP = lbIP
|
// LoadBalancerIPModeVIP indicates that the traffic passing through this LoadBalancer
|
||||||
LoadBalancerRouteTypeVIP LoadBalancerRouteType = "VIP"
|
// is delivered with the destination IP set to the specified LoadBalancer IP
|
||||||
// LoadBalancerRouteTypeProxy is the type of route used by a proxy like LoadBalancer
|
LoadBalancerIPModeVIP LoadBalancerIPMode = "VIP"
|
||||||
LoadBalancerRouteTypeProxy LoadBalancerRouteType = "Proxy"
|
// LoadBalancerIPModeProxy indicates that the specified LoadBalancer acts like a proxy,
|
||||||
|
// changing the destination IP to the node IP and the source IP to the LoadBalaner (mostly private) IP
|
||||||
|
LoadBalancerIPModeProxy LoadBalancerIPMode = "Proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPFamily represents the IP Family (IPv4 or IPv6). This type is used
|
// IPFamily represents the IP Family (IPv4 or IPv6). This type is used
|
||||||
|
@ -19,7 +19,7 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/kubernetes/pkg/util/parsers"
|
"k8s.io/kubernetes/pkg/util/parsers"
|
||||||
@ -161,10 +161,17 @@ func SetDefaults_Service(obj *v1.Service) {
|
|||||||
// NOTE: strategy handles cases where ClusterIPs is used (but not ClusterIP).
|
// NOTE: strategy handles cases where ClusterIPs is used (but not ClusterIP).
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// any other defaulting depends on cluster configuration.
|
if utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) &&
|
||||||
// further IPFamilies, IPFamilyPolicy defaulting is in ClusterIP alloc/reserve logic
|
obj.Spec.Type == v1.ServiceTypeLoadBalancer {
|
||||||
// note: conversion logic handles cases where ClusterIPs is used (but not ClusterIP).
|
|
||||||
|
for _, ing := range obj.Status.LoadBalancer.Ingress {
|
||||||
|
if ing.IPMode == nil {
|
||||||
|
ipMode := v1.LoadBalancerIPModeVIP
|
||||||
|
ing.IPMode = &ipMode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func SetDefaults_Pod(obj *v1.Pod) {
|
func SetDefaults_Pod(obj *v1.Pod) {
|
||||||
|
@ -5973,6 +5973,10 @@ func ValidatePodLogOptions(opts *core.PodLogOptions) field.ErrorList {
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
supportedLoadBalancerIPMode = sets.NewString(string(core.LoadBalancerIPModeVIP), string(core.LoadBalancerIPModeProxy))
|
||||||
|
)
|
||||||
|
|
||||||
// ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
|
// ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
|
||||||
func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.Path) field.ErrorList {
|
func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
@ -5983,6 +5987,22 @@ func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.
|
|||||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("ip"), ingress.IP, "must be a valid IP address"))
|
allErrs = append(allErrs, field.Invalid(idxPath.Child("ip"), ingress.IP, "must be a valid IP address"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) {
|
||||||
|
if len(ingress.IP) > 0 && ingress.IPMode != nil {
|
||||||
|
switch *ingress.IPMode {
|
||||||
|
case core.LoadBalancerIPModeVIP, core.LoadBalancerIPModeProxy:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
allErrs = append(allErrs, field.NotSupported(idxPath.Child("ipMode"), ingress.IPMode, supportedLoadBalancerIPMode.List()))
|
||||||
|
}
|
||||||
|
} else if len(ingress.IP) > 0 && ingress.IPMode == nil {
|
||||||
|
allErrs = append(allErrs, field.Required(idxPath.Child("ipMode"), "must be specified when `ip` is set"))
|
||||||
|
} else if len(ingress.IP) == 0 && ingress.IPMode != nil {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(idxPath.Child("ipMode"), "may not be used when `ip` is not set"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(ingress.Hostname) > 0 {
|
if len(ingress.Hostname) > 0 {
|
||||||
for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
|
for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
|
||||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
|
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
|
||||||
@ -5991,6 +6011,7 @@ func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.
|
|||||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
|
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
@ -663,6 +663,11 @@ const (
|
|||||||
//
|
//
|
||||||
// Enables kubelet support to size memory backed volumes
|
// Enables kubelet support to size memory backed volumes
|
||||||
SizeMemoryBackedVolumes featuregate.Feature = "SizeMemoryBackedVolumes"
|
SizeMemoryBackedVolumes featuregate.Feature = "SizeMemoryBackedVolumes"
|
||||||
|
|
||||||
|
// owner: TBD ?
|
||||||
|
// alpha: v1.21
|
||||||
|
// LoadBalancerIPMode enables the IPMode field in the LoadBalancerIngress status of a Service
|
||||||
|
LoadBalancerIPMode featuregate.Feature = "LoadBalancerIPMode"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -763,6 +768,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
HPAContainerMetrics: {Default: false, PreRelease: featuregate.Alpha},
|
HPAContainerMetrics: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
RootCAConfigMap: {Default: true, PreRelease: featuregate.Beta},
|
RootCAConfigMap: {Default: true, PreRelease: featuregate.Beta},
|
||||||
SizeMemoryBackedVolumes: {Default: false, PreRelease: featuregate.Alpha},
|
SizeMemoryBackedVolumes: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
LoadBalancerIPMode: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||||
// unintentionally on either side:
|
// unintentionally on either side:
|
||||||
|
@ -1175,7 +1175,7 @@ func (proxier *Proxier) syncProxyRules() {
|
|||||||
// 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.
|
||||||
|
|
||||||
if ingress.RouteType != v1.LoadBalancerRouteTypeProxy {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) || ingress.IPMode == nil || *ingress.IPMode == v1.LoadBalancerIPModeVIP {
|
||||||
args = append(args[:0],
|
args = append(args[:0],
|
||||||
"-A", string(kubeServicesChain),
|
"-A", string(kubeServicesChain),
|
||||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s loadbalancer IP"`, svcNameString),
|
"-m", "comment", "--comment", fmt.Sprintf(`"%s loadbalancer IP"`, svcNameString),
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -34,6 +35,7 @@ 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"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/proxy"
|
"k8s.io/kubernetes/pkg/proxy"
|
||||||
"k8s.io/kubernetes/pkg/proxy/healthcheck"
|
"k8s.io/kubernetes/pkg/proxy/healthcheck"
|
||||||
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
|
utilproxy "k8s.io/kubernetes/pkg/proxy/util"
|
||||||
@ -46,6 +48,8 @@ import (
|
|||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
fakeexec "k8s.io/utils/exec/testing"
|
fakeexec "k8s.io/utils/exec/testing"
|
||||||
utilpointer "k8s.io/utils/pointer"
|
utilpointer "k8s.io/utils/pointer"
|
||||||
|
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkAllLines(t *testing.T, table utiliptables.Table, save []byte, expectedLines map[utiliptables.Chain]string) {
|
func checkAllLines(t *testing.T, table utiliptables.Table, save []byte, expectedLines map[utiliptables.Chain]string) {
|
||||||
@ -2683,59 +2687,89 @@ COMMIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadBalancerIngressRouteTypeProxy(t *testing.T) {
|
func TestLoadBalancerIngressRouteTypeProxy(t *testing.T) {
|
||||||
ipt := iptablestest.NewFake()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, true)()
|
||||||
fp := NewFakeProxier(ipt, false)
|
ipModeProxy := v1.LoadBalancerIPModeProxy
|
||||||
svcIP := "10.20.30.41"
|
ipModeVIP := v1.LoadBalancerIPModeVIP
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
svcIP string
|
||||||
|
svcLBIP string
|
||||||
|
ipMode *v1.LoadBalancerIPMode
|
||||||
|
expectedRule bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
svcIP: "10.20.30.41",
|
||||||
|
svcLBIP: "1.2.3.4",
|
||||||
|
ipMode: &ipModeProxy,
|
||||||
|
expectedRule: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
svcIP: "10.20.30.42",
|
||||||
|
svcLBIP: "1.2.3.5",
|
||||||
|
ipMode: &ipModeVIP,
|
||||||
|
expectedRule: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
svcIP: "10.20.30.43",
|
||||||
|
svcLBIP: "1.2.3.6",
|
||||||
|
ipMode: nil,
|
||||||
|
expectedRule: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
svcPort := 80
|
svcPort := 80
|
||||||
svcNodePort := 3001
|
svcNodePort := 3001
|
||||||
svcLBIP := "1.2.3.4"
|
|
||||||
svcPortName := proxy.ServicePortName{
|
svcPortName := proxy.ServicePortName{
|
||||||
NamespacedName: makeNSN("ns1", "svc1"),
|
NamespacedName: makeNSN("ns1", "svc1"),
|
||||||
Port: "p80",
|
Port: "p80",
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
}
|
}
|
||||||
|
|
||||||
makeServiceMap(fp,
|
for _, testCase := range testCases {
|
||||||
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
|
ipt := iptablestest.NewFake()
|
||||||
svc.Spec.Type = "LoadBalancer"
|
fp := NewFakeProxier(ipt, false)
|
||||||
svc.Spec.ClusterIP = svcIP
|
makeServiceMap(fp,
|
||||||
svc.Spec.Ports = []v1.ServicePort{{
|
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
|
||||||
Name: svcPortName.Port,
|
svc.Spec.Type = "LoadBalancer"
|
||||||
Port: int32(svcPort),
|
svc.Spec.ClusterIP = testCase.svcIP
|
||||||
Protocol: v1.ProtocolTCP,
|
svc.Spec.Ports = []v1.ServicePort{{
|
||||||
NodePort: int32(svcNodePort),
|
|
||||||
}}
|
|
||||||
svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
|
|
||||||
IP: svcLBIP,
|
|
||||||
RouteType: v1.LoadBalancerRouteTypeProxy,
|
|
||||||
}}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
epIP := "10.180.0.1"
|
|
||||||
makeEndpointsMap(fp,
|
|
||||||
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
|
|
||||||
ept.Subsets = []v1.EndpointSubset{{
|
|
||||||
Addresses: []v1.EndpointAddress{{
|
|
||||||
IP: epIP,
|
|
||||||
}},
|
|
||||||
Ports: []v1.EndpointPort{{
|
|
||||||
Name: svcPortName.Port,
|
Name: svcPortName.Port,
|
||||||
Port: int32(svcPort),
|
Port: int32(svcPort),
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
}},
|
NodePort: int32(svcNodePort),
|
||||||
}}
|
}}
|
||||||
}),
|
svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
|
||||||
)
|
IP: testCase.svcLBIP,
|
||||||
|
IPMode: testCase.ipMode,
|
||||||
|
}}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
fp.syncProxyRules()
|
epIP := "10.180.0.1"
|
||||||
|
makeEndpointsMap(fp,
|
||||||
|
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
|
||||||
|
ept.Subsets = []v1.EndpointSubset{{
|
||||||
|
Addresses: []v1.EndpointAddress{{
|
||||||
|
IP: epIP,
|
||||||
|
}},
|
||||||
|
Ports: []v1.EndpointPort{{
|
||||||
|
Name: svcPortName.Port,
|
||||||
|
Port: int32(svcPort),
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
proto := strings.ToLower(string(v1.ProtocolTCP))
|
fp.syncProxyRules()
|
||||||
fwChain := string(serviceFirewallChainName(svcPortName.String(), proto))
|
|
||||||
|
|
||||||
kubeSvcRules := ipt.GetRules(string(kubeServicesChain))
|
proto := strings.ToLower(string(v1.ProtocolTCP))
|
||||||
if hasJump(kubeSvcRules, fwChain, svcLBIP, svcPort) {
|
fwChain := string(serviceFirewallChainName(svcPortName.String(), proto))
|
||||||
errorf(fmt.Sprintf("Found jump to firewall chain %v", fwChain), kubeSvcRules, t)
|
|
||||||
|
kubeSvcRules := ipt.GetRules(string(kubeServicesChain))
|
||||||
|
if hasJump(kubeSvcRules, fwChain, testCase.svcLBIP, svcPort) != testCase.expectedRule {
|
||||||
|
errorf(fmt.Sprintf("Found jump to firewall chain %v", fwChain), kubeSvcRules, t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1333,7 +1333,7 @@ func (proxier *Proxier) syncProxyRules() {
|
|||||||
|
|
||||||
// Capture load-balancer ingress.
|
// Capture load-balancer ingress.
|
||||||
for _, ingress := range svcInfo.LoadBalancerIngress() {
|
for _, ingress := range svcInfo.LoadBalancerIngress() {
|
||||||
if ingress.IP != "" && ingress.RouteType != v1.LoadBalancerRouteTypeProxy {
|
if ingress.IP != "" && (!utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) || ingress.IPMode == nil || *ingress.IPMode == v1.LoadBalancerIPModeVIP) {
|
||||||
// ipset call
|
// ipset call
|
||||||
entry = &utilipset.Entry{
|
entry = &utilipset.Entry{
|
||||||
IP: ingress.IP,
|
IP: ingress.IP,
|
||||||
|
@ -33,6 +33,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/proxy"
|
"k8s.io/kubernetes/pkg/proxy"
|
||||||
"k8s.io/kubernetes/pkg/proxy/healthcheck"
|
"k8s.io/kubernetes/pkg/proxy/healthcheck"
|
||||||
netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing"
|
netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing"
|
||||||
@ -51,6 +53,8 @@ import (
|
|||||||
utilpointer "k8s.io/utils/pointer"
|
utilpointer "k8s.io/utils/pointer"
|
||||||
|
|
||||||
utilnet "k8s.io/utils/net"
|
utilnet "k8s.io/utils/net"
|
||||||
|
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testHostname = "test-hostname"
|
const testHostname = "test-hostname"
|
||||||
@ -4322,55 +4326,85 @@ func TestFilterCIDRs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadBalancerIngressRouteTypeProxy(t *testing.T) {
|
func TestLoadBalancerIngressRouteTypeProxy(t *testing.T) {
|
||||||
_, fp := buildFakeProxier()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, true)()
|
||||||
svcIP := "10.20.30.41"
|
ipModeProxy := v1.LoadBalancerIPModeProxy
|
||||||
|
ipModeVIP := v1.LoadBalancerIPModeVIP
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
svcIP string
|
||||||
|
svcLBIP string
|
||||||
|
ipMode *v1.LoadBalancerIPMode
|
||||||
|
expectedServices int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
svcIP: "10.20.30.41",
|
||||||
|
svcLBIP: "1.2.3.4",
|
||||||
|
ipMode: &ipModeProxy,
|
||||||
|
expectedServices: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
svcIP: "10.20.30.42",
|
||||||
|
svcLBIP: "1.2.3.5",
|
||||||
|
ipMode: &ipModeVIP,
|
||||||
|
expectedServices: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
svcIP: "10.20.30.43",
|
||||||
|
svcLBIP: "1.2.3.6",
|
||||||
|
ipMode: nil,
|
||||||
|
expectedServices: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
svcPort := 80
|
svcPort := 80
|
||||||
svcNodePort := 3001
|
svcNodePort := 3001
|
||||||
svcLBIP := "1.2.3.4"
|
|
||||||
svcPortName := proxy.ServicePortName{
|
svcPortName := proxy.ServicePortName{
|
||||||
NamespacedName: makeNSN("ns1", "svc1"),
|
NamespacedName: makeNSN("ns1", "svc1"),
|
||||||
Port: "p80",
|
Port: "p80",
|
||||||
}
|
}
|
||||||
|
|
||||||
makeServiceMap(fp,
|
for _, testCase := range testCases {
|
||||||
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
|
_, fp := buildFakeProxier()
|
||||||
svc.Spec.Type = "LoadBalancer"
|
makeServiceMap(fp,
|
||||||
svc.Spec.ClusterIP = svcIP
|
makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) {
|
||||||
svc.Spec.Ports = []v1.ServicePort{{
|
svc.Spec.Type = "LoadBalancer"
|
||||||
Name: svcPortName.Port,
|
svc.Spec.ClusterIP = testCase.svcIP
|
||||||
Port: int32(svcPort),
|
svc.Spec.Ports = []v1.ServicePort{{
|
||||||
Protocol: v1.ProtocolTCP,
|
Name: svcPortName.Port,
|
||||||
NodePort: int32(svcNodePort),
|
Port: int32(svcPort),
|
||||||
}}
|
Protocol: v1.ProtocolTCP,
|
||||||
svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
|
NodePort: int32(svcNodePort),
|
||||||
IP: svcLBIP,
|
}}
|
||||||
RouteType: v1.LoadBalancerRouteTypeProxy,
|
svc.Status.LoadBalancer.Ingress = []v1.LoadBalancerIngress{{
|
||||||
}}
|
IP: testCase.svcLBIP,
|
||||||
}),
|
IPMode: testCase.ipMode,
|
||||||
)
|
}}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
epIP := "10.180.0.1"
|
epIP := "10.180.0.1"
|
||||||
makeEndpointsMap(fp,
|
makeEndpointsMap(fp,
|
||||||
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
|
makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) {
|
||||||
ept.Subsets = []v1.EndpointSubset{{
|
ept.Subsets = []v1.EndpointSubset{{
|
||||||
Addresses: []v1.EndpointAddress{{
|
Addresses: []v1.EndpointAddress{{
|
||||||
IP: epIP,
|
IP: epIP,
|
||||||
}},
|
}},
|
||||||
Ports: []v1.EndpointPort{{
|
Ports: []v1.EndpointPort{{
|
||||||
Name: svcPortName.Port,
|
Name: svcPortName.Port,
|
||||||
Port: int32(svcPort),
|
Port: int32(svcPort),
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
fp.syncProxyRules()
|
fp.syncProxyRules()
|
||||||
|
|
||||||
services, err := fp.ipvs.GetVirtualServers()
|
services, err := fp.ipvs.GetVirtualServers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to get ipvs services, err: %v", err)
|
t.Errorf("Failed to get ipvs services, err: %v", err)
|
||||||
}
|
}
|
||||||
if len(services) != 1 {
|
if len(services) != testCase.expectedServices {
|
||||||
t.Errorf("Expect 1 ipvs services, got %d", len(services))
|
t.Errorf("Expect %d ipvs services, got %d", testCase.expectedServices, len(services))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,22 +164,24 @@ func (sct *ServiceChangeTracker) newBaseServiceInfo(port *v1.ServicePort, servic
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtain Load Balancer Ingress IPs
|
// Obtain Load Balancer Ingress IPs
|
||||||
var ips []string
|
var allIncorrectIPs []string
|
||||||
for _, ing := range service.Status.LoadBalancer.Ingress {
|
for _, ing := range service.Status.LoadBalancer.Ingress {
|
||||||
ips = append(ips, ing.IP)
|
correctIPs, incorrectIPs := utilproxy.FilterIncorrectIPVersion([]string{ing.IP}, sct.ipFamily)
|
||||||
|
|
||||||
|
// len is either 1 or 0
|
||||||
|
if len(correctIPs) == 1 {
|
||||||
|
// Update the LoadBalancerStatus with the filtered IPs
|
||||||
|
info.loadBalancerStatus.Ingress = append(info.loadBalancerStatus.Ingress, ing)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// here len(incorrectIPs) == 1
|
||||||
|
allIncorrectIPs = append(allIncorrectIPs, incorrectIPs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ips) > 0 {
|
if len(incorrectIPs) > 0 {
|
||||||
correctIPs, incorrectIPs := utilproxy.FilterIncorrectIPVersion(ips, sct.ipFamily)
|
klog.V(4).Infof("service change tracker(%v) ignored the following load balancer(%s) ingress ips for service %v/%v as they don't match IPFamily", sct.ipFamily, strings.Join(incorrectIPs, ","), service.Namespace, service.Name)
|
||||||
|
|
||||||
if len(incorrectIPs) > 0 {
|
|
||||||
klog.V(4).Infof("service change tracker(%v) ignored the following load balancer(%s) ingress ips for service %v/%v as they don't match IPFamily", sct.ipFamily, strings.Join(incorrectIPs, ","), service.Namespace, service.Name)
|
|
||||||
|
|
||||||
}
|
|
||||||
// Create the LoadBalancerStatus with the filtered IPs
|
|
||||||
for _, ip := range correctIPs {
|
|
||||||
info.loadBalancerStatus.Ingress = append(info.loadBalancerStatus.Ingress, v1.LoadBalancerIngress{IP: ip})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if apiservice.NeedsHealthCheck(service) {
|
if apiservice.NeedsHealthCheck(service) {
|
||||||
|
@ -179,6 +179,12 @@ func dropServiceDisabledFields(newSvc *api.Service, oldSvc *api.Service) {
|
|||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ServiceTopology) && !topologyKeysInUse(oldSvc) {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.ServiceTopology) && !topologyKeysInUse(oldSvc) {
|
||||||
newSvc.Spec.TopologyKeys = nil
|
newSvc.Spec.TopologyKeys = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) && !loadbalancerIPModeInUse(oldSvc) {
|
||||||
|
for _, ing := range newSvc.Status.LoadBalancer.Ingress {
|
||||||
|
ing.IPMode = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if svc.Spec.ServiceIPFamily field is in use
|
// returns true if svc.Spec.ServiceIPFamily field is in use
|
||||||
@ -202,6 +208,19 @@ func topologyKeysInUse(svc *api.Service) bool {
|
|||||||
return len(svc.Spec.TopologyKeys) > 0
|
return len(svc.Spec.TopologyKeys) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns true is svc.Status.LoadBalancer.Ingress[].IPMode fields are in use
|
||||||
|
func loadbalancerIPModeInUse(svc *api.Service) bool {
|
||||||
|
if svc == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, ing := range svc.Status.LoadBalancer.Ingress {
|
||||||
|
if ing.IPMode != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type serviceStatusStrategy struct {
|
type serviceStatusStrategy struct {
|
||||||
Strategy
|
Strategy
|
||||||
}
|
}
|
||||||
|
@ -3972,10 +3972,10 @@ type LoadBalancerIngress struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Hostname string `json:"hostname,omitempty" protobuf:"bytes,2,opt,name=hostname"`
|
Hostname string `json:"hostname,omitempty" protobuf:"bytes,2,opt,name=hostname"`
|
||||||
|
|
||||||
// RouteType specifies the type of route to use for this ingress
|
// IPMode specifies the IP mode to use for this ingress
|
||||||
// Defaults to `VIP`
|
// Defaults to `VIP` if IP is set, null otherwise
|
||||||
// +optional
|
// +optional
|
||||||
RouteType LoadBalancerRouteType `json:"routeType,omitempty" protobuf:"bytes,3,opt,name=routeType"`
|
IPMode *LoadBalancerIPMode `json:"ipMode,omitempty" protobuf:"bytes,3,opt,name=ipMode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -3983,14 +3983,16 @@ const (
|
|||||||
MaxServiceTopologyKeys = 16
|
MaxServiceTopologyKeys = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadBalancerRouteType represents the type of route available for a LoadBalancer ingress
|
// LoadBalancerIPMode represents the mode of the LoadBalancer ingress IP
|
||||||
type LoadBalancerRouteType string
|
type LoadBalancerIPMode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// LoadBalancerRouteTypeVIP is the type of route used by a LoadBalancer where dstIP = lbIP
|
// LoadBalancerIPModeVIP indicates that the traffic passing through this LoadBalancer
|
||||||
LoadBalancerRouteTypeVIP LoadBalancerRouteType = "VIP"
|
// is delivered with the destination IP set to the specified LoadBalancer IP
|
||||||
// LoadBalancerRouteTypeProxy is the type of route used by a proxy like LoadBalancer
|
LoadBalancerIPModeVIP LoadBalancerIPMode = "VIP"
|
||||||
LoadBalancerRouteTypeProxy LoadBalancerRouteType = "Proxy"
|
// LoadBalancerIPModeProxy indicates that the specified LoadBalancer acts like a proxy,
|
||||||
|
// changing the destination IP to the node IP and the source IP to the LoadBalaner (mostly private) IP
|
||||||
|
LoadBalancerIPModeProxy LoadBalancerIPMode = "Proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPFamily represents the IP Family (IPv4 or IPv6). This type is used
|
// IPFamily represents the IP Family (IPv4 or IPv6). This type is used
|
||||||
|
Loading…
Reference in New Issue
Block a user