move IPv6DualStack feature to stable. (#104691)
* kube-proxy * endpoints controller * app: kube-controller-manager * app: cloud-controller-manager * kubelet * app: api-server * node utils + registry/strategy * api: validation (comment removal) * api:pod strategy (util pkg) * api: docs * core: integration testing * kubeadm: change feature gate to GA * service registry and rest stack * move feature to GA * generated
This commit is contained in:

committed by
GitHub

parent
c74d799677
commit
a53e2eaeab
@@ -490,32 +490,16 @@ func DropDisabledPodFields(pod, oldPod *api.Pod) {
|
||||
podAnnotations map[string]string
|
||||
oldPodSpec *api.PodSpec
|
||||
oldPodAnnotations map[string]string
|
||||
podStatus *api.PodStatus
|
||||
oldPodStatus *api.PodStatus
|
||||
)
|
||||
if pod != nil {
|
||||
podSpec = &pod.Spec
|
||||
podAnnotations = pod.Annotations
|
||||
podStatus = &pod.Status
|
||||
}
|
||||
if oldPod != nil {
|
||||
oldPodSpec = &oldPod.Spec
|
||||
oldPodAnnotations = oldPod.Annotations
|
||||
oldPodStatus = &oldPod.Status
|
||||
}
|
||||
dropDisabledFields(podSpec, podAnnotations, oldPodSpec, oldPodAnnotations)
|
||||
dropPodStatusDisabledFields(podStatus, oldPodStatus)
|
||||
}
|
||||
|
||||
// dropPodStatusDisabledFields removes disabled fields from the pod status
|
||||
func dropPodStatusDisabledFields(podStatus *api.PodStatus, oldPodStatus *api.PodStatus) {
|
||||
// trim PodIPs down to only one entry (non dual stack).
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) &&
|
||||
!multiplePodIPsInUse(oldPodStatus) {
|
||||
if len(podStatus.PodIPs) != 0 {
|
||||
podStatus.PodIPs = podStatus.PodIPs[0:1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dropDisabledFields removes disabled fields from the pod metadata and spec.
|
||||
@@ -824,17 +808,6 @@ func ephemeralInUse(podSpec *api.PodSpec) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// podPriorityInUse returns true if status is not nil and number of PodIPs is greater than one
|
||||
func multiplePodIPsInUse(podStatus *api.PodStatus) bool {
|
||||
if podStatus == nil {
|
||||
return false
|
||||
}
|
||||
if len(podStatus.PodIPs) > 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SeccompAnnotationForField takes a pod seccomp profile field and returns the
|
||||
// converted annotation value
|
||||
func SeccompAnnotationForField(field *api.SeccompProfile) string {
|
||||
|
@@ -1053,107 +1053,6 @@ func TestDropProbeGracePeriod(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// helper creates a podStatus with list of PodIPs
|
||||
func makePodStatus(podIPs []api.PodIP) *api.PodStatus {
|
||||
return &api.PodStatus{
|
||||
PodIPs: podIPs,
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropStatusPodIPs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
podStatus *api.PodStatus
|
||||
oldPodStatus *api.PodStatus
|
||||
comparePodStatus *api.PodStatus
|
||||
enableDualStack bool
|
||||
}{
|
||||
{
|
||||
name: "nil pod ips",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus(nil),
|
||||
oldPodStatus: nil,
|
||||
comparePodStatus: makePodStatus(nil),
|
||||
},
|
||||
{
|
||||
name: "empty pod ips",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{}),
|
||||
oldPodStatus: nil,
|
||||
comparePodStatus: makePodStatus([]api.PodIP{}),
|
||||
},
|
||||
{
|
||||
name: "single family ipv6",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "::1"}}),
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "::1"}}),
|
||||
},
|
||||
{
|
||||
name: "single family ipv4",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "1.1.1.1"}}),
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "1.1.1.1"}}),
|
||||
},
|
||||
{
|
||||
name: "dualstack 4-6",
|
||||
enableDualStack: true,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}}),
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}}),
|
||||
},
|
||||
{
|
||||
name: "dualstack 6-4",
|
||||
enableDualStack: true,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>4only",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
oldPodStatus: nil,
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "::1"}}),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>as is (used in old)",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
oldPodStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>6only",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
oldPodStatus: nil,
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "::1"}}),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>as is (used in old)",
|
||||
enableDualStack: false,
|
||||
podStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
oldPodStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
comparePodStatus: makePodStatus([]api.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
func() {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
dropPodStatusDisabledFields(tc.podStatus, tc.oldPodStatus)
|
||||
|
||||
old := tc.oldPodStatus.DeepCopy()
|
||||
// old pod status should never be changed
|
||||
if !reflect.DeepEqual(tc.oldPodStatus, old) {
|
||||
t.Errorf("%v: old pod status changed: %v", tc.name, cmp.Diff(tc.oldPodStatus, old))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tc.podStatus, tc.comparePodStatus) {
|
||||
t.Errorf("%v: unexpected pod status: %v", tc.name, cmp.Diff(tc.podStatus, tc.comparePodStatus))
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropEphemeralContainers(t *testing.T) {
|
||||
podWithEphemeralContainers := func() *api.Pod {
|
||||
return &api.Pod{
|
||||
|
@@ -2286,8 +2286,6 @@ var validEnvDownwardAPIFieldPathExpressions = sets.NewString(
|
||||
"spec.serviceAccountName",
|
||||
"status.hostIP",
|
||||
"status.podIP",
|
||||
// status.podIPs is populated even if IPv6DualStack feature gate
|
||||
// is not enabled. This will work for single stack and dual stack.
|
||||
"status.podIPs")
|
||||
|
||||
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "limits.ephemeral-storage", "requests.cpu", "requests.memory", "requests.ephemeral-storage")
|
||||
|
@@ -30,7 +30,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
@@ -48,7 +47,6 @@ import (
|
||||
helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utillabels "k8s.io/kubernetes/pkg/util/labels"
|
||||
utilnet "k8s.io/utils/net"
|
||||
)
|
||||
@@ -229,42 +227,38 @@ func podToEndpointAddressForService(svc *v1.Service, pod *v1.Pod) (*v1.EndpointA
|
||||
var endpointIP string
|
||||
ipFamily := v1.IPv4Protocol
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
// In a legacy cluster, the pod IP is guaranteed to be usable
|
||||
endpointIP = pod.Status.PodIP
|
||||
if len(svc.Spec.IPFamilies) > 0 {
|
||||
// controller is connected to an api-server that correctly sets IPFamilies
|
||||
ipFamily = svc.Spec.IPFamilies[0] // this works for headful and headless
|
||||
} else {
|
||||
//feature flag enabled and pods may have multiple IPs
|
||||
if len(svc.Spec.IPFamilies) > 0 {
|
||||
// controller is connected to an api-server that correctly sets IPFamilies
|
||||
ipFamily = svc.Spec.IPFamilies[0] // this works for headful and headless
|
||||
// controller is connected to an api server that does not correctly
|
||||
// set IPFamilies (e.g. old api-server during an upgrade)
|
||||
// TODO (khenidak): remove by when the possibility of upgrading
|
||||
// from a cluster that does not support dual stack is nil
|
||||
if len(svc.Spec.ClusterIP) > 0 && svc.Spec.ClusterIP != v1.ClusterIPNone {
|
||||
// headful service. detect via service clusterIP
|
||||
if utilnet.IsIPv6String(svc.Spec.ClusterIP) {
|
||||
ipFamily = v1.IPv6Protocol
|
||||
}
|
||||
} else {
|
||||
// controller is connected to an api server that does not correctly
|
||||
// set IPFamilies (e.g. old api-server during an upgrade)
|
||||
if len(svc.Spec.ClusterIP) > 0 && svc.Spec.ClusterIP != v1.ClusterIPNone {
|
||||
// headful service. detect via service clusterIP
|
||||
if utilnet.IsIPv6String(svc.Spec.ClusterIP) {
|
||||
ipFamily = v1.IPv6Protocol
|
||||
}
|
||||
} else {
|
||||
// Since this is a headless service we use podIP to identify the family.
|
||||
// This assumes that status.PodIP is assigned correctly (follows pod cidr and
|
||||
// pod cidr list order is same as service cidr list order). The expectation is
|
||||
// this is *most probably* the case.
|
||||
// Since this is a headless service we use podIP to identify the family.
|
||||
// This assumes that status.PodIP is assigned correctly (follows pod cidr and
|
||||
// pod cidr list order is same as service cidr list order). The expectation is
|
||||
// this is *most probably* the case.
|
||||
|
||||
// if the family was incorrectly identified then this will be corrected once the
|
||||
// the upgrade is completed (controller connects to api-server that correctly defaults services)
|
||||
if utilnet.IsIPv6String(pod.Status.PodIP) {
|
||||
ipFamily = v1.IPv6Protocol
|
||||
}
|
||||
// if the family was incorrectly identified then this will be corrected once the
|
||||
// the upgrade is completed (controller connects to api-server that correctly defaults services)
|
||||
if utilnet.IsIPv6String(pod.Status.PodIP) {
|
||||
ipFamily = v1.IPv6Protocol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find an ip that matches the family
|
||||
for _, podIP := range pod.Status.PodIPs {
|
||||
if (ipFamily == v1.IPv6Protocol) == utilnet.IsIPv6String(podIP.IP) {
|
||||
endpointIP = podIP.IP
|
||||
break
|
||||
}
|
||||
// find an ip that matches the family
|
||||
for _, podIP := range pod.Status.PodIPs {
|
||||
if (ipFamily == v1.IPv6Protocol) == utilnet.IsIPv6String(podIP.IP) {
|
||||
endpointIP = podIP.IP
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
@@ -41,11 +40,9 @@ import (
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
endptspkg "k8s.io/kubernetes/pkg/api/v1/endpoints"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
controllerpkg "k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utilnet "k8s.io/utils/net"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
@@ -1207,209 +1204,154 @@ func TestPodToEndpointAddressForService(t *testing.T) {
|
||||
ipv6 := v1.IPv6Protocol
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
enableDualStack bool
|
||||
ipFamilies []v1.IPFamily
|
||||
|
||||
service v1.Service
|
||||
|
||||
name string
|
||||
ipFamilies []v1.IPFamily
|
||||
service v1.Service
|
||||
expectedEndpointFamily v1.IPFamily
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "v4 service, in a single stack cluster",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv4only,
|
||||
|
||||
name: "v4 service, in a single stack cluster",
|
||||
ipFamilies: ipv4only,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
{
|
||||
name: "v4 service, in a dual stack cluster",
|
||||
|
||||
enableDualStack: true,
|
||||
ipFamilies: ipv4ipv6,
|
||||
|
||||
name: "v4 service, in a dual stack cluster",
|
||||
ipFamilies: ipv4ipv6,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
{
|
||||
name: "v4 service, in a dual stack ipv6-primary cluster",
|
||||
|
||||
enableDualStack: true,
|
||||
ipFamilies: ipv6ipv4,
|
||||
|
||||
name: "v4 service, in a dual stack ipv6-primary cluster",
|
||||
ipFamilies: ipv6ipv4,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
{
|
||||
name: "v4 headless service, in a single stack cluster",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv4only,
|
||||
|
||||
name: "v4 headless service, in a single stack cluster",
|
||||
ipFamilies: ipv4only,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
{
|
||||
name: "v4 headless service, in a dual stack cluster",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv4ipv6,
|
||||
|
||||
name: "v4 headless service, in a dual stack cluster",
|
||||
ipFamilies: ipv4ipv6,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
{
|
||||
name: "v4 legacy headless service, in a dual stack cluster",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv4ipv6,
|
||||
|
||||
name: "v4 legacy headless service, in a dual stack cluster",
|
||||
ipFamilies: ipv4ipv6,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
{
|
||||
name: "v4 legacy headless service, in a dual stack ipv6-primary cluster",
|
||||
|
||||
enableDualStack: true,
|
||||
ipFamilies: ipv6ipv4,
|
||||
|
||||
name: "v4 legacy headless service, in a dual stack ipv6-primary cluster",
|
||||
ipFamilies: ipv6ipv4,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv6,
|
||||
},
|
||||
{
|
||||
name: "v6 service, in a dual stack cluster",
|
||||
|
||||
enableDualStack: true,
|
||||
ipFamilies: ipv4ipv6,
|
||||
|
||||
name: "v6 service, in a dual stack cluster",
|
||||
ipFamilies: ipv4ipv6,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "3000::1",
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv6,
|
||||
},
|
||||
{
|
||||
name: "v6 headless service, in a single stack cluster",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv6only,
|
||||
|
||||
name: "v6 headless service, in a single stack cluster",
|
||||
ipFamilies: ipv6only,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv6,
|
||||
},
|
||||
{
|
||||
name: "v6 headless service, in a dual stack cluster (connected to a new api-server)",
|
||||
|
||||
enableDualStack: true,
|
||||
ipFamilies: ipv4ipv6,
|
||||
|
||||
name: "v6 headless service, in a dual stack cluster (connected to a new api-server)",
|
||||
ipFamilies: ipv4ipv6,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
IPFamilies: []v1.IPFamily{v1.IPv6Protocol}, // <- set by a api-server defaulting logic
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv6,
|
||||
},
|
||||
{
|
||||
name: "v6 legacy headless service, in a dual stack cluster (connected to a old api-server)",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv4ipv6,
|
||||
|
||||
name: "v6 legacy headless service, in a dual stack cluster (connected to a old api-server)",
|
||||
ipFamilies: ipv4ipv6,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: v1.ClusterIPNone, // <- families are not set by api-server
|
||||
},
|
||||
},
|
||||
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
|
||||
// in reality this is a misconfigured cluster
|
||||
// i.e user is not using dual stack and have PodIP == v4 and ServiceIP==v6
|
||||
// we are testing that we will keep producing the expected behavior
|
||||
// previously controller could assign wrong ip to endpoint address
|
||||
// with gate removed. this is no longer the case. this is *not* behavior change
|
||||
// because previously things would have failed in kube-proxy anyway (due to editing wrong iptables).
|
||||
{
|
||||
name: "v6 service, in a v4 only cluster. dual stack disabled",
|
||||
|
||||
enableDualStack: false,
|
||||
ipFamilies: ipv4only,
|
||||
|
||||
name: "v6 service, in a v4 only cluster.",
|
||||
ipFamilies: ipv4only,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "3000::1",
|
||||
},
|
||||
},
|
||||
|
||||
expectError: true,
|
||||
expectedEndpointFamily: ipv4,
|
||||
},
|
||||
// but this will actually give an error
|
||||
{
|
||||
name: "v6 service, in a v4 only cluster - dual stack enabled",
|
||||
|
||||
enableDualStack: true,
|
||||
ipFamilies: ipv4only,
|
||||
|
||||
name: "v6 service, in a v4 only cluster",
|
||||
ipFamilies: ipv4only,
|
||||
service: v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "3000::1",
|
||||
},
|
||||
},
|
||||
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
podStore := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
|
||||
ns := "test"
|
||||
addPods(podStore, ns, 1, 1, 0, tc.ipFamilies)
|
||||
|
@@ -796,7 +796,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
LocalStorageCapacityIsolationFSQuotaMonitoring: {Default: false, PreRelease: featuregate.Alpha},
|
||||
NonPreemptingPriority: {Default: true, PreRelease: featuregate.Beta},
|
||||
PodOverhead: {Default: true, PreRelease: featuregate.Beta},
|
||||
IPv6DualStack: {Default: true, PreRelease: featuregate.Beta},
|
||||
IPv6DualStack: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.25
|
||||
EndpointSlice: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.25
|
||||
EndpointSliceProxying: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.25
|
||||
EndpointSliceTerminatingCondition: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
@@ -369,3 +369,7 @@ func (f *fakeIPTables) isBuiltinChain(tableName utiliptables.Table, chainName ut
|
||||
func (f *fakeIPTables) HasRandomFully() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) Present() bool {
|
||||
return true
|
||||
}
|
||||
|
@@ -47,8 +47,6 @@ import (
|
||||
utilexec "k8s.io/utils/exec"
|
||||
utilebtables "k8s.io/utils/net/ebtables"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
@@ -253,12 +251,6 @@ func (plugin *kubenetNetworkPlugin) Event(name string, details map[string]interf
|
||||
klog.V(4).InfoS("Kubenet: PodCIDR is set to new value", "podCIDR", podCIDR)
|
||||
podCIDRs := strings.Split(podCIDR, ",")
|
||||
|
||||
// reset to one cidr if dual stack is not enabled
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.IPv6DualStack) && len(podCIDRs) > 1 {
|
||||
klog.V(2).InfoS("This node has multiple pod cidrs assigned and dual stack is not enabled. ignoring all except first cidr")
|
||||
podCIDRs = podCIDRs[0:1]
|
||||
}
|
||||
|
||||
for idx, currentPodCIDR := range podCIDRs {
|
||||
_, cidr, err := netutils.ParseCIDRSloppy(currentPodCIDR)
|
||||
if nil != err {
|
||||
|
@@ -39,9 +39,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
netutils "k8s.io/utils/net"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -263,19 +260,6 @@ func getOnePodIP(execer utilexec.Interface, nsenterPath, netnsPath, interfaceNam
|
||||
// TODO (khenidak). The "primary ip" in dual stack world does not really exist. For now
|
||||
// we are defaulting to v4 as primary
|
||||
func GetPodIPs(execer utilexec.Interface, nsenterPath, netnsPath, interfaceName string) ([]net.IP, error) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.IPv6DualStack) {
|
||||
ip, err := getOnePodIP(execer, nsenterPath, netnsPath, interfaceName, "-4")
|
||||
if err != nil {
|
||||
// Fall back to IPv6 address if no IPv4 address is present
|
||||
ip, err = getOnePodIP(execer, nsenterPath, netnsPath, interfaceName, "-6")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []net.IP{ip}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
list []net.IP
|
||||
errs []error
|
||||
|
@@ -24,9 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
utilnet "k8s.io/utils/net"
|
||||
@@ -34,21 +32,23 @@ import (
|
||||
|
||||
func (kl *Kubelet) initNetworkUtil() {
|
||||
exec := utilexec.New()
|
||||
|
||||
// At this point in startup we don't know the actual node IPs, so we configure dual stack iptables
|
||||
// rules if the node _might_ be dual-stack, and single-stack based on requested nodeIPs[0] otherwise.
|
||||
maybeDualStack := utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack)
|
||||
// TODO: @khenidak review when there is no IPv6 iptables exec what should happen here (note: no error returned from this func)
|
||||
ipv6Primary := kl.nodeIPs != nil && utilnet.IsIPv6(kl.nodeIPs[0])
|
||||
|
||||
var iptClients []utiliptables.Interface
|
||||
var protocols []utiliptables.Protocol
|
||||
if maybeDualStack || !ipv6Primary {
|
||||
protocols = append(protocols, utiliptables.ProtocolIPv4)
|
||||
iptClients = append(iptClients, utiliptables.New(exec, utiliptables.ProtocolIPv4))
|
||||
}
|
||||
if maybeDualStack || ipv6Primary {
|
||||
protocols = append(protocols, utiliptables.ProtocolIPv6)
|
||||
iptClients = append(iptClients, utiliptables.New(exec, utiliptables.ProtocolIPv6))
|
||||
|
||||
// assume 4,6
|
||||
protocols = append(protocols, utiliptables.ProtocolIPv4)
|
||||
iptClients = append(iptClients, utiliptables.New(exec, utiliptables.ProtocolIPv4))
|
||||
|
||||
protocols = append(protocols, utiliptables.ProtocolIPv6)
|
||||
iptClients = append(iptClients, utiliptables.New(exec, utiliptables.ProtocolIPv6))
|
||||
|
||||
// and if they are not
|
||||
if ipv6Primary {
|
||||
protocols[0], protocols[1] = protocols[1], protocols[0]
|
||||
iptClients[0], iptClients[1] = iptClients[1], iptClients[0]
|
||||
}
|
||||
|
||||
for i := range iptClients {
|
||||
|
@@ -1508,7 +1508,7 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po
|
||||
if kubecontainer.IsHostNetworkPod(pod) && s.PodIP == "" {
|
||||
s.PodIP = hostIPs[0].String()
|
||||
s.PodIPs = []v1.PodIP{{IP: s.PodIP}}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) && len(hostIPs) == 2 {
|
||||
if len(hostIPs) == 2 {
|
||||
s.PodIPs = append(s.PodIPs, v1.PodIP{IP: hostIPs[1].String()})
|
||||
}
|
||||
}
|
||||
|
@@ -38,10 +38,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/record"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
netutils "k8s.io/utils/net"
|
||||
|
||||
// TODO: remove this import if
|
||||
@@ -49,7 +47,6 @@ import (
|
||||
// to "v1"?
|
||||
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cri/streaming/portforward"
|
||||
@@ -3113,7 +3110,6 @@ func TestTruncatePodHostname(t *testing.T) {
|
||||
func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
dualStack bool
|
||||
nodeAddresses []v1.NodeAddress
|
||||
criPodIPs []string
|
||||
podIPs []v1.PodIP
|
||||
@@ -3137,22 +3133,11 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
{IP: "10.0.0.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Dual-stack addresses are ignored in single-stack cluster",
|
||||
nodeAddresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "fd01::1234"},
|
||||
},
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "10.0.0.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Single-stack addresses in dual-stack cluster",
|
||||
nodeAddresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
|
||||
},
|
||||
dualStack: true,
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "10.0.0.1"},
|
||||
},
|
||||
@@ -3164,7 +3149,6 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.2"},
|
||||
{Type: v1.NodeExternalIP, Address: "192.168.0.1"},
|
||||
},
|
||||
dualStack: true,
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "10.0.0.1"},
|
||||
},
|
||||
@@ -3175,7 +3159,6 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "fd01::1234"},
|
||||
},
|
||||
dualStack: true,
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "10.0.0.1"},
|
||||
{IP: "fd01::1234"},
|
||||
@@ -3187,7 +3170,6 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "fd01::1234"},
|
||||
},
|
||||
dualStack: true,
|
||||
criPodIPs: []string{"192.168.0.1"},
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "192.168.0.1"},
|
||||
@@ -3199,7 +3181,6 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "fd01::1234"},
|
||||
},
|
||||
dualStack: true,
|
||||
criPodIPs: []string{"192.168.0.1", "2001:db8::2"},
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "192.168.0.1"},
|
||||
@@ -3213,7 +3194,6 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "fd01::1234"},
|
||||
},
|
||||
dualStack: true,
|
||||
criPodIPs: []string{"2001:db8::2", "192.168.0.1"},
|
||||
podIPs: []v1.PodIP{
|
||||
{IP: "192.168.0.1"},
|
||||
@@ -3228,8 +3208,6 @@ func TestGenerateAPIPodStatusHostNetworkPodIPs(t *testing.T) {
|
||||
defer testKubelet.Cleanup()
|
||||
kl := testKubelet.kubelet
|
||||
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.dualStack)()
|
||||
|
||||
kl.nodeLister = testNodeLister{nodes: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: string(kl.nodeName)},
|
||||
|
@@ -30,7 +30,6 @@ import (
|
||||
componentbaseconfig "k8s.io/component-base/config"
|
||||
"k8s.io/component-base/metrics"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
@@ -75,22 +74,19 @@ func Validate(config *kubeproxyconfig.KubeProxyConfiguration) field.ErrorList {
|
||||
}
|
||||
allErrs = append(allErrs, validateHostPort(config.MetricsBindAddress, newPath.Child("MetricsBindAddress"))...)
|
||||
|
||||
dualStackEnabled := effectiveFeatures.Enabled(kubefeatures.IPv6DualStack)
|
||||
|
||||
if config.ClusterCIDR != "" {
|
||||
cidrs := strings.Split(config.ClusterCIDR, ",")
|
||||
switch {
|
||||
// if DualStack only valid one cidr or two cidrs with one of each IP family
|
||||
case dualStackEnabled && len(cidrs) > 2:
|
||||
case len(cidrs) > 2:
|
||||
allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "only one CIDR allowed or a valid DualStack CIDR (e.g. 10.100.0.0/16,fde4:8dba:82e1::/48)"))
|
||||
// if DualStack and two cidrs validate if there is at least one of each IP family
|
||||
case dualStackEnabled && len(cidrs) == 2:
|
||||
case len(cidrs) == 2:
|
||||
isDual, err := netutils.IsDualStackCIDRStrings(cidrs)
|
||||
if err != nil || !isDual {
|
||||
allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "must be a valid DualStack CIDR (e.g. 10.100.0.0/16,fde4:8dba:82e1::/48)"))
|
||||
}
|
||||
// if not DualStack only one CIDR allowed
|
||||
case !dualStackEnabled && len(cidrs) > 1:
|
||||
case len(cidrs) > 1:
|
||||
allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "only one CIDR allowed (e.g. 10.100.0.0/16 or fde4:8dba:82e1::/48)"))
|
||||
// if we are here means that len(cidrs) == 1, we need to validate it
|
||||
default:
|
||||
|
@@ -122,7 +122,6 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
|
||||
BindAddress: "10.10.12.11",
|
||||
HealthzBindAddress: "0.0.0.0:12345",
|
||||
MetricsBindAddress: "127.0.0.1:10249",
|
||||
FeatureGates: map[string]bool{"IPv6DualStack": true},
|
||||
ClusterCIDR: "192.168.59.0/24",
|
||||
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
|
||||
@@ -142,7 +141,6 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
|
||||
BindAddress: "10.10.12.11",
|
||||
HealthzBindAddress: "0.0.0.0:12345",
|
||||
MetricsBindAddress: "127.0.0.1:10249",
|
||||
FeatureGates: map[string]bool{"IPv6DualStack": true},
|
||||
ClusterCIDR: "fd00:192:168::/64",
|
||||
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
|
||||
@@ -162,7 +160,6 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
|
||||
BindAddress: "10.10.12.11",
|
||||
HealthzBindAddress: "0.0.0.0:12345",
|
||||
MetricsBindAddress: "127.0.0.1:10249",
|
||||
FeatureGates: map[string]bool{"IPv6DualStack": true},
|
||||
ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64",
|
||||
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
|
||||
@@ -279,36 +276,11 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
|
||||
},
|
||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ClusterCIDR"), "192.168.59.0", "must be a valid CIDR block (e.g. 10.100.0.0/16 or fde4:8dba:82e1::/48)")},
|
||||
},
|
||||
"Two ClusterCIDR addresses provided without DualStack feature-enabled": {
|
||||
config: kubeproxyconfig.KubeProxyConfiguration{
|
||||
BindAddress: "10.10.12.11",
|
||||
HealthzBindAddress: "0.0.0.0:12345",
|
||||
MetricsBindAddress: "127.0.0.1:10249",
|
||||
// DualStack ClusterCIDR without feature flag enabled
|
||||
FeatureGates: map[string]bool{"IPv6DualStack": false},
|
||||
ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64",
|
||||
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
|
||||
IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
|
||||
MasqueradeAll: true,
|
||||
SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
|
||||
MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
|
||||
},
|
||||
Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
|
||||
MaxPerCore: pointer.Int32Ptr(1),
|
||||
Min: pointer.Int32Ptr(1),
|
||||
TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||
TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||
},
|
||||
},
|
||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ClusterCIDR"), "192.168.59.0/24,fd00:192:168::/64", "only one CIDR allowed (e.g. 10.100.0.0/16 or fde4:8dba:82e1::/48)")},
|
||||
},
|
||||
"Invalid number of ClusterCIDRs": {
|
||||
config: kubeproxyconfig.KubeProxyConfiguration{
|
||||
BindAddress: "10.10.12.11",
|
||||
HealthzBindAddress: "0.0.0.0:12345",
|
||||
MetricsBindAddress: "127.0.0.1:10249",
|
||||
FeatureGates: map[string]bool{"IPv6DualStack": true},
|
||||
ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64,10.0.0.0/16",
|
||||
UDPIdleTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||
ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
|
||||
@@ -396,16 +368,18 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
errs := Validate(&testCase.config)
|
||||
if len(testCase.expectedErrs) != len(errs) {
|
||||
t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs)
|
||||
}
|
||||
for i, err := range errs {
|
||||
if err.Error() != testCase.expectedErrs[i].Error() {
|
||||
t.Fatalf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error())
|
||||
for name, testCase := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
errs := Validate(&testCase.config)
|
||||
if len(testCase.expectedErrs) != len(errs) {
|
||||
t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs)
|
||||
}
|
||||
}
|
||||
for i, err := range errs {
|
||||
if err.Error() != testCase.expectedErrs[i].Error() {
|
||||
t.Fatalf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -180,11 +180,6 @@ type StackCompatTester interface {
|
||||
type DualStackCompatTester struct{}
|
||||
|
||||
func (t DualStackCompatTester) DualStackCompatible(networkName string) bool {
|
||||
dualStackFeatureEnabled := utilfeature.DefaultFeatureGate.Enabled(kubefeatures.IPv6DualStack)
|
||||
if !dualStackFeatureEnabled {
|
||||
return false
|
||||
}
|
||||
|
||||
// First tag of hcsshim that has a proper check for dual stack support is v0.8.22 due to a bug.
|
||||
if err := hcn.IPv6DualStackSupported(); err != nil {
|
||||
// Hcn *can* fail the query to grab the version of hcn itself (which this call will do internally before parsing
|
||||
|
@@ -104,23 +104,6 @@ func dropDisabledFields(node *api.Node, oldNode *api.Node) {
|
||||
node.Spec.ConfigSource = nil
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) && !multiNodeCIDRsInUse(oldNode) {
|
||||
if len(node.Spec.PodCIDRs) > 1 {
|
||||
node.Spec.PodCIDRs = node.Spec.PodCIDRs[0:1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multiNodeCIDRsInUse returns true if Node.Spec.PodCIDRs is greater than one
|
||||
func multiNodeCIDRsInUse(node *api.Node) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(node.Spec.PodCIDRs) > 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// nodeConfigSourceInUse returns true if node's Spec ConfigSource is set(used)
|
||||
|
@@ -92,78 +92,42 @@ func TestDropFields(t *testing.T) {
|
||||
node *api.Node
|
||||
oldNode *api.Node
|
||||
compareNode *api.Node
|
||||
enableDualStack bool
|
||||
enableNodeDynamicConfig bool
|
||||
}{
|
||||
{
|
||||
name: "nil pod cidrs",
|
||||
enableDualStack: false,
|
||||
node: makeNode(nil, false, false),
|
||||
oldNode: nil,
|
||||
compareNode: makeNode(nil, false, false),
|
||||
name: "nil pod cidrs",
|
||||
node: makeNode(nil, false, false),
|
||||
oldNode: nil,
|
||||
compareNode: makeNode(nil, false, false),
|
||||
},
|
||||
{
|
||||
name: "empty pod ips",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{}, false, false),
|
||||
oldNode: nil,
|
||||
compareNode: makeNode([]string{}, false, false),
|
||||
name: "empty pod ips",
|
||||
node: makeNode([]string{}, false, false),
|
||||
oldNode: nil,
|
||||
compareNode: makeNode([]string{}, false, false),
|
||||
},
|
||||
{
|
||||
name: "single family ipv6",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{"2000::/10"}, false, false),
|
||||
compareNode: makeNode([]string{"2000::/10"}, false, false),
|
||||
name: "single family ipv6",
|
||||
node: makeNode([]string{"2000::/10"}, false, false),
|
||||
compareNode: makeNode([]string{"2000::/10"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "single family ipv4",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{"10.0.0.0/8"}, false, false),
|
||||
compareNode: makeNode([]string{"10.0.0.0/8"}, false, false),
|
||||
name: "single family ipv4",
|
||||
node: makeNode([]string{"10.0.0.0/8"}, false, false),
|
||||
compareNode: makeNode([]string{"10.0.0.0/8"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "dualstack 4-6",
|
||||
enableDualStack: true,
|
||||
node: makeNode([]string{"10.0.0.0/8", "2000::/10"}, false, false),
|
||||
compareNode: makeNode([]string{"10.0.0.0/8", "2000::/10"}, false, false),
|
||||
name: "dualstack 4-6",
|
||||
node: makeNode([]string{"10.0.0.0/8", "2000::/10"}, false, false),
|
||||
compareNode: makeNode([]string{"10.0.0.0/8", "2000::/10"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "dualstack 6-4",
|
||||
enableDualStack: true,
|
||||
node: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>4only",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
oldNode: nil,
|
||||
compareNode: makeNode([]string{"2000::/10"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>as is (used in old)",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
oldNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>6only",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
oldNode: nil,
|
||||
compareNode: makeNode([]string{"2000::/10"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "not dualstack 6-4=>as is (used in old)",
|
||||
enableDualStack: false,
|
||||
node: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
oldNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
name: "dualstack 6-4",
|
||||
node: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
|
||||
},
|
||||
{
|
||||
name: "new with no Spec.ConfigSource and no Status.Config , enableNodeDynamicConfig disabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: false,
|
||||
node: makeNode(nil, false, false),
|
||||
oldNode: nil,
|
||||
@@ -171,7 +135,6 @@ func TestDropFields(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "new with Spec.ConfigSource and no Status.Config, enableNodeDynamicConfig disabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: false,
|
||||
node: makeNode(nil, true, false),
|
||||
oldNode: nil,
|
||||
@@ -179,7 +142,6 @@ func TestDropFields(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "new with Spec.ConfigSource and Status.Config, enableNodeDynamicConfig disabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: false,
|
||||
node: makeNode(nil, true, true),
|
||||
oldNode: nil,
|
||||
@@ -187,7 +149,6 @@ func TestDropFields(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update with Spec.ConfigSource and Status.Config (old has none), enableNodeDynamicConfig disabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: false,
|
||||
node: makeNode(nil, true, true),
|
||||
oldNode: makeNode(nil, false, false),
|
||||
@@ -195,7 +156,6 @@ func TestDropFields(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update with Spec.ConfigSource and Status.Config (old has them), enableNodeDynamicConfig disabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: false,
|
||||
node: makeNode(nil, true, true),
|
||||
oldNode: makeNode(nil, true, true),
|
||||
@@ -203,7 +163,6 @@ func TestDropFields(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "update with Spec.ConfigSource and Status.Config (old has Status.Config), enableNodeDynamicConfig disabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: false,
|
||||
node: makeNode(nil, true, true),
|
||||
oldNode: makeNode(nil, false, true),
|
||||
@@ -211,7 +170,6 @@ func TestDropFields(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "new with Spec.ConfigSource and Status.Config, enableNodeDynamicConfig enabled",
|
||||
enableDualStack: false,
|
||||
enableNodeDynamicConfig: true,
|
||||
node: makeNode(nil, true, true),
|
||||
oldNode: nil,
|
||||
@@ -221,7 +179,6 @@ func TestDropFields(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
func() {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicKubeletConfig, tc.enableNodeDynamicConfig)()
|
||||
|
||||
dropDisabledFields(tc.node, tc.oldNode)
|
||||
|
@@ -215,7 +215,7 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generi
|
||||
|
||||
// allocator for secondary service ip range
|
||||
var secondaryServiceClusterIPAllocator ipallocator.Interface
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) && c.SecondaryServiceIPRange.IP != nil {
|
||||
if c.SecondaryServiceIPRange.IP != nil {
|
||||
var secondaryServiceClusterIPRegistry rangeallocation.RangeRegistry
|
||||
secondaryServiceClusterIPAllocator, err = ipallocator.New(&c.SecondaryServiceIPRange, func(max int, rangeSpec string) (allocator.Interface, error) {
|
||||
mem := allocator.NewAllocationMap(max, rangeSpec)
|
||||
|
@@ -29,10 +29,6 @@ import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
netutils "k8s.io/utils/net"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
type mockRangeRegistry struct {
|
||||
@@ -328,8 +324,6 @@ func TestShouldWorkOnSecondary(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRepairDualStack(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
fakeClient := fake.NewSimpleClientset()
|
||||
ipregistry := &mockRangeRegistry{
|
||||
item: &api.RangeAllocation{Range: "192.168.1.0/24"},
|
||||
@@ -368,8 +362,6 @@ func TestRepairDualStack(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRepairLeakDualStack(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
_, cidr, _ := netutils.ParseCIDRSloppy("192.168.1.0/24")
|
||||
previous, err := ipallocator.NewInMemory(cidr)
|
||||
if err != nil {
|
||||
@@ -466,7 +458,6 @@ func TestRepairWithExistingDualStack(t *testing.T) {
|
||||
// we can saftly create tests that has ipFamilyPolicy:nil
|
||||
// this will work every where except alloc & validation
|
||||
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
_, cidr, _ := netutils.ParseCIDRSloppy("192.168.1.0/24")
|
||||
previous, err := ipallocator.NewInMemory(cidr)
|
||||
if err != nil {
|
||||
|
@@ -107,12 +107,6 @@ func (al *Allocators) initIPFamilyFields(after After, before Before) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// gate off. We don't need to validate or default new fields
|
||||
// we totally depend on existing validation in apis/validation
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We don't want to auto-upgrade (add an IP) or downgrade (remove an IP)
|
||||
// PreferDualStack services following a cluster change to/from
|
||||
// dual-stackness.
|
||||
@@ -343,10 +337,6 @@ func (al *Allocators) allocClusterIPs(service *api.Service, dryRun bool) (map[ap
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
return al.allocClusterIP(service, dryRun)
|
||||
}
|
||||
|
||||
toAlloc := make(map[api.IPFamily]string)
|
||||
// at this stage, the only fact we know is that service has correct ip families
|
||||
// assigned to it. It may have partial assigned ClusterIPs (Upgrade to dual stack)
|
||||
@@ -391,25 +381,6 @@ func (al *Allocators) allocClusterIPs(service *api.Service, dryRun bool) (map[ap
|
||||
return allocated, err
|
||||
}
|
||||
|
||||
// standard allocator for dualstackgate==Off, hard wired dependency
|
||||
// and ignores policy, families and clusterIPs
|
||||
func (al *Allocators) allocClusterIP(service *api.Service, dryRun bool) (map[api.IPFamily]string, error) {
|
||||
toAlloc := make(map[api.IPFamily]string)
|
||||
|
||||
// get clusterIP.. empty string if user did not specify an ip
|
||||
toAlloc[al.defaultServiceIPFamily] = service.Spec.ClusterIP
|
||||
// alloc
|
||||
allocated, err := al.allocIPs(service, toAlloc, dryRun)
|
||||
|
||||
// set
|
||||
if err == nil {
|
||||
service.Spec.ClusterIP = allocated[al.defaultServiceIPFamily]
|
||||
service.Spec.ClusterIPs = []string{allocated[al.defaultServiceIPFamily]}
|
||||
}
|
||||
|
||||
return allocated, err
|
||||
}
|
||||
|
||||
func (al *Allocators) allocIPs(service *api.Service, toAlloc map[api.IPFamily]string, dryRun bool) (map[api.IPFamily]string, error) {
|
||||
allocated := make(map[api.IPFamily]string)
|
||||
|
||||
@@ -687,24 +658,12 @@ func (al *Allocators) updateClusterIPs(after After, before Before, dryRun bool)
|
||||
// Update service from non-ExternalName to ExternalName, should release ClusterIP if exists.
|
||||
if oldService.Spec.Type != api.ServiceTypeExternalName && service.Spec.Type == api.ServiceTypeExternalName {
|
||||
toRelease = make(map[api.IPFamily]string)
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
// for non dual stack enabled cluster we use clusterIPs
|
||||
toRelease[al.defaultServiceIPFamily] = oldService.Spec.ClusterIP
|
||||
} else {
|
||||
// dual stack is enabled, collect ClusterIPs by families
|
||||
for i, family := range oldService.Spec.IPFamilies {
|
||||
toRelease[family] = oldService.Spec.ClusterIPs[i]
|
||||
}
|
||||
for i, family := range oldService.Spec.IPFamilies {
|
||||
toRelease[family] = oldService.Spec.ClusterIPs[i]
|
||||
}
|
||||
|
||||
return nil, toRelease, nil
|
||||
}
|
||||
|
||||
// upgrade and downgrade are specific to dualstack
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
upgraded := len(oldService.Spec.IPFamilies) == 1 && len(service.Spec.IPFamilies) == 2
|
||||
downgraded := len(oldService.Spec.IPFamilies) == 2 && len(service.Spec.IPFamilies) == 1
|
||||
|
||||
@@ -909,10 +868,6 @@ func (al *Allocators) releaseClusterIPs(service *api.Service) (released map[api.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
return al.releaseClusterIP(service)
|
||||
}
|
||||
|
||||
toRelease := make(map[api.IPFamily]string)
|
||||
for _, ip := range service.Spec.ClusterIPs {
|
||||
if netutils.IsIPv6String(ip) {
|
||||
@@ -924,21 +879,6 @@ func (al *Allocators) releaseClusterIPs(service *api.Service) (released map[api.
|
||||
return al.releaseIPs(toRelease)
|
||||
}
|
||||
|
||||
// for pre dual stack (gate == off). Hardwired to ClusterIP and ignores all new fields
|
||||
func (al *Allocators) releaseClusterIP(service *api.Service) (released map[api.IPFamily]string, err error) {
|
||||
toRelease := make(map[api.IPFamily]string)
|
||||
|
||||
// we need to do that to handle cases where allocator is no longer configured on
|
||||
// cluster
|
||||
if netutils.IsIPv6String(service.Spec.ClusterIP) {
|
||||
toRelease[api.IPv6Protocol] = service.Spec.ClusterIP
|
||||
} else {
|
||||
toRelease[api.IPv4Protocol] = service.Spec.ClusterIP
|
||||
}
|
||||
|
||||
return al.releaseIPs(toRelease)
|
||||
}
|
||||
|
||||
// This is O(N), but we expect haystack to be small;
|
||||
// so small that we expect a linear search to be faster
|
||||
func containsNumber(haystack []int, needle int) bool {
|
||||
|
@@ -36,10 +36,8 @@ import (
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
@@ -242,11 +240,6 @@ func (r *REST) defaultOnReadService(service *api.Service) {
|
||||
// We still want to present a consistent view of them.
|
||||
normalizeClusterIPs(After{service}, Before{nil})
|
||||
|
||||
// The rest of this does not apply unless dual-stack is enabled.
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
return
|
||||
}
|
||||
|
||||
// Set ipFamilies and ipFamilyPolicy if needed.
|
||||
r.defaultOnReadIPFamilies(service)
|
||||
}
|
||||
|
@@ -639,8 +639,6 @@ func TestServiceDefaultOnRead(t *testing.T) {
|
||||
input: &api.Pod{},
|
||||
}}
|
||||
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol})
|
||||
@@ -727,9 +725,6 @@ func helpTestCreateUpdateDeleteWithFamilies(t *testing.T, testCases []cudTestCas
|
||||
// NOTE: do not call t.Helper() here. It's more useful for errors to be
|
||||
// attributed to lines in this function than the caller of it.
|
||||
|
||||
// This test is ONLY with the gate enabled.
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
storage, _, server := newStorage(t, ipFamilies)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
@@ -850,20 +845,18 @@ func verifyEquiv(t testingTInterface, call string, tc *svcTestCase, got *api.Ser
|
||||
if want.Spec.ClusterIP == "" {
|
||||
want.Spec.ClusterIP = got.Spec.ClusterIP
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
if want.Spec.IPFamilyPolicy == nil {
|
||||
want.Spec.IPFamilyPolicy = got.Spec.IPFamilyPolicy
|
||||
}
|
||||
if tc.expectStackDowngrade && len(want.Spec.ClusterIPs) > len(got.Spec.ClusterIPs) {
|
||||
want.Spec.ClusterIPs = want.Spec.ClusterIPs[0:1]
|
||||
} else if len(got.Spec.ClusterIPs) > len(want.Spec.ClusterIPs) {
|
||||
want.Spec.ClusterIPs = append(want.Spec.ClusterIPs, got.Spec.ClusterIPs[len(want.Spec.ClusterIPs):]...)
|
||||
}
|
||||
if tc.expectStackDowngrade && len(want.Spec.IPFamilies) > len(got.Spec.ClusterIPs) {
|
||||
want.Spec.IPFamilies = want.Spec.IPFamilies[0:1]
|
||||
} else if len(got.Spec.IPFamilies) > len(want.Spec.IPFamilies) {
|
||||
want.Spec.IPFamilies = append(want.Spec.IPFamilies, got.Spec.IPFamilies[len(want.Spec.IPFamilies):]...)
|
||||
}
|
||||
if want.Spec.IPFamilyPolicy == nil {
|
||||
want.Spec.IPFamilyPolicy = got.Spec.IPFamilyPolicy
|
||||
}
|
||||
if tc.expectStackDowngrade && len(want.Spec.ClusterIPs) > len(got.Spec.ClusterIPs) {
|
||||
want.Spec.ClusterIPs = want.Spec.ClusterIPs[0:1]
|
||||
} else if len(got.Spec.ClusterIPs) > len(want.Spec.ClusterIPs) {
|
||||
want.Spec.ClusterIPs = append(want.Spec.ClusterIPs, got.Spec.ClusterIPs[len(want.Spec.ClusterIPs):]...)
|
||||
}
|
||||
if tc.expectStackDowngrade && len(want.Spec.IPFamilies) > len(got.Spec.ClusterIPs) {
|
||||
want.Spec.IPFamilies = want.Spec.IPFamilies[0:1]
|
||||
} else if len(got.Spec.IPFamilies) > len(want.Spec.IPFamilies) {
|
||||
want.Spec.IPFamilies = append(want.Spec.IPFamilies, got.Spec.IPFamilies[len(want.Spec.IPFamilies):]...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,8 +1014,6 @@ func TestVerifyEquiv(t *testing.T) {
|
||||
expect: false,
|
||||
}}
|
||||
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := verifyEquiv(fakeTestingT{t}, "test", &tc.input, tc.output)
|
||||
@@ -1098,22 +1089,14 @@ func proveClusterIPsAllocated(t *testing.T, storage *wrapperRESTForTests, before
|
||||
t.Errorf("%s: expected clusterIP == clusterIPs[0]: %q != %q", callName(before, after), sing, plur)
|
||||
}
|
||||
|
||||
clips := []string{}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
clips = after.Spec.ClusterIPs
|
||||
} else {
|
||||
clips = append(clips, after.Spec.ClusterIP)
|
||||
}
|
||||
for _, clip := range clips {
|
||||
for _, clip := range after.Spec.ClusterIPs {
|
||||
if !ipIsAllocated(t, storage.alloc.serviceIPAllocatorsByFamily[familyOf(clip)], clip) {
|
||||
t.Errorf("%s: expected clusterIP to be allocated: %q", callName(before, after), clip)
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
if lc, lf := len(after.Spec.ClusterIPs), len(after.Spec.IPFamilies); lc != lf {
|
||||
t.Errorf("%s: expected same number of clusterIPs and ipFamilies: %d != %d", callName(before, after), lc, lf)
|
||||
}
|
||||
if lc, lf := len(after.Spec.ClusterIPs), len(after.Spec.IPFamilies); lc != lf {
|
||||
t.Errorf("%s: expected same number of clusterIPs and ipFamilies: %d != %d", callName(before, after), lc, lf)
|
||||
}
|
||||
|
||||
for i, fam := range after.Spec.IPFamilies {
|
||||
@@ -1122,23 +1105,21 @@ func proveClusterIPsAllocated(t *testing.T, storage *wrapperRESTForTests, before
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
if after.Spec.IPFamilyPolicy == nil {
|
||||
t.Errorf("%s: expected ipFamilyPolicy to be set", callName(before, after))
|
||||
} else {
|
||||
pol := *after.Spec.IPFamilyPolicy
|
||||
fams := len(after.Spec.IPFamilies)
|
||||
clus := 1
|
||||
if storage.secondaryIPFamily != "" {
|
||||
clus = 2
|
||||
}
|
||||
if pol == api.IPFamilyPolicySingleStack && fams != 1 {
|
||||
t.Errorf("%s: expected 1 ipFamily, got %d", callName(before, after), fams)
|
||||
} else if pol == api.IPFamilyPolicyRequireDualStack && fams != 2 {
|
||||
t.Errorf("%s: expected 2 ipFamilies, got %d", callName(before, after), fams)
|
||||
} else if pol == api.IPFamilyPolicyPreferDualStack && fams != clus {
|
||||
t.Errorf("%s: expected %d ipFamilies, got %d", callName(before, after), clus, fams)
|
||||
}
|
||||
if after.Spec.IPFamilyPolicy == nil {
|
||||
t.Errorf("%s: expected ipFamilyPolicy to be set", callName(before, after))
|
||||
} else {
|
||||
pol := *after.Spec.IPFamilyPolicy
|
||||
fams := len(after.Spec.IPFamilies)
|
||||
clus := 1
|
||||
if storage.secondaryIPFamily != "" {
|
||||
clus = 2
|
||||
}
|
||||
if pol == api.IPFamilyPolicySingleStack && fams != 1 {
|
||||
t.Errorf("%s: expected 1 ipFamily, got %d", callName(before, after), fams)
|
||||
} else if pol == api.IPFamilyPolicyRequireDualStack && fams != 2 {
|
||||
t.Errorf("%s: expected 2 ipFamilies, got %d", callName(before, after), fams)
|
||||
} else if pol == api.IPFamilyPolicyPreferDualStack && fams != clus {
|
||||
t.Errorf("%s: expected %d ipFamilies, got %d", callName(before, after), clus, fams)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1180,13 +1161,7 @@ func proveClusterIPsDeallocated(t *testing.T, storage *wrapperRESTForTests, befo
|
||||
}
|
||||
|
||||
if before != nil && before.Spec.ClusterIP != api.ClusterIPNone {
|
||||
clips := []string{}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
clips = before.Spec.ClusterIPs
|
||||
} else {
|
||||
clips = append(clips, before.Spec.ClusterIP)
|
||||
}
|
||||
for _, clip := range clips {
|
||||
for _, clip := range before.Spec.ClusterIPs {
|
||||
if ipIsAllocated(t, storage.alloc.serviceIPAllocatorsByFamily[familyOf(clip)], clip) {
|
||||
t.Errorf("%s: expected clusterIP to be deallocated: %q", callName(before, after), clip)
|
||||
}
|
||||
@@ -1289,35 +1264,10 @@ func TestCreateIgnoresIPsForExternalName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
clusterFamilies []api.IPFamily
|
||||
enableDualStack bool
|
||||
cases []testCase
|
||||
}{{
|
||||
name: "singlestack:v4_gate:off",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: false,
|
||||
cases: []testCase{{
|
||||
name: "Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "Policy:SingleStack_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "Policy:PreferDualStack_Families:v4v6",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)),
|
||||
}, {
|
||||
name: "Policy:RequireDualStack_Families:v6v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||
}},
|
||||
}, {
|
||||
name: "singlestack:v6_gate:on",
|
||||
name: "singlestack:v6",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
cases: []testCase{{
|
||||
name: "Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo"),
|
||||
@@ -1341,32 +1291,8 @@ func TestCreateIgnoresIPsForExternalName(t *testing.T) {
|
||||
expectError: true,
|
||||
}},
|
||||
}, {
|
||||
name: "dualstack:v4v6_gate:off",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol},
|
||||
enableDualStack: false,
|
||||
cases: []testCase{{
|
||||
name: "Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "Policy:SingleStack_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "Policy:PreferDualStack_Families:v4v6",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)),
|
||||
}, {
|
||||
name: "Policy:RequireDualStack_Families:v6v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||
}},
|
||||
}, {
|
||||
name: "dualstack:v6v4_gate:on",
|
||||
name: "dualstack:v6v4",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol},
|
||||
enableDualStack: true,
|
||||
cases: []testCase{{
|
||||
name: "Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo"),
|
||||
@@ -1393,8 +1319,6 @@ func TestCreateIgnoresIPsForExternalName(t *testing.T) {
|
||||
|
||||
for _, otc := range testCases {
|
||||
t.Run(otc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, otc.enableDualStack)()
|
||||
|
||||
storage, _, server := newStorage(t, otc.clusterFamilies)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
@@ -1437,159 +1361,6 @@ func TestCreateIgnoresIPsForExternalName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Prove that create ignores IPFamily stuff when dual-stack is disabled.
|
||||
func TestCreateIgnoresIPFamilyWithoutDualStack(t *testing.T) {
|
||||
// These cases were chosen from the full gamut to ensure all "interesting"
|
||||
// cases are covered.
|
||||
testCases := []struct {
|
||||
name string
|
||||
svc *api.Service
|
||||
}{
|
||||
//----------------------------------------
|
||||
// ClusterIP:unset
|
||||
//----------------------------------------
|
||||
{
|
||||
name: "ClusterIP:unset_Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:unset_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:unset_Families:v6v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:SingleStack_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:SingleStack_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:SingleStack_Families:v6v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:PreferDualStack_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:PreferDualStack_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:PreferDualStack_Families:v6v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack),
|
||||
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:RequireDualStack_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:RequireDualStack_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIP:unset_Policy:RequireDualStack_Families:v6v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||
},
|
||||
//----------------------------------------
|
||||
// ClusterIPs:v4v6
|
||||
//----------------------------------------
|
||||
{
|
||||
name: "ClusterIPs:v4v6_Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetClusterIPs("10.0.0.1", "2000::1")),
|
||||
}, {
|
||||
name: "ClusterIPs:v4v6_Policy:unset_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetClusterIPs("10.0.0.1", "2000::1"),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "ClusterIPs:v4v6_Policy:RequireDualStack_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetClusterIPs("10.0.0.1", "2000::1"),
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)),
|
||||
},
|
||||
//----------------------------------------
|
||||
// Headless
|
||||
//----------------------------------------
|
||||
{
|
||||
name: "Headless_Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetHeadless),
|
||||
}, {
|
||||
name: "Headless_Policy:unset_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetHeadless,
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "Headless_Policy:RequireDualStack_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetHeadless,
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)),
|
||||
},
|
||||
//----------------------------------------
|
||||
// HeadlessSelectorless
|
||||
//----------------------------------------
|
||||
{
|
||||
name: "HeadlessSelectorless_Policy:unset_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetHeadless,
|
||||
svctest.SetSelector(nil)),
|
||||
}, {
|
||||
name: "HeadlessSelectorless_Policy:unset_Families:v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetHeadless,
|
||||
svctest.SetSelector(nil),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||
}, {
|
||||
name: "HeadlessSelectorless_Policy:RequireDualStack_Families:unset",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetHeadless,
|
||||
svctest.SetSelector(nil),
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)),
|
||||
},
|
||||
}
|
||||
|
||||
// This test is ONLY with the gate off.
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, false)()
|
||||
|
||||
// Do this in the outer scope for performance.
|
||||
storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol})
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := genericapirequest.NewDefaultContext()
|
||||
createdObj, err := storage.Create(ctx, tc.svc, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating service: %v", err)
|
||||
}
|
||||
defer storage.Delete(ctx, tc.svc.Name, rest.ValidateAllObjectFunc, &metav1.DeleteOptions{})
|
||||
createdSvc := createdObj.(*api.Service)
|
||||
|
||||
// The gate is off - these should always be empty.
|
||||
if want, got := fmtIPFamilyPolicy(nil), fmtIPFamilyPolicy(createdSvc.Spec.IPFamilyPolicy); want != got {
|
||||
t.Errorf("wrong IPFamilyPolicy: want %s, got %s", want, got)
|
||||
}
|
||||
if want, got := fmtIPFamilies(nil), fmtIPFamilies(createdSvc.Spec.IPFamilies); want != got {
|
||||
t.Errorf("wrong IPFamilies: want %s, got %s", want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Prove that create initializes clusterIPs from clusterIP. This simplifies
|
||||
// later tests to not need to re-prove this.
|
||||
func TestCreateInitClusterIPsFromClusterIP(t *testing.T) {
|
||||
@@ -1635,9 +1406,6 @@ func TestCreateInitClusterIPsFromClusterIP(t *testing.T) {
|
||||
svctest.SetClusterIP("2000::1")),
|
||||
}}
|
||||
|
||||
// This test is ONLY with the gate enabled.
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
storage, _, server := newStorage(t, tc.clusterFamilies)
|
||||
@@ -6093,9 +5861,6 @@ func TestCreateInitIPFields(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// This test is ONLY with the gate enabled.
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
for _, otc := range testCases {
|
||||
t.Run(otc.name, func(t *testing.T) {
|
||||
|
||||
@@ -6250,8 +6015,6 @@ func TestCreateInvalidClusterIPInputs(t *testing.T) {
|
||||
expect: []string{"must be a valid IP"},
|
||||
}}
|
||||
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
storage, _, server := newStorage(t, tc.families)
|
||||
@@ -6291,9 +6054,6 @@ func TestCreateDeleteReuse(t *testing.T) {
|
||||
svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)),
|
||||
}}
|
||||
|
||||
// This test is ONLY with the gate enabled.
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol})
|
||||
@@ -6729,47 +6489,38 @@ func TestCreateSkipsAllocationsForHeadless(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
clusterFamilies []api.IPFamily
|
||||
enableDualStack bool
|
||||
svc *api.Service
|
||||
expectError bool
|
||||
}{{
|
||||
name: "singlestack:v4_gate:off",
|
||||
name: "singlestack:v4",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "singlestack:v6_gate:on",
|
||||
name: "singlestack:v6",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "dualstack:v4v6_gate:off",
|
||||
name: "dualstack:v4v6",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol},
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "dualstack:v6v4_gate:on",
|
||||
name: "dualstack:v6v4",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "singlestack:v4_gate:off_type:NodePort",
|
||||
name: "singlestack:v4_type:NodePort",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo", svctest.SetTypeNodePort),
|
||||
expectError: true,
|
||||
}, {
|
||||
name: "singlestack:v6_gate:on_type:LoadBalancer",
|
||||
name: "singlestack:v6_type:LoadBalancer",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo", svctest.SetTypeLoadBalancer),
|
||||
expectError: true,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
|
||||
storage, _, server := newStorage(t, tc.clusterFamilies)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
@@ -6805,54 +6556,43 @@ func TestCreateDryRun(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
clusterFamilies []api.IPFamily
|
||||
enableDualStack bool
|
||||
svc *api.Service
|
||||
}{{
|
||||
name: "singlestack:v4_gate:off_clusterip:unset",
|
||||
name: "singlestack:v4_clusterip:unset",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "singlestack:v4_gate:off_clusterip:set",
|
||||
name: "singlestack:v4_clusterip:set",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1")),
|
||||
}, {
|
||||
name: "singlestack:v6_gate:on_clusterip:unset",
|
||||
name: "singlestack:v6_clusterip:unset",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo"),
|
||||
}, {
|
||||
name: "singlestack:v6_gate:on_clusterip:set",
|
||||
name: "singlestack:v6_clusterip:set",
|
||||
clusterFamilies: []api.IPFamily{api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1")),
|
||||
}, {
|
||||
name: "dualstack:v4v6_gate:on_clusterip:unset",
|
||||
name: "dualstack:v4v6_clusterip:unset",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo", svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)),
|
||||
}, {
|
||||
name: "dualstack:v4v6_gate:on_clusterip:set",
|
||||
name: "dualstack:v4v6_clusterip:set",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo", svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), svctest.SetClusterIPs("10.0.0.1", "2000::1")),
|
||||
}, {
|
||||
name: "singlestack:v4_gate:off_type:NodePort_nodeport:unset",
|
||||
name: "singlestack:v4_type:NodePort_nodeport:unset",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo", svctest.SetTypeNodePort),
|
||||
}, {
|
||||
name: "singlestack:v4_gate:on_type:LoadBalancer_nodePort:set",
|
||||
name: "singlestack:v4_type:LoadBalancer_nodePort:set",
|
||||
clusterFamilies: []api.IPFamily{api.IPv4Protocol},
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo", svctest.SetTypeLoadBalancer, svctest.SetUniqueNodePorts),
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
|
||||
storage, _, server := newStorage(t, tc.clusterFamilies)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
@@ -6886,9 +6626,6 @@ func TestCreateDryRun(t *testing.T) {
|
||||
func TestDeleteWithFinalizer(t *testing.T) {
|
||||
svcName := "foo"
|
||||
|
||||
// This test is ONLY with the gate enabled.
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)()
|
||||
|
||||
storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol})
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
@@ -6962,33 +6699,29 @@ func TestDeleteWithFinalizer(t *testing.T) {
|
||||
// Prove that a dry-run delete doesn't actually deallocate IPs or ports.
|
||||
func TestDeleteDryRun(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
enableDualStack bool
|
||||
svc *api.Service
|
||||
}{{
|
||||
name: "gate:off",
|
||||
enableDualStack: false,
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetTypeLoadBalancer,
|
||||
svctest.SetExternalTrafficPolicy(api.ServiceExternalTrafficPolicyTypeLocal)),
|
||||
}, {
|
||||
name: "gate:on",
|
||||
enableDualStack: true,
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetTypeLoadBalancer,
|
||||
svctest.SetExternalTrafficPolicy(api.ServiceExternalTrafficPolicyTypeLocal)),
|
||||
}}
|
||||
name string
|
||||
svc *api.Service
|
||||
}{
|
||||
{
|
||||
name: "v4",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetTypeLoadBalancer,
|
||||
svctest.SetIPFamilies(api.IPv4Protocol),
|
||||
svctest.SetExternalTrafficPolicy(api.ServiceExternalTrafficPolicyTypeLocal)),
|
||||
},
|
||||
{
|
||||
name: "v4v6",
|
||||
svc: svctest.MakeService("foo",
|
||||
svctest.SetTypeLoadBalancer,
|
||||
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||
svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol),
|
||||
svctest.SetExternalTrafficPolicy(api.ServiceExternalTrafficPolicyTypeLocal)),
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
|
||||
families := []api.IPFamily{api.IPv4Protocol}
|
||||
if tc.enableDualStack {
|
||||
families = append(families, api.IPv6Protocol)
|
||||
}
|
||||
|
||||
storage, _, server := newStorage(t, families)
|
||||
storage, _, server := newStorage(t, tc.svc.Spec.IPFamilies)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
|
||||
|
@@ -162,14 +162,6 @@ func (svcStrategy) AllowUnconditionalUpdate() bool {
|
||||
// newSvc.Spec.MyFeature = nil
|
||||
// }
|
||||
func dropServiceDisabledFields(newSvc *api.Service, oldSvc *api.Service) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) && !serviceDualStackFieldsInUse(oldSvc) {
|
||||
newSvc.Spec.IPFamilies = nil
|
||||
newSvc.Spec.IPFamilyPolicy = nil
|
||||
if len(newSvc.Spec.ClusterIPs) > 1 {
|
||||
newSvc.Spec.ClusterIPs = newSvc.Spec.ClusterIPs[0:1]
|
||||
}
|
||||
}
|
||||
|
||||
// Clear AllocateLoadBalancerNodePorts if ServiceLBNodePortControl is not enabled
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ServiceLBNodePortControl) {
|
||||
if !allocateLoadBalancerNodePortsInUse(oldSvc) {
|
||||
@@ -211,19 +203,6 @@ func allocateLoadBalancerNodePortsInUse(svc *api.Service) bool {
|
||||
return svc.Spec.AllocateLoadBalancerNodePorts != nil
|
||||
}
|
||||
|
||||
// returns true if svc.Spec.ServiceIPFamily field is in use
|
||||
func serviceDualStackFieldsInUse(svc *api.Service) bool {
|
||||
if svc == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ipFamilyPolicyInUse := svc.Spec.IPFamilyPolicy != nil
|
||||
ipFamiliesInUse := len(svc.Spec.IPFamilies) > 0
|
||||
ClusterIPsInUse := len(svc.Spec.ClusterIPs) > 1
|
||||
|
||||
return ipFamilyPolicyInUse || ipFamiliesInUse || ClusterIPsInUse
|
||||
}
|
||||
|
||||
// returns true when the svc.Status.Conditions field is in use.
|
||||
func serviceConditionsInUse(svc *api.Service) bool {
|
||||
if svc == nil {
|
||||
|
@@ -144,15 +144,6 @@ func TestServiceStatusStrategy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeServiceWithIPFamilies(ipfamilies []api.IPFamily, ipFamilyPolicy *api.IPFamilyPolicyType) *api.Service {
|
||||
return &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
IPFamilies: ipfamilies,
|
||||
IPFamilyPolicy: ipFamilyPolicy,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeServiceWithConditions(conditions []metav1.Condition) *api.Service {
|
||||
return &api.Service{
|
||||
Status: api.ServiceStatus{
|
||||
@@ -192,15 +183,10 @@ func makeServiceWithInternalTrafficPolicy(policy *api.ServiceInternalTrafficPoli
|
||||
}
|
||||
|
||||
func TestDropDisabledField(t *testing.T) {
|
||||
requireDualStack := api.IPFamilyPolicyRequireDualStack
|
||||
preferDualStack := api.IPFamilyPolicyPreferDualStack
|
||||
singleStack := api.IPFamilyPolicySingleStack
|
||||
|
||||
localInternalTrafficPolicy := api.ServiceInternalTrafficPolicyLocal
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
enableDualStack bool
|
||||
enableMixedProtocol bool
|
||||
enableLoadBalancerClass bool
|
||||
enableInternalTrafficPolicy bool
|
||||
@@ -208,64 +194,6 @@ func TestDropDisabledField(t *testing.T) {
|
||||
oldSvc *api.Service
|
||||
compareSvc *api.Service
|
||||
}{
|
||||
{
|
||||
name: "not dual stack, field not used",
|
||||
enableDualStack: false,
|
||||
svc: makeServiceWithIPFamilies(nil, nil),
|
||||
oldSvc: nil,
|
||||
compareSvc: makeServiceWithIPFamilies(nil, nil),
|
||||
},
|
||||
{
|
||||
name: "not dual stack, field used in old and new",
|
||||
enableDualStack: false,
|
||||
svc: makeServiceWithIPFamilies([]api.IPFamily{api.IPv4Protocol}, nil),
|
||||
oldSvc: makeServiceWithIPFamilies([]api.IPFamily{api.IPv4Protocol}, nil),
|
||||
compareSvc: makeServiceWithIPFamilies([]api.IPFamily{api.IPv4Protocol}, nil),
|
||||
},
|
||||
{
|
||||
name: "dualstack, field used",
|
||||
enableDualStack: true,
|
||||
svc: makeServiceWithIPFamilies([]api.IPFamily{api.IPv6Protocol}, nil),
|
||||
oldSvc: nil,
|
||||
compareSvc: makeServiceWithIPFamilies([]api.IPFamily{api.IPv6Protocol}, nil),
|
||||
},
|
||||
/* preferDualStack field */
|
||||
{
|
||||
name: "not dual stack, fields is not use",
|
||||
enableDualStack: false,
|
||||
svc: makeServiceWithIPFamilies(nil, nil),
|
||||
oldSvc: nil,
|
||||
compareSvc: makeServiceWithIPFamilies(nil, nil),
|
||||
},
|
||||
{
|
||||
name: "not dual stack, fields used in new, not in old",
|
||||
enableDualStack: false,
|
||||
svc: makeServiceWithIPFamilies(nil, &preferDualStack),
|
||||
oldSvc: nil,
|
||||
compareSvc: makeServiceWithIPFamilies(nil, nil),
|
||||
},
|
||||
{
|
||||
name: "not dual stack, fields used in new, not in old",
|
||||
enableDualStack: false,
|
||||
svc: makeServiceWithIPFamilies(nil, &requireDualStack),
|
||||
oldSvc: nil,
|
||||
compareSvc: makeServiceWithIPFamilies(nil, nil),
|
||||
},
|
||||
|
||||
{
|
||||
name: "not dual stack, fields not used in old (single stack)",
|
||||
enableDualStack: false,
|
||||
svc: makeServiceWithIPFamilies(nil, nil),
|
||||
oldSvc: makeServiceWithIPFamilies(nil, &singleStack),
|
||||
compareSvc: makeServiceWithIPFamilies(nil, nil),
|
||||
},
|
||||
{
|
||||
name: "dualstack, field used",
|
||||
enableDualStack: true,
|
||||
svc: makeServiceWithIPFamilies(nil, &singleStack),
|
||||
oldSvc: nil,
|
||||
compareSvc: makeServiceWithIPFamilies(nil, &singleStack),
|
||||
},
|
||||
/* svc.Status.Conditions */
|
||||
{
|
||||
name: "mixed protocol not enabled, field not used in old, not used in new",
|
||||
@@ -463,7 +391,6 @@ func TestDropDisabledField(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
func() {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MixedProtocolLBService, tc.enableMixedProtocol)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceLoadBalancerClass, tc.enableLoadBalancerClass)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceInternalTrafficPolicy, tc.enableInternalTrafficPolicy)()
|
||||
|
@@ -89,6 +89,9 @@ type Interface interface {
|
||||
// mapped to the same IP:PORT and consequently some suffer packet
|
||||
// drops.
|
||||
HasRandomFully() bool
|
||||
|
||||
// Present checks if the kernel supports the iptable interface
|
||||
Present() bool
|
||||
}
|
||||
|
||||
// Protocol defines the ip protocol either ipv4 or ipv6
|
||||
@@ -723,6 +726,16 @@ func (runner *runner) HasRandomFully() bool {
|
||||
return runner.hasRandomFully
|
||||
}
|
||||
|
||||
// Present tests if iptable is supported on current kernel by checking the existence
|
||||
// of default table and chain
|
||||
func (runner *runner) Present() bool {
|
||||
if _, err := runner.ChainExists(TableNAT, ChainPostrouting); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var iptablesNotFoundStrings = []string{
|
||||
// iptables-legacy [-A|-I] BAD-CHAIN [...]
|
||||
// iptables-legacy [-C|-D] GOOD-CHAIN [...non-matching rule...]
|
||||
|
@@ -176,4 +176,8 @@ func (f *FakeIPTables) HasRandomFully() bool {
|
||||
return f.hasRandomFully
|
||||
}
|
||||
|
||||
func (f *FakeIPTables) Present() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var _ = iptables.Interface(&FakeIPTables{})
|
||||
|
@@ -33,10 +33,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
@@ -122,12 +120,10 @@ func GetNodeHostIPs(node *v1.Node) ([]net.IP, error) {
|
||||
}
|
||||
|
||||
nodeIPs := []net.IP{allIPs[0]}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
for _, ip := range allIPs {
|
||||
if netutils.IsIPv6(ip) != netutils.IsIPv6(nodeIPs[0]) {
|
||||
nodeIPs = append(nodeIPs, ip)
|
||||
break
|
||||
}
|
||||
for _, ip := range allIPs {
|
||||
if netutils.IsIPv6(ip) != netutils.IsIPv6(nodeIPs[0]) {
|
||||
nodeIPs = append(nodeIPs, ip)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,9 +23,6 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
@@ -99,7 +96,6 @@ func TestGetNodeHostIPs(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
addresses []v1.NodeAddress
|
||||
dualStack bool
|
||||
|
||||
expectIPs []net.IP
|
||||
}{
|
||||
@@ -141,7 +137,7 @@ func TestGetNodeHostIPs(t *testing.T) {
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("4.3.2.1")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, single-stack cluster",
|
||||
name: "dual-stack node",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
@@ -149,52 +145,50 @@ func TestGetNodeHostIPs(t *testing.T) {
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
},
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, dual-stack cluster",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
},
|
||||
dualStack: true,
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, different order, single-stack cluster",
|
||||
name: "dual-stack node",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
},
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, different order, dual-stack cluster",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
},
|
||||
dualStack: true,
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, IPv6-first, no internal IPv4, single-stack cluster",
|
||||
name: "dual-stack node, different order",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
},
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, different order",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "1.2.3.4"},
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
},
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("1.2.3.4"), netutils.ParseIPSloppy("a:b::c:d")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, IPv6-first, no internal IPv4",
|
||||
addresses: []v1.NodeAddress{
|
||||
{Type: v1.NodeInternalIP, Address: "a:b::c:d"},
|
||||
{Type: v1.NodeExternalIP, Address: "d:c::b:a"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
},
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d")},
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d"), netutils.ParseIPSloppy("4.3.2.1")},
|
||||
},
|
||||
{
|
||||
name: "dual-stack node, IPv6-first, no internal IPv4, dual-stack cluster",
|
||||
@@ -204,14 +198,12 @@ func TestGetNodeHostIPs(t *testing.T) {
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "4.3.2.2"},
|
||||
},
|
||||
dualStack: true,
|
||||
expectIPs: []net.IP{netutils.ParseIPSloppy("a:b::c:d"), netutils.ParseIPSloppy("4.3.2.1")},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.dualStack)()
|
||||
node := &v1.Node{
|
||||
Status: v1.NodeStatus{Addresses: tc.addresses},
|
||||
}
|
||||
|
Reference in New Issue
Block a user