Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dual stack services #91824

Merged
merged 22 commits into from Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
42ba40c
api: structure change
khenidak Jul 13, 2020
c01117f
api: defaulting, conversion, and validation
khenidak Oct 16, 2020
790ca33
[FIX] validation: auto remove second ip/family when service changes t…
khenidak Oct 23, 2020
91481de
[FIX] api: defaulting, conversion, and validation
khenidak Oct 26, 2020
4122adc
api-server: clusterIPs alloc, printers, storage and strategy
khenidak Jul 13, 2020
158d626
[FIX] clusterIPs default on read
khenidak Oct 26, 2020
663a967
alloc: auto remove second ip/family when service changes to SingleStack
khenidak Oct 23, 2020
3a235c6
api-server: repair loop handling for clusterIPs
khenidak Jun 23, 2020
7587f57
api-server: force kubernetes default service into single stack
khenidak Jun 29, 2020
8451a97
api-server: tie dualstack feature flag with endpoint feature flag
khenidak Jun 29, 2020
184cbe8
controller-manager: feature flag, endpoint, and endpointSlice control…
khenidak Oct 19, 2020
6cf8251
[FIX] controller-manager: feature flag, endpoint, and endpointSliceco…
khenidak Oct 26, 2020
543cc29
kube-proxy: feature-flag, utils, proxier, and meta proxier
khenidak Jul 9, 2020
61eef7a
[FIX] kubeproxy: call both proxier at the same time
khenidak Oct 26, 2020
8b78122
kubenet: remove forced pod IP sorting
khenidak Jul 8, 2020
7b7afdd
kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFam…
khenidak Jun 29, 2020
59569ab
e2e: fix tests that depends on IPFamily field AND add dual stack tests
khenidak Jul 8, 2020
675362d
e2e: fix expected error message for ClusterIP immutability
khenidak Oct 5, 2020
07f6752
add integration tests for dualstack
Sep 25, 2020
96e26b5
[FIX] add integration tests for dualstack
khenidak Oct 26, 2020
52a1925
generated data
khenidak Oct 22, 2020
14b3451
generated files
khenidak Oct 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 18 additions & 2 deletions api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 8 additions & 5 deletions cmd/kube-apiserver/app/options/validation.go
Expand Up @@ -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)) {
thockin marked this conversation as resolved.
Show resolved Hide resolved
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)))
khenidak marked this conversation as resolved.
Show resolved Hide resolved
}

// note: While the cluster might be dualstack (i.e. pods with multiple IPs), the user may choose
Expand All @@ -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"))
khenidak marked this conversation as resolved.
Show resolved Hide resolved
}

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)
}
}
Expand Down
88 changes: 56 additions & 32 deletions cmd/kube-apiserver/app/options/validation_test.go
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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 */
{
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions cmd/kube-controller-manager/app/core.go
Expand Up @@ -110,14 +110,14 @@ func startNodeIpamController(ctx ControllerContext) (http.Handler, bool, error)
return nil, false, err
}

// failure: more than one cidr and dual stack is not enabled
if len(clusterCIDRs) > 1 && !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
return nil, false, fmt.Errorf("len of ClusterCIDRs==%v and dualstack feature is not enabled", len(clusterCIDRs))
// failure: more than one cidr and dual stack is not enabled and/or endpoint slice is not enabled
if len(clusterCIDRs) > 1 && (!utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) || !utilfeature.DefaultFeatureGate.Enabled(features.EndpointSlice)) {
return nil, false, fmt.Errorf("len of ClusterCIDRs==%v and dualstack or EndpointSlice feature is not enabled", len(clusterCIDRs))
}

// failure: more than one cidr but they are not configured as dual stack
if len(clusterCIDRs) > 1 && !dualStack {
return nil, false, fmt.Errorf("len of ClusterCIDRs==%v and they are not configured as dual stack (at least one from each IPFamily", len(clusterCIDRs))
return nil, false, fmt.Errorf("len of ClusterCIDRs==%v and they are not configured as dual stack (at least one from each IPFamily)", len(clusterCIDRs))
}

// failure: more than cidrs is not allowed even with dual stack
Expand Down
5 changes: 4 additions & 1 deletion pkg/apis/core/helper/helpers.go
Expand Up @@ -267,7 +267,10 @@ func IsIntegerResourceName(str string) bool {
// IsServiceIPSet aims to check if the service's ClusterIP is set or not
// the objective is not to perform validation here
func IsServiceIPSet(service *core.Service) bool {
return service.Spec.ClusterIP != core.ClusterIPNone && service.Spec.ClusterIP != ""
// This function assumes that the service is semantically validated
khenidak marked this conversation as resolved.
Show resolved Hide resolved
// it does not test if the IP is valid, just makes sure that it is set.
return len(service.Spec.ClusterIP) > 0 &&
service.Spec.ClusterIP != core.ClusterIPNone
}

var standardFinalizers = sets.NewString(
Expand Down
70 changes: 70 additions & 0 deletions pkg/apis/core/helper/helpers_test.go
Expand Up @@ -294,3 +294,73 @@ func TestIsOvercommitAllowed(t *testing.T) {
}
}
}

func TestIsServiceIPSet(t *testing.T) {
testCases := []struct {
input core.ServiceSpec
output bool
name string
}{
{
name: "nil cluster ip",
input: core.ServiceSpec{
ClusterIPs: nil,
},

output: false,
},
{
name: "headless service",
input: core.ServiceSpec{
ClusterIP: "None",
ClusterIPs: []string{"None"},
},
output: false,
},
// true cases
{
name: "one ipv4",
input: core.ServiceSpec{
ClusterIP: "1.2.3.4",
ClusterIPs: []string{"1.2.3.4"},
},
output: true,
},
{
name: "one ipv6",
input: core.ServiceSpec{
ClusterIP: "2001::1",
ClusterIPs: []string{"2001::1"},
},
output: true,
},
{
name: "v4, v6",
input: core.ServiceSpec{
ClusterIP: "1.2.3.4",
ClusterIPs: []string{"1.2.3.4", "2001::1"},
khenidak marked this conversation as resolved.
Show resolved Hide resolved
},
output: true,
},
{
name: "v6, v4",
input: core.ServiceSpec{
ClusterIP: "2001::1",
ClusterIPs: []string{"2001::1", "1.2.3.4"},
},

output: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := core.Service{
Spec: tc.input,
}
if IsServiceIPSet(&s) != tc.output {
t.Errorf("case, input: %v, expected: %v, got: %v", tc.input, tc.output, !tc.output)
}
})
}
}