dual stack services (#91824)
* api: structure change * api: defaulting, conversion, and validation * [FIX] validation: auto remove second ip/family when service changes to SingleStack * [FIX] api: defaulting, conversion, and validation * api-server: clusterIPs alloc, printers, storage and strategy * [FIX] clusterIPs default on read * alloc: auto remove second ip/family when service changes to SingleStack * api-server: repair loop handling for clusterIPs * api-server: force kubernetes default service into single stack * api-server: tie dualstack feature flag with endpoint feature flag * controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service * [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service * kube-proxy: feature-flag, utils, proxier, and meta proxier * [FIX] kubeproxy: call both proxier at the same time * kubenet: remove forced pod IP sorting * kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy * e2e: fix tests that depends on IPFamily field AND add dual stack tests * e2e: fix expected error message for ClusterIP immutability * add integration tests for dualstack the third phase of dual stack is a very complex change in the API, basically it introduces Dual Stack services. Main changes are: - It pluralizes the Service IPFamily field to IPFamilies, and removes the singular field. - It introduces a new field IPFamilyPolicyType that can take 3 values to express the "dual-stack(mad)ness" of the cluster: SingleStack, PreferDualStack and RequireDualStack - It pluralizes ClusterIP to ClusterIPs. The goal is to add coverage to the services API operations, taking into account the 6 different modes a cluster can have: - single stack: IP4 or IPv6 (as of today) - dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4 * [FIX] add integration tests for dualstack * generated data * generated files Co-authored-by: Antonio Ojea <aojea@redhat.com> Kubernetes-commit: 6675eba3eff1c8e565c4060a9c1396f75da7cc3e
This commit is contained in:
parent
f28a68d5aa
commit
2bf7244939
|
@ -764,11 +764,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/api",
|
"ImportPath": "k8s.io/api",
|
||||||
"Rev": "ccbd11b5dd0a"
|
"Rev": "0415de894212"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery",
|
"ImportPath": "k8s.io/apimachinery",
|
||||||
"Rev": "554eef9dbf66"
|
"Rev": "ab98f4dc11fb"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/cli-runtime",
|
"ImportPath": "k8s.io/cli-runtime",
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -34,8 +34,8 @@ require (
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4
|
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.2.8
|
||||||
k8s.io/api v0.0.0-20201023125151-ccbd11b5dd0a
|
k8s.io/api v0.0.0-20201026202134-0415de894212
|
||||||
k8s.io/apimachinery v0.0.0-20201020200440-554eef9dbf66
|
k8s.io/apimachinery v0.0.0-20201026201559-ab98f4dc11fb
|
||||||
k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb
|
k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb
|
||||||
k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96
|
k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96
|
||||||
k8s.io/component-base v0.0.0-20201020201314-b8c7ac1518d8
|
k8s.io/component-base v0.0.0-20201020201314-b8c7ac1518d8
|
||||||
|
@ -48,8 +48,8 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
k8s.io/api => k8s.io/api v0.0.0-20201023125151-ccbd11b5dd0a
|
k8s.io/api => k8s.io/api v0.0.0-20201026202134-0415de894212
|
||||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20201020200440-554eef9dbf66
|
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20201026201559-ab98f4dc11fb
|
||||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb
|
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb
|
||||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96
|
k8s.io/client-go => k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96
|
||||||
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20201020200306-60862b8acf58
|
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20201020200306-60862b8acf58
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -508,8 +508,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
k8s.io/api v0.0.0-20201023125151-ccbd11b5dd0a/go.mod h1:ZstqwXfkQ55HZmXltK0DHYZaNJbMq4U/mnlyigw1pH0=
|
k8s.io/api v0.0.0-20201026202134-0415de894212/go.mod h1:ZHfIarpAhcOE82iD+VeQxQXxatIY63UubJAgxUq67ig=
|
||||||
k8s.io/apimachinery v0.0.0-20201020200440-554eef9dbf66/go.mod h1:6s3VNb000AUbBIxR7q3WHlbBwfpEGqIJsCG5gIX+0LI=
|
k8s.io/apimachinery v0.0.0-20201026201559-ab98f4dc11fb/go.mod h1:6s3VNb000AUbBIxR7q3WHlbBwfpEGqIJsCG5gIX+0LI=
|
||||||
k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb/go.mod h1:8J/p/CEj2WGIZobSh/Oe9kVIVndBvFpmRutcbtZVJ1U=
|
k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb/go.mod h1:8J/p/CEj2WGIZobSh/Oe9kVIVndBvFpmRutcbtZVJ1U=
|
||||||
k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96/go.mod h1:MEvNyY+iZEFaAPeMVco7LyFmysN3O9jX9QSKcacQ0O8=
|
k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96/go.mod h1:MEvNyY+iZEFaAPeMVco7LyFmysN3O9jX9QSKcacQ0O8=
|
||||||
k8s.io/code-generator v0.0.0-20201020200306-60862b8acf58/go.mod h1:oioc17TXBB973K4R+ytm5k9jY1BktboxJc7qu3i29V0=
|
k8s.io/code-generator v0.0.0-20201020200306-60862b8acf58/go.mod h1:oioc17TXBB973K4R+ytm5k9jY1BktboxJc7qu3i29V0=
|
||||||
|
|
|
@ -2721,10 +2721,27 @@ func describeService(service *corev1.Service, endpoints *corev1.Endpoints, event
|
||||||
printAnnotationsMultiline(w, "Annotations", service.Annotations)
|
printAnnotationsMultiline(w, "Annotations", service.Annotations)
|
||||||
w.Write(LEVEL_0, "Selector:\t%s\n", labels.FormatLabels(service.Spec.Selector))
|
w.Write(LEVEL_0, "Selector:\t%s\n", labels.FormatLabels(service.Spec.Selector))
|
||||||
w.Write(LEVEL_0, "Type:\t%s\n", service.Spec.Type)
|
w.Write(LEVEL_0, "Type:\t%s\n", service.Spec.Type)
|
||||||
w.Write(LEVEL_0, "IP:\t%s\n", service.Spec.ClusterIP)
|
|
||||||
|
|
||||||
if service.Spec.IPFamily != nil {
|
if service.Spec.IPFamilyPolicy != nil {
|
||||||
w.Write(LEVEL_0, "IPFamily:\t%s\n", *(service.Spec.IPFamily))
|
w.Write(LEVEL_0, "IP Family Policy:\t%s\n", *(service.Spec.IPFamilyPolicy))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(service.Spec.IPFamilies) > 0 {
|
||||||
|
ipfamiliesStrings := make([]string, 0, len(service.Spec.IPFamilies))
|
||||||
|
for _, family := range service.Spec.IPFamilies {
|
||||||
|
ipfamiliesStrings = append(ipfamiliesStrings, string(family))
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(LEVEL_0, "IP Families:\t%s\n", strings.Join(ipfamiliesStrings, ","))
|
||||||
|
} else {
|
||||||
|
w.Write(LEVEL_0, "IP Families:\t%s\n", "<none>")
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(LEVEL_0, "IP:\t%s\n", service.Spec.ClusterIP)
|
||||||
|
if len(service.Spec.ClusterIPs) > 0 {
|
||||||
|
w.Write(LEVEL_0, "IPs:\t%s\n", strings.Join(service.Spec.ClusterIPs, ","))
|
||||||
|
} else {
|
||||||
|
w.Write(LEVEL_0, "IPs:\t%s\n", "<none>")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(service.Spec.ExternalIPs) > 0 {
|
if len(service.Spec.ExternalIPs) > 0 {
|
||||||
|
|
|
@ -358,8 +358,7 @@ func getResourceList(cpu, memory string) corev1.ResourceList {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDescribeService(t *testing.T) {
|
func TestDescribeService(t *testing.T) {
|
||||||
defaultServiceIPFamily := corev1.IPv4Protocol
|
singleStack := corev1.IPFamilyPolicySingleStack
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
service *corev1.Service
|
service *corev1.Service
|
||||||
|
@ -373,8 +372,7 @@ func TestDescribeService(t *testing.T) {
|
||||||
Namespace: "foo",
|
Namespace: "foo",
|
||||||
},
|
},
|
||||||
Spec: corev1.ServiceSpec{
|
Spec: corev1.ServiceSpec{
|
||||||
Type: corev1.ServiceTypeLoadBalancer,
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
IPFamily: &defaultServiceIPFamily,
|
|
||||||
Ports: []corev1.ServicePort{{
|
Ports: []corev1.ServicePort{{
|
||||||
Name: "port-tcp",
|
Name: "port-tcp",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
|
@ -384,6 +382,7 @@ func TestDescribeService(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
Selector: map[string]string{"blah": "heh"},
|
Selector: map[string]string{"blah": "heh"},
|
||||||
ClusterIP: "1.2.3.4",
|
ClusterIP: "1.2.3.4",
|
||||||
|
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
|
||||||
LoadBalancerIP: "5.6.7.8",
|
LoadBalancerIP: "5.6.7.8",
|
||||||
SessionAffinity: "None",
|
SessionAffinity: "None",
|
||||||
ExternalTrafficPolicy: "Local",
|
ExternalTrafficPolicy: "Local",
|
||||||
|
@ -412,8 +411,7 @@ func TestDescribeService(t *testing.T) {
|
||||||
Namespace: "foo",
|
Namespace: "foo",
|
||||||
},
|
},
|
||||||
Spec: corev1.ServiceSpec{
|
Spec: corev1.ServiceSpec{
|
||||||
Type: corev1.ServiceTypeLoadBalancer,
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
IPFamily: &defaultServiceIPFamily,
|
|
||||||
Ports: []corev1.ServicePort{{
|
Ports: []corev1.ServicePort{{
|
||||||
Name: "port-tcp",
|
Name: "port-tcp",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
|
@ -423,6 +421,7 @@ func TestDescribeService(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
Selector: map[string]string{"blah": "heh"},
|
Selector: map[string]string{"blah": "heh"},
|
||||||
ClusterIP: "1.2.3.4",
|
ClusterIP: "1.2.3.4",
|
||||||
|
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
|
||||||
LoadBalancerIP: "5.6.7.8",
|
LoadBalancerIP: "5.6.7.8",
|
||||||
SessionAffinity: "None",
|
SessionAffinity: "None",
|
||||||
ExternalTrafficPolicy: "Local",
|
ExternalTrafficPolicy: "Local",
|
||||||
|
@ -451,8 +450,7 @@ func TestDescribeService(t *testing.T) {
|
||||||
Namespace: "foo",
|
Namespace: "foo",
|
||||||
},
|
},
|
||||||
Spec: corev1.ServiceSpec{
|
Spec: corev1.ServiceSpec{
|
||||||
Type: corev1.ServiceTypeLoadBalancer,
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
IPFamily: &defaultServiceIPFamily,
|
|
||||||
Ports: []corev1.ServicePort{{
|
Ports: []corev1.ServicePort{{
|
||||||
Name: "port-tcp",
|
Name: "port-tcp",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
|
@ -462,6 +460,7 @@ func TestDescribeService(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
Selector: map[string]string{"blah": "heh"},
|
Selector: map[string]string{"blah": "heh"},
|
||||||
ClusterIP: "1.2.3.4",
|
ClusterIP: "1.2.3.4",
|
||||||
|
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
|
||||||
LoadBalancerIP: "5.6.7.8",
|
LoadBalancerIP: "5.6.7.8",
|
||||||
SessionAffinity: "None",
|
SessionAffinity: "None",
|
||||||
ExternalTrafficPolicy: "Local",
|
ExternalTrafficPolicy: "Local",
|
||||||
|
@ -474,7 +473,51 @@ func TestDescribeService(t *testing.T) {
|
||||||
"Selector", "blah=heh",
|
"Selector", "blah=heh",
|
||||||
"Type", "LoadBalancer",
|
"Type", "LoadBalancer",
|
||||||
"IP", "1.2.3.4",
|
"IP", "1.2.3.4",
|
||||||
"IPFamily", "IPv4",
|
"IP Families", "IPv4",
|
||||||
|
"Port", "port-tcp", "8080/TCP",
|
||||||
|
"TargetPort", "targetPort/TCP",
|
||||||
|
"NodePort", "port-tcp", "31111/TCP",
|
||||||
|
"Session Affinity", "None",
|
||||||
|
"External Traffic Policy", "Local",
|
||||||
|
"HealthCheck NodePort", "32222",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test-ServiceIPFamilyPolicy+ClusterIPs",
|
||||||
|
service: &corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "bar",
|
||||||
|
Namespace: "foo",
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
Ports: []corev1.ServicePort{{
|
||||||
|
Name: "port-tcp",
|
||||||
|
Port: 8080,
|
||||||
|
Protocol: corev1.ProtocolTCP,
|
||||||
|
TargetPort: intstr.FromString("targetPort"),
|
||||||
|
NodePort: 31111,
|
||||||
|
}},
|
||||||
|
Selector: map[string]string{"blah": "heh"},
|
||||||
|
ClusterIP: "1.2.3.4",
|
||||||
|
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
|
||||||
|
IPFamilyPolicy: &singleStack,
|
||||||
|
ClusterIPs: []string{"1.2.3.4"},
|
||||||
|
LoadBalancerIP: "5.6.7.8",
|
||||||
|
SessionAffinity: "None",
|
||||||
|
ExternalTrafficPolicy: "Local",
|
||||||
|
HealthCheckNodePort: 32222,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: []string{
|
||||||
|
"Name", "bar",
|
||||||
|
"Namespace", "foo",
|
||||||
|
"Selector", "blah=heh",
|
||||||
|
"Type", "LoadBalancer",
|
||||||
|
"IP", "1.2.3.4",
|
||||||
|
"IP Families", "IPv4",
|
||||||
|
"IP Family Policy", "SingleStack",
|
||||||
|
"IPs", "1.2.3.4",
|
||||||
"Port", "port-tcp", "8080/TCP",
|
"Port", "port-tcp", "8080/TCP",
|
||||||
"TargetPort", "targetPort/TCP",
|
"TargetPort", "targetPort/TCP",
|
||||||
"NodePort", "port-tcp", "31111/TCP",
|
"NodePort", "port-tcp", "31111/TCP",
|
||||||
|
|
Loading…
Reference in New Issue