mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-17 07:03:31 +00:00
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>
This commit is contained in:
committed by
GitHub
parent
d0e06cf3e0
commit
6675eba3ef
@@ -55,9 +55,12 @@ func validateClusterIPFlags(options *ServerRunOptions) []error {
|
||||
}
|
||||
|
||||
// Secondary IP validation
|
||||
// while api-server dualstack bits does not have dependency on EndPointSlice, its
|
||||
// a good idea to have validation consistent across all components (ControllerManager
|
||||
// needs EndPointSlice + DualStack feature flags).
|
||||
secondaryServiceClusterIPRangeUsed := (options.SecondaryServiceClusterIPRange.IP != nil)
|
||||
if secondaryServiceClusterIPRangeUsed && !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||
errs = append(errs, fmt.Errorf("--secondary-service-cluster-ip-range can only be used if %v feature is enabled", string(features.IPv6DualStack)))
|
||||
if secondaryServiceClusterIPRangeUsed && (!utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) || !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSlice)) {
|
||||
errs = append(errs, fmt.Errorf("secondary service cluster-ip range(--service-cluster-ip-range[1]) can only be used if %v and %v feature is enabled", string(features.IPv6DualStack), string(features.EndpointSlice)))
|
||||
}
|
||||
|
||||
// note: While the cluster might be dualstack (i.e. pods with multiple IPs), the user may choose
|
||||
@@ -68,14 +71,14 @@ func validateClusterIPFlags(options *ServerRunOptions) []error {
|
||||
// Should be dualstack IPFamily(PrimaryServiceClusterIPRange) != IPFamily(SecondaryServiceClusterIPRange)
|
||||
dualstack, err := netutils.IsDualStackCIDRs([]*net.IPNet{&options.PrimaryServiceClusterIPRange, &options.SecondaryServiceClusterIPRange})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.New("error attempting to validate dualstack for --service-cluster-ip-range and --secondary-service-cluster-ip-range"))
|
||||
errs = append(errs, fmt.Errorf("error attempting to validate dualstack for --service-cluster-ip-range value error:%v", err))
|
||||
}
|
||||
|
||||
if !dualstack {
|
||||
errs = append(errs, errors.New("--service-cluster-ip-range and --secondary-service-cluster-ip-range must be of different IP family"))
|
||||
errs = append(errs, errors.New("--service-cluster-ip-range[0] and --service-cluster-ip-range[1] must be of different IP family"))
|
||||
}
|
||||
|
||||
if err := validateMaxCIDRRange(options.SecondaryServiceClusterIPRange, maxCIDRBits, "--secondary-service-cluster-ip-range"); err != nil {
|
||||
if err := validateMaxCIDRRange(options.SecondaryServiceClusterIPRange, maxCIDRBits, "--service-cluster-ip-range[1]"); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
@@ -54,10 +54,11 @@ func makeOptionsWithCIDRs(serviceCIDR string, secondaryServiceCIDR string) *Serv
|
||||
|
||||
func TestClusterSerivceIPRange(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
options *ServerRunOptions
|
||||
enableDualStack bool
|
||||
expectErrors bool
|
||||
name string
|
||||
options *ServerRunOptions
|
||||
enableDualStack bool
|
||||
enableEndpointSlice bool
|
||||
expectErrors bool
|
||||
}{
|
||||
{
|
||||
name: "no service cidr",
|
||||
@@ -66,10 +67,11 @@ func TestClusterSerivceIPRange(t *testing.T) {
|
||||
enableDualStack: false,
|
||||
},
|
||||
{
|
||||
name: "only secondary service cidr, dual stack gate on",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("", "10.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
name: "only secondary service cidr, dual stack gate on",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("", "10.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
{
|
||||
name: "only secondary service cidr, dual stack gate off",
|
||||
@@ -78,16 +80,18 @@ func TestClusterSerivceIPRange(t *testing.T) {
|
||||
enableDualStack: false,
|
||||
},
|
||||
{
|
||||
name: "primary and secondary are provided but not dual stack v4-v4",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "11.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
name: "primary and secondary are provided but not dual stack v4-v4",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "11.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
{
|
||||
name: "primary and secondary are provided but not dual stack v6-v6",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("2000::/108", "3000::/108"),
|
||||
enableDualStack: true,
|
||||
name: "primary and secondary are provided but not dual stack v6-v6",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("2000::/108", "3000::/108"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
{
|
||||
name: "valid dual stack with gate disabled",
|
||||
@@ -96,16 +100,33 @@ func TestClusterSerivceIPRange(t *testing.T) {
|
||||
enableDualStack: false,
|
||||
},
|
||||
{
|
||||
name: "service cidr to big",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/8", ""),
|
||||
enableDualStack: true,
|
||||
name: "service cidr is too big",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/8", ""),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
{
|
||||
name: "dual-stack secondary cidr to big",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "3000::/64"),
|
||||
enableDualStack: true,
|
||||
name: "dual-stack secondary cidr too big",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "3000::/64"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
{
|
||||
name: "valid v6-v4 dual stack + gate on + endpointSlice gate is on",
|
||||
expectErrors: false,
|
||||
options: makeOptionsWithCIDRs("3000::/108", "10.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "valid v4-v6 dual stack + gate on + endpointSlice is off",
|
||||
expectErrors: true,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "3000::/108"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: false,
|
||||
},
|
||||
/* success cases */
|
||||
{
|
||||
@@ -115,22 +136,25 @@ func TestClusterSerivceIPRange(t *testing.T) {
|
||||
enableDualStack: false,
|
||||
},
|
||||
{
|
||||
name: "valid v4-v6 dual stack + gate on",
|
||||
expectErrors: false,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "3000::/108"),
|
||||
enableDualStack: true,
|
||||
name: "valid v4-v6 dual stack + gate on",
|
||||
expectErrors: false,
|
||||
options: makeOptionsWithCIDRs("10.0.0.0/16", "3000::/108"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
{
|
||||
name: "valid v6-v4 dual stack + gate on",
|
||||
expectErrors: false,
|
||||
options: makeOptionsWithCIDRs("3000::/108", "10.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
name: "valid v6-v4 dual stack + gate on",
|
||||
expectErrors: false,
|
||||
options: makeOptionsWithCIDRs("3000::/108", "10.0.0.0/16"),
|
||||
enableDualStack: true,
|
||||
enableEndpointSlice: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, tc.enableDualStack)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.EndpointSlice, tc.enableEndpointSlice)()
|
||||
errs := validateClusterIPFlags(tc.options)
|
||||
if len(errs) > 0 && !tc.expectErrors {
|
||||
t.Errorf("expected no errors, errors found %+v", errs)
|
||||
|
Reference in New Issue
Block a user