mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 23:47:50 +00:00
Service Topology implementation
* Implement Service Topology for ipvs and iptables proxier * Add test files * API validation
This commit is contained in:
@@ -4053,6 +4053,35 @@ func ValidateService(service *core.Service) field.ErrorList {
|
||||
ports[key] = true
|
||||
}
|
||||
|
||||
// Validate TopologyKeys
|
||||
if len(service.Spec.TopologyKeys) > 0 {
|
||||
topoPath := specPath.Child("topologyKeys")
|
||||
// topologyKeys is mutually exclusive with 'externalTrafficPolicy=Local'
|
||||
if service.Spec.ExternalTrafficPolicy == core.ServiceExternalTrafficPolicyTypeLocal {
|
||||
allErrs = append(allErrs, field.Forbidden(topoPath, "may not be specified when `externalTrafficPolicy=Local`"))
|
||||
}
|
||||
if len(service.Spec.TopologyKeys) > core.MaxServiceTopologyKeys {
|
||||
allErrs = append(allErrs, field.TooMany(topoPath, len(service.Spec.TopologyKeys), core.MaxServiceTopologyKeys))
|
||||
}
|
||||
topoKeys := sets.NewString()
|
||||
for i, key := range service.Spec.TopologyKeys {
|
||||
keyPath := topoPath.Index(i)
|
||||
if topoKeys.Has(key) {
|
||||
allErrs = append(allErrs, field.Duplicate(keyPath, key))
|
||||
}
|
||||
topoKeys.Insert(key)
|
||||
// "Any" must be the last value specified
|
||||
if key == v1.TopologyKeyAny && i != len(service.Spec.TopologyKeys)-1 {
|
||||
allErrs = append(allErrs, field.Invalid(keyPath, key, `"*" must be the last value specified`))
|
||||
}
|
||||
if key != v1.TopologyKeyAny {
|
||||
for _, msg := range validation.IsQualifiedName(key) {
|
||||
allErrs = append(allErrs, field.Invalid(keyPath, service.Spec.TopologyKeys, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate SourceRange field and annotation
|
||||
_, ok := service.Annotations[core.AnnotationLoadBalancerSourceRangesKey]
|
||||
if len(service.Spec.LoadBalancerSourceRanges) > 0 || ok {
|
||||
@@ -4143,6 +4172,10 @@ func validateServiceExternalTrafficFieldsValue(service *core.Service) field.Erro
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("externalTrafficPolicy"), service.Spec.ExternalTrafficPolicy,
|
||||
fmt.Sprintf("ExternalTrafficPolicy must be empty, %v or %v", core.ServiceExternalTrafficPolicyTypeCluster, core.ServiceExternalTrafficPolicyTypeLocal)))
|
||||
}
|
||||
// 'externalTrafficPolicy=Local' is mutually exclusive with topologyKeys
|
||||
if service.Spec.ExternalTrafficPolicy == core.ServiceExternalTrafficPolicyTypeLocal && len(service.Spec.TopologyKeys) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("externalTrafficPolicy"), "externalTrafficPolicy must not be set to 'Local' when topologyKeys is specified"))
|
||||
}
|
||||
if service.Spec.HealthCheckNodePort < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort,
|
||||
"HealthCheckNodePort must be not less than 0"))
|
||||
|
||||
@@ -18,6 +18,7 @@ package validation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -9380,6 +9381,7 @@ func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
|
||||
|
||||
func TestValidateService(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceTopology, true)()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -10058,6 +10060,66 @@ func TestValidateService(t *testing.T) {
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "valid topology keys",
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.TopologyKeys = []string{
|
||||
"kubernetes.io/hostname",
|
||||
"failure-domain.beta.kubernetes.io/zone",
|
||||
"failure-domain.beta.kubernetes.io/region",
|
||||
v1.TopologyKeyAny,
|
||||
}
|
||||
},
|
||||
numErrs: 0,
|
||||
},
|
||||
{
|
||||
name: "invalid topology key",
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.TopologyKeys = []string{"NoUppercaseOrSpecialCharsLike=Equals"}
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "too many topology keys",
|
||||
tweakSvc: func(s *core.Service) {
|
||||
for i := 0; i < core.MaxServiceTopologyKeys+1; i++ {
|
||||
s.Spec.TopologyKeys = append(s.Spec.TopologyKeys, fmt.Sprintf("topologykey-%d", i))
|
||||
}
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: `"Any" was not the last key`,
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.TopologyKeys = []string{
|
||||
"kubernetes.io/hostname",
|
||||
v1.TopologyKeyAny,
|
||||
"failure-domain.beta.kubernetes.io/zone",
|
||||
}
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: `duplicate topology key`,
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.TopologyKeys = []string{
|
||||
"kubernetes.io/hostname",
|
||||
"kubernetes.io/hostname",
|
||||
"failure-domain.beta.kubernetes.io/zone",
|
||||
}
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: `use topology keys with externalTrafficPolicy=Local`,
|
||||
tweakSvc: func(s *core.Service) {
|
||||
s.Spec.ExternalTrafficPolicy = "Local"
|
||||
s.Spec.TopologyKeys = []string{
|
||||
"kubernetes.io/hostname",
|
||||
}
|
||||
},
|
||||
numErrs: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
Reference in New Issue
Block a user