mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 20:53:33 +00:00
Svc REST: clean up defaultOnRead to be consistent
Headless+selectorless -> RequireDualStack Headless+selector -> SingleStack Add test cases to cover this and ExternalName and dual-stack init (which I think can never trigger, but best to be safe).
This commit is contained in:
parent
6a49ed41ea
commit
52f54ce90d
@ -117,7 +117,7 @@ func MakeServicePort(name string, port int, tgtPort intstr.IntOrString, proto ap
|
|||||||
// SetHeadless sets the service as headless and clears other fields.
|
// SetHeadless sets the service as headless and clears other fields.
|
||||||
func SetHeadless(svc *api.Service) {
|
func SetHeadless(svc *api.Service) {
|
||||||
SetTypeClusterIP(svc)
|
SetTypeClusterIP(svc)
|
||||||
svc.Spec.ClusterIP = api.ClusterIPNone
|
SetClusterIPs(api.ClusterIPNone)(svc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSelector sets the service selector.
|
// SetSelector sets the service selector.
|
||||||
|
@ -47,9 +47,8 @@ import (
|
|||||||
svcreg "k8s.io/kubernetes/pkg/registry/core/service"
|
svcreg "k8s.io/kubernetes/pkg/registry/core/service"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/service/portallocator"
|
"k8s.io/kubernetes/pkg/registry/core/service/portallocator"
|
||||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
|
||||||
|
|
||||||
netutil "k8s.io/utils/net"
|
netutil "k8s.io/utils/net"
|
||||||
|
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EndpointsStorage interface {
|
type EndpointsStorage interface {
|
||||||
@ -248,65 +247,59 @@ func (r *REST) defaultOnReadService(service *api.Service) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set ipFamilies and ipFamilyPolicy if needed.
|
||||||
|
r.defaultOnReadIPFamilies(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *REST) defaultOnReadIPFamilies(service *api.Service) {
|
||||||
|
// ExternalName does not need this.
|
||||||
|
if !needsClusterIP(service) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If IPFamilies is set, we assume IPFamilyPolicy is also set (it should
|
||||||
|
// not be possible to have one and not the other), and therefore we don't
|
||||||
|
// need further defaulting. Likewise, if IPFamilies is *not* set, we
|
||||||
|
// assume IPFamilyPolicy can't be set either.
|
||||||
if len(service.Spec.IPFamilies) > 0 {
|
if len(service.Spec.IPFamilies) > 0 {
|
||||||
return // already defaulted
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// set clusterIPs based on ClusterIP
|
|
||||||
if len(service.Spec.ClusterIPs) == 0 {
|
|
||||||
if len(service.Spec.ClusterIP) > 0 {
|
|
||||||
service.Spec.ClusterIPs = []string{service.Spec.ClusterIP}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requireDualStack := api.IPFamilyPolicyRequireDualStack
|
|
||||||
singleStack := api.IPFamilyPolicySingleStack
|
singleStack := api.IPFamilyPolicySingleStack
|
||||||
preferDualStack := api.IPFamilyPolicyPreferDualStack
|
requireDualStack := api.IPFamilyPolicyRequireDualStack
|
||||||
// headless services
|
|
||||||
if len(service.Spec.ClusterIPs) == 1 && service.Spec.ClusterIPs[0] == api.ClusterIPNone {
|
|
||||||
service.Spec.IPFamilies = []api.IPFamily{r.primaryIPFamily}
|
|
||||||
|
|
||||||
// headless+selectorless
|
if service.Spec.ClusterIP == api.ClusterIPNone {
|
||||||
// headless+selectorless takes both families. Why?
|
// Headless.
|
||||||
// at this stage we don't know what kind of endpoints (specifically their IPFamilies) the
|
|
||||||
// user has assigned to this selectorless service. We assume it has dualstack and we default
|
|
||||||
// it to PreferDualStack on any cluster (single or dualstack configured).
|
|
||||||
if len(service.Spec.Selector) == 0 {
|
if len(service.Spec.Selector) == 0 {
|
||||||
service.Spec.IPFamilyPolicy = &preferDualStack
|
// Headless + selectorless is a special-case.
|
||||||
if r.primaryIPFamily == api.IPv4Protocol {
|
//
|
||||||
service.Spec.IPFamilies = append(service.Spec.IPFamilies, api.IPv6Protocol)
|
// At this stage we don't know what kind of endpoints (specifically
|
||||||
} else {
|
// their IPFamilies) the user has assigned to this selectorless
|
||||||
service.Spec.IPFamilies = append(service.Spec.IPFamilies, api.IPv4Protocol)
|
// service. We assume it has dual-stack and we default it to
|
||||||
}
|
// RequireDualStack on any cluster (single- or dual-stack
|
||||||
|
// configured).
|
||||||
|
service.Spec.IPFamilyPolicy = &requireDualStack
|
||||||
|
service.Spec.IPFamilies = []api.IPFamily{r.primaryIPFamily, otherFamily(r.primaryIPFamily)}
|
||||||
} else {
|
} else {
|
||||||
// headless w/ selector
|
// Headless + selector - default to single.
|
||||||
// this service type follows cluster configuration. this service (selector based) uses a
|
service.Spec.IPFamilyPolicy = &singleStack
|
||||||
// selector and will have to follow how the cluster is configured. If the cluster is
|
service.Spec.IPFamilies = []api.IPFamily{r.primaryIPFamily}
|
||||||
// configured to dual stack then the service defaults to PreferDualStack. Otherwise we
|
|
||||||
// default it to SingleStack.
|
|
||||||
if r.secondaryIPFamily != "" {
|
|
||||||
service.Spec.IPFamilies = append(service.Spec.IPFamilies, r.secondaryIPFamily)
|
|
||||||
service.Spec.IPFamilyPolicy = &preferDualStack
|
|
||||||
} else {
|
|
||||||
service.Spec.IPFamilyPolicy = &singleStack
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// headful
|
// Headful: init ipFamilies from clusterIPs.
|
||||||
// make sure a slice exists to receive the families
|
service.Spec.IPFamilies = make([]api.IPFamily, len(service.Spec.ClusterIPs))
|
||||||
service.Spec.IPFamilies = make([]api.IPFamily, len(service.Spec.ClusterIPs), len(service.Spec.ClusterIPs))
|
|
||||||
for idx, ip := range service.Spec.ClusterIPs {
|
for idx, ip := range service.Spec.ClusterIPs {
|
||||||
if netutil.IsIPv6String(ip) {
|
if netutil.IsIPv6String(ip) {
|
||||||
service.Spec.IPFamilies[idx] = api.IPv6Protocol
|
service.Spec.IPFamilies[idx] = api.IPv6Protocol
|
||||||
} else {
|
} else {
|
||||||
service.Spec.IPFamilies[idx] = api.IPv4Protocol
|
service.Spec.IPFamilies[idx] = api.IPv4Protocol
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if len(service.Spec.IPFamilies) == 1 {
|
if len(service.Spec.IPFamilies) == 1 {
|
||||||
service.Spec.IPFamilyPolicy = &singleStack
|
service.Spec.IPFamilyPolicy = &singleStack
|
||||||
} else if len(service.Spec.IPFamilies) == 2 {
|
} else if len(service.Spec.IPFamilies) == 2 {
|
||||||
service.Spec.IPFamilyPolicy = &requireDualStack
|
// It shouldn't be possible to get here, but just in case.
|
||||||
}
|
service.Spec.IPFamilyPolicy = &requireDualStack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,29 +561,79 @@ func TestServiceDefaultOnRead(t *testing.T) {
|
|||||||
input runtime.Object
|
input runtime.Object
|
||||||
expect runtime.Object
|
expect runtime.Object
|
||||||
}{{
|
}{{
|
||||||
name: "no change v4",
|
name: "single v4",
|
||||||
input: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1")),
|
input: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1")),
|
||||||
expect: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1")),
|
expect: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||||
}, {
|
}, {
|
||||||
name: "missing clusterIPs v4",
|
name: "single v6",
|
||||||
input: svctest.MakeService("foo", svctest.SetClusterIP("10.0.0.1")),
|
input: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1")),
|
||||||
expect: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1")),
|
expect: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv6Protocol)),
|
||||||
}, {
|
}, {
|
||||||
name: "no change v6",
|
name: "missing clusterIPs v4",
|
||||||
input: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1")),
|
input: svctest.MakeService("foo", svctest.SetClusterIP("10.0.0.1")),
|
||||||
expect: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1")),
|
expect: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||||
}, {
|
}, {
|
||||||
name: "missing clusterIPs v6",
|
name: "missing clusterIPs v6",
|
||||||
input: svctest.MakeService("foo", svctest.SetClusterIP("2000::1")),
|
input: svctest.MakeService("foo", svctest.SetClusterIP("2000::1")),
|
||||||
expect: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1")),
|
expect: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv6Protocol)),
|
||||||
}, {
|
}, {
|
||||||
name: "list, no change v4",
|
name: "list v4",
|
||||||
input: makeServiceList(svctest.SetClusterIPs("10.0.0.1")),
|
input: makeServiceList(svctest.SetClusterIPs("10.0.0.1")),
|
||||||
expect: makeServiceList(svctest.SetClusterIPs("10.0.0.1")),
|
expect: makeServiceList(svctest.SetClusterIPs("10.0.0.1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||||
}, {
|
}, {
|
||||||
name: "list, missing clusterIPs v4",
|
name: "list missing clusterIPs v4",
|
||||||
input: makeServiceList(svctest.SetClusterIP("10.0.0.1")),
|
input: makeServiceList(svctest.SetClusterIP("10.0.0.1")),
|
||||||
expect: makeServiceList(svctest.SetClusterIPs("10.0.0.1")),
|
expect: makeServiceList(svctest.SetClusterIPs("10.0.0.1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||||
|
}, {
|
||||||
|
name: "external name",
|
||||||
|
input: makeServiceList(svctest.SetTypeExternalName),
|
||||||
|
expect: makeServiceList(svctest.SetTypeExternalName),
|
||||||
|
}, {
|
||||||
|
name: "dual v4v6",
|
||||||
|
input: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1", "2000::1")),
|
||||||
|
expect: svctest.MakeService("foo", svctest.SetClusterIPs("10.0.0.1", "2000::1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)),
|
||||||
|
}, {
|
||||||
|
name: "dual v6v4",
|
||||||
|
input: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1", "10.0.0.1")),
|
||||||
|
expect: svctest.MakeService("foo", svctest.SetClusterIPs("2000::1", "10.0.0.1"),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)),
|
||||||
|
}, {
|
||||||
|
name: "headless",
|
||||||
|
input: svctest.MakeService("foo", svctest.SetHeadless),
|
||||||
|
expect: svctest.MakeService("foo", svctest.SetHeadless,
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol)),
|
||||||
|
}, {
|
||||||
|
name: "headless selectorless",
|
||||||
|
input: svctest.MakeService("foo", svctest.SetHeadless,
|
||||||
|
svctest.SetSelector(map[string]string{})),
|
||||||
|
expect: svctest.MakeService("foo", svctest.SetHeadless,
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)),
|
||||||
|
}, {
|
||||||
|
name: "headless selectorless pre-set",
|
||||||
|
input: svctest.MakeService("foo", svctest.SetHeadless,
|
||||||
|
svctest.SetSelector(map[string]string{}),
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv6Protocol)),
|
||||||
|
expect: svctest.MakeService("foo", svctest.SetHeadless,
|
||||||
|
svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack),
|
||||||
|
svctest.SetIPFamilies(api.IPv6Protocol)),
|
||||||
}, {
|
}, {
|
||||||
name: "not Service or ServiceList",
|
name: "not Service or ServiceList",
|
||||||
input: &api.Pod{},
|
input: &api.Pod{},
|
||||||
@ -593,7 +643,7 @@ func TestServiceDefaultOnRead(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol})
|
storage, _, server := newStorage(t, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol})
|
||||||
defer server.Terminate(t)
|
defer server.Terminate(t)
|
||||||
defer storage.Store.DestroyFunc()
|
defer storage.Store.DestroyFunc()
|
||||||
|
|
||||||
@ -619,11 +669,17 @@ func TestServiceDefaultOnRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify fields we know are affected
|
// Verify fields we know are affected
|
||||||
if svc.Spec.ClusterIP != exp.Spec.ClusterIP {
|
if want, got := exp.Spec.ClusterIP, svc.Spec.ClusterIP; want != got {
|
||||||
t.Errorf("clusterIP: expected %v, got %v", exp.Spec.ClusterIP, svc.Spec.ClusterIP)
|
t.Errorf("clusterIP: expected %v, got %v", want, got)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(svc.Spec.ClusterIPs, exp.Spec.ClusterIPs) {
|
if want, got := exp.Spec.ClusterIPs, svc.Spec.ClusterIPs; !reflect.DeepEqual(want, got) {
|
||||||
t.Errorf("clusterIPs: expected %v, got %v", exp.Spec.ClusterIPs, svc.Spec.ClusterIPs)
|
t.Errorf("clusterIPs: expected %v, got %v", want, got)
|
||||||
|
}
|
||||||
|
if want, got := fmtIPFamilyPolicy(exp.Spec.IPFamilyPolicy), fmtIPFamilyPolicy(svc.Spec.IPFamilyPolicy); want != got {
|
||||||
|
t.Errorf("ipFamilyPolicy: expected %v, got %v", want, got)
|
||||||
|
}
|
||||||
|
if want, got := exp.Spec.IPFamilies, svc.Spec.IPFamilies; !reflect.DeepEqual(want, got) {
|
||||||
|
t.Errorf("ipFamilies: expected %v, got %v", want, got)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user