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:
Khaled Henidak (Kal) 2020-10-26 13:15:59 -07:00 committed by Kubernetes Publisher
parent f28a68d5aa
commit 2bf7244939
5 changed files with 80 additions and 20 deletions

4
Godeps/Godeps.json generated
View File

@ -764,11 +764,11 @@
},
{
"ImportPath": "k8s.io/api",
"Rev": "ccbd11b5dd0a"
"Rev": "0415de894212"
},
{
"ImportPath": "k8s.io/apimachinery",
"Rev": "554eef9dbf66"
"Rev": "ab98f4dc11fb"
},
{
"ImportPath": "k8s.io/cli-runtime",

8
go.mod
View File

@ -34,8 +34,8 @@ require (
github.com/stretchr/testify v1.4.0
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4
gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.0.0-20201023125151-ccbd11b5dd0a
k8s.io/apimachinery v0.0.0-20201020200440-554eef9dbf66
k8s.io/api v0.0.0-20201026202134-0415de894212
k8s.io/apimachinery v0.0.0-20201026201559-ab98f4dc11fb
k8s.io/cli-runtime v0.0.0-20201023084906-87ad57c444cb
k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96
k8s.io/component-base v0.0.0-20201020201314-b8c7ac1518d8
@ -48,8 +48,8 @@ require (
)
replace (
k8s.io/api => k8s.io/api v0.0.0-20201023125151-ccbd11b5dd0a
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20201020200440-554eef9dbf66
k8s.io/api => k8s.io/api v0.0.0-20201026202134-0415de894212
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/client-go => k8s.io/client-go v0.0.0-20201020200834-d1a4fe5f2d96
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20201020200306-60862b8acf58

4
go.sum
View File

@ -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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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/apimachinery v0.0.0-20201020200440-554eef9dbf66/go.mod h1:6s3VNb000AUbBIxR7q3WHlbBwfpEGqIJsCG5gIX+0LI=
k8s.io/api v0.0.0-20201026202134-0415de894212/go.mod h1:ZHfIarpAhcOE82iD+VeQxQXxatIY63UubJAgxUq67ig=
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/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=

View File

@ -2721,10 +2721,27 @@ func describeService(service *corev1.Service, endpoints *corev1.Endpoints, event
printAnnotationsMultiline(w, "Annotations", service.Annotations)
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, "IP:\t%s\n", service.Spec.ClusterIP)
if service.Spec.IPFamily != nil {
w.Write(LEVEL_0, "IPFamily:\t%s\n", *(service.Spec.IPFamily))
if service.Spec.IPFamilyPolicy != nil {
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 {

View File

@ -358,8 +358,7 @@ func getResourceList(cpu, memory string) corev1.ResourceList {
}
func TestDescribeService(t *testing.T) {
defaultServiceIPFamily := corev1.IPv4Protocol
singleStack := corev1.IPFamilyPolicySingleStack
testCases := []struct {
name string
service *corev1.Service
@ -373,8 +372,7 @@ func TestDescribeService(t *testing.T) {
Namespace: "foo",
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeLoadBalancer,
IPFamily: &defaultServiceIPFamily,
Type: corev1.ServiceTypeLoadBalancer,
Ports: []corev1.ServicePort{{
Name: "port-tcp",
Port: 8080,
@ -384,6 +382,7 @@ func TestDescribeService(t *testing.T) {
}},
Selector: map[string]string{"blah": "heh"},
ClusterIP: "1.2.3.4",
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
LoadBalancerIP: "5.6.7.8",
SessionAffinity: "None",
ExternalTrafficPolicy: "Local",
@ -412,8 +411,7 @@ func TestDescribeService(t *testing.T) {
Namespace: "foo",
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeLoadBalancer,
IPFamily: &defaultServiceIPFamily,
Type: corev1.ServiceTypeLoadBalancer,
Ports: []corev1.ServicePort{{
Name: "port-tcp",
Port: 8080,
@ -423,6 +421,7 @@ func TestDescribeService(t *testing.T) {
}},
Selector: map[string]string{"blah": "heh"},
ClusterIP: "1.2.3.4",
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
LoadBalancerIP: "5.6.7.8",
SessionAffinity: "None",
ExternalTrafficPolicy: "Local",
@ -451,8 +450,7 @@ func TestDescribeService(t *testing.T) {
Namespace: "foo",
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeLoadBalancer,
IPFamily: &defaultServiceIPFamily,
Type: corev1.ServiceTypeLoadBalancer,
Ports: []corev1.ServicePort{{
Name: "port-tcp",
Port: 8080,
@ -462,6 +460,7 @@ func TestDescribeService(t *testing.T) {
}},
Selector: map[string]string{"blah": "heh"},
ClusterIP: "1.2.3.4",
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
LoadBalancerIP: "5.6.7.8",
SessionAffinity: "None",
ExternalTrafficPolicy: "Local",
@ -474,7 +473,51 @@ func TestDescribeService(t *testing.T) {
"Selector", "blah=heh",
"Type", "LoadBalancer",
"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",
"TargetPort", "targetPort/TCP",
"NodePort", "port-tcp", "31111/TCP",