mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #110502 from aojea/clean_svc_strategies
services strategy no longer depends on IPFamilies
This commit is contained in:
commit
f5db989b26
@ -41,7 +41,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/service"
|
|
||||||
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"
|
||||||
@ -86,18 +85,16 @@ func NewREST(
|
|||||||
pods PodStorage,
|
pods PodStorage,
|
||||||
proxyTransport http.RoundTripper) (*REST, *StatusREST, *svcreg.ProxyREST, error) {
|
proxyTransport http.RoundTripper) (*REST, *StatusREST, *svcreg.ProxyREST, error) {
|
||||||
|
|
||||||
strategy, _ := svcreg.StrategyForServiceCIDRs(ipAllocs[serviceIPFamily].CIDR(), len(ipAllocs) > 1)
|
|
||||||
|
|
||||||
store := &genericregistry.Store{
|
store := &genericregistry.Store{
|
||||||
NewFunc: func() runtime.Object { return &api.Service{} },
|
NewFunc: func() runtime.Object { return &api.Service{} },
|
||||||
NewListFunc: func() runtime.Object { return &api.ServiceList{} },
|
NewListFunc: func() runtime.Object { return &api.ServiceList{} },
|
||||||
DefaultQualifiedResource: api.Resource("services"),
|
DefaultQualifiedResource: api.Resource("services"),
|
||||||
ReturnDeletedObject: true,
|
ReturnDeletedObject: true,
|
||||||
|
|
||||||
CreateStrategy: strategy,
|
CreateStrategy: svcreg.Strategy,
|
||||||
UpdateStrategy: strategy,
|
UpdateStrategy: svcreg.Strategy,
|
||||||
DeleteStrategy: strategy,
|
DeleteStrategy: svcreg.Strategy,
|
||||||
ResetFieldsStrategy: strategy,
|
ResetFieldsStrategy: svcreg.Strategy,
|
||||||
|
|
||||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||||
}
|
}
|
||||||
@ -107,9 +104,8 @@ func NewREST(
|
|||||||
}
|
}
|
||||||
|
|
||||||
statusStore := *store
|
statusStore := *store
|
||||||
statusStrategy := service.NewServiceStatusStrategy(strategy)
|
statusStore.UpdateStrategy = svcreg.StatusStrategy
|
||||||
statusStore.UpdateStrategy = statusStrategy
|
statusStore.ResetFieldsStrategy = svcreg.StatusStrategy
|
||||||
statusStore.ResetFieldsStrategy = statusStrategy
|
|
||||||
|
|
||||||
var primaryIPFamily api.IPFamily = serviceIPFamily
|
var primaryIPFamily api.IPFamily = serviceIPFamily
|
||||||
var secondaryIPFamily api.IPFamily = "" // sentinel value
|
var secondaryIPFamily api.IPFamily = "" // sentinel value
|
||||||
|
@ -18,74 +18,29 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
|
||||||
"k8s.io/apiserver/pkg/storage/names"
|
"k8s.io/apiserver/pkg/storage/names"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
netutil "k8s.io/utils/net"
|
|
||||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Strategy interface {
|
|
||||||
rest.RESTCreateUpdateStrategy
|
|
||||||
rest.ResetFieldsStrategy
|
|
||||||
}
|
|
||||||
|
|
||||||
// svcStrategy implements behavior for Services
|
// svcStrategy implements behavior for Services
|
||||||
type svcStrategy struct {
|
type svcStrategy struct {
|
||||||
runtime.ObjectTyper
|
runtime.ObjectTyper
|
||||||
names.NameGenerator
|
names.NameGenerator
|
||||||
|
|
||||||
ipFamilies []api.IPFamily
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StrategyForServiceCIDRs returns the appropriate service strategy for the given configuration.
|
// Strategy is the default logic that applies when creating and updating Services
|
||||||
func StrategyForServiceCIDRs(primaryCIDR net.IPNet, hasSecondary bool) (Strategy, api.IPFamily) {
|
// objects via the REST API.
|
||||||
// detect this cluster default Service IPFamily (ipfamily of --service-cluster-ip-range)
|
var Strategy = svcStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
// we do it once here, to avoid having to do it over and over during ipfamily assignment
|
|
||||||
serviceIPFamily := api.IPv4Protocol
|
|
||||||
if netutil.IsIPv6CIDR(&primaryCIDR) {
|
|
||||||
serviceIPFamily = api.IPv6Protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
var strategy Strategy
|
|
||||||
switch {
|
|
||||||
case hasSecondary && serviceIPFamily == api.IPv4Protocol:
|
|
||||||
strategy = svcStrategy{
|
|
||||||
ObjectTyper: legacyscheme.Scheme,
|
|
||||||
NameGenerator: names.SimpleNameGenerator,
|
|
||||||
ipFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol},
|
|
||||||
}
|
|
||||||
case hasSecondary && serviceIPFamily == api.IPv6Protocol:
|
|
||||||
strategy = svcStrategy{
|
|
||||||
ObjectTyper: legacyscheme.Scheme,
|
|
||||||
NameGenerator: names.SimpleNameGenerator,
|
|
||||||
ipFamilies: []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol},
|
|
||||||
}
|
|
||||||
case serviceIPFamily == api.IPv6Protocol:
|
|
||||||
strategy = svcStrategy{
|
|
||||||
ObjectTyper: legacyscheme.Scheme,
|
|
||||||
NameGenerator: names.SimpleNameGenerator,
|
|
||||||
ipFamilies: []api.IPFamily{api.IPv6Protocol},
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
strategy = svcStrategy{
|
|
||||||
ObjectTyper: legacyscheme.Scheme,
|
|
||||||
NameGenerator: names.SimpleNameGenerator,
|
|
||||||
ipFamilies: []api.IPFamily{api.IPv4Protocol},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strategy, serviceIPFamily
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamespaceScoped is true for services.
|
// NamespaceScoped is true for services.
|
||||||
func (svcStrategy) NamespaceScoped() bool {
|
func (svcStrategy) NamespaceScoped() bool {
|
||||||
@ -105,7 +60,7 @@ func (svcStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrepareForCreate sets contextual defaults and clears fields that are not allowed to be set by end users on creation.
|
// PrepareForCreate sets contextual defaults and clears fields that are not allowed to be set by end users on creation.
|
||||||
func (strategy svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
func (svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||||
service := obj.(*api.Service)
|
service := obj.(*api.Service)
|
||||||
service.Status = api.ServiceStatus{}
|
service.Status = api.ServiceStatus{}
|
||||||
|
|
||||||
@ -113,7 +68,7 @@ func (strategy svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Ob
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrepareForUpdate sets contextual defaults and clears fields that are not allowed to be set by end users on update.
|
// PrepareForUpdate sets contextual defaults and clears fields that are not allowed to be set by end users on update.
|
||||||
func (strategy svcStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
func (svcStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||||
newService := obj.(*api.Service)
|
newService := obj.(*api.Service)
|
||||||
oldService := old.(*api.Service)
|
oldService := old.(*api.Service)
|
||||||
newService.Status = oldService.Status
|
newService.Status = oldService.Status
|
||||||
@ -123,7 +78,7 @@ func (strategy svcStrategy) PrepareForUpdate(ctx context.Context, obj, old runti
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a new service.
|
// Validate validates a new service.
|
||||||
func (strategy svcStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (svcStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
service := obj.(*api.Service)
|
service := obj.(*api.Service)
|
||||||
allErrs := validation.ValidateServiceCreate(service)
|
allErrs := validation.ValidateServiceCreate(service)
|
||||||
allErrs = append(allErrs, validation.ValidateConditionalService(service, nil)...)
|
allErrs = append(allErrs, validation.ValidateConditionalService(service, nil)...)
|
||||||
@ -211,13 +166,11 @@ func serviceInternalTrafficPolicyInUse(svc *api.Service) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type serviceStatusStrategy struct {
|
type serviceStatusStrategy struct {
|
||||||
Strategy
|
svcStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceStatusStrategy creates a status strategy for the provided base strategy.
|
// StatusStrategy wraps and exports the used svcStrategy for the storage package.
|
||||||
func NewServiceStatusStrategy(strategy Strategy) Strategy {
|
var StatusStrategy = serviceStatusStrategy{Strategy}
|
||||||
return serviceStatusStrategy{strategy}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResetFields returns the set of fields that get reset by the strategy
|
// GetResetFields returns the set of fields that get reset by the strategy
|
||||||
// and should not be modified by the user.
|
// and should not be modified by the user.
|
||||||
|
@ -31,38 +31,26 @@ import (
|
|||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
netutils "k8s.io/utils/net"
|
|
||||||
utilpointer "k8s.io/utils/pointer"
|
utilpointer "k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStrategy(cidr string, hasSecondary bool) (testStrategy Strategy, testStatusStrategy Strategy) {
|
|
||||||
_, testCIDR, err := netutils.ParseCIDRSloppy(cidr)
|
|
||||||
if err != nil {
|
|
||||||
panic("invalid CIDR")
|
|
||||||
}
|
|
||||||
testStrategy, _ = StrategyForServiceCIDRs(*testCIDR, hasSecondary)
|
|
||||||
testStatusStrategy = NewServiceStatusStrategy(testStrategy)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckGeneratedNameError(t *testing.T) {
|
func TestCheckGeneratedNameError(t *testing.T) {
|
||||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
|
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
|
||||||
Resource: "foos",
|
Resource: "foos",
|
||||||
})
|
})
|
||||||
|
|
||||||
testStrategy, _ := newStrategy("10.0.0.0/16", false)
|
|
||||||
expect := errors.NewNotFound(api.Resource("foos"), "bar")
|
expect := errors.NewNotFound(api.Resource("foos"), "bar")
|
||||||
if err := rest.CheckGeneratedNameError(ctx, testStrategy, expect, &api.Service{}); err != expect {
|
if err := rest.CheckGeneratedNameError(ctx, Strategy, expect, &api.Service{}); err != expect {
|
||||||
t.Errorf("NotFoundError should be ignored: %v", err)
|
t.Errorf("NotFoundError should be ignored: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expect = errors.NewAlreadyExists(api.Resource("foos"), "bar")
|
expect = errors.NewAlreadyExists(api.Resource("foos"), "bar")
|
||||||
if err := rest.CheckGeneratedNameError(ctx, testStrategy, expect, &api.Service{}); err != expect {
|
if err := rest.CheckGeneratedNameError(ctx, Strategy, expect, &api.Service{}); err != expect {
|
||||||
t.Errorf("AlreadyExists should be returned when no GenerateName field: %v", err)
|
t.Errorf("AlreadyExists should be returned when no GenerateName field: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expect = errors.NewAlreadyExists(api.Resource("foos"), "bar")
|
expect = errors.NewAlreadyExists(api.Resource("foos"), "bar")
|
||||||
if err := rest.CheckGeneratedNameError(ctx, testStrategy, expect, &api.Service{ObjectMeta: metav1.ObjectMeta{GenerateName: "foo"}}); err == nil || !errors.IsAlreadyExists(err) {
|
if err := rest.CheckGeneratedNameError(ctx, Strategy, expect, &api.Service{ObjectMeta: metav1.ObjectMeta{GenerateName: "foo"}}); err == nil || !errors.IsAlreadyExists(err) {
|
||||||
t.Errorf("expected try again later error: %v", err)
|
t.Errorf("expected try again later error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,9 +102,8 @@ func makeValidServiceCustom(tweaks ...func(svc *api.Service)) *api.Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceStatusStrategy(t *testing.T) {
|
func TestServiceStatusStrategy(t *testing.T) {
|
||||||
_, testStatusStrategy := newStrategy("10.0.0.0/16", false)
|
|
||||||
ctx := genericapirequest.NewDefaultContext()
|
ctx := genericapirequest.NewDefaultContext()
|
||||||
if !testStatusStrategy.NamespaceScoped() {
|
if !StatusStrategy.NamespaceScoped() {
|
||||||
t.Errorf("Service must be namespace scoped")
|
t.Errorf("Service must be namespace scoped")
|
||||||
}
|
}
|
||||||
oldService := makeValidService()
|
oldService := makeValidService()
|
||||||
@ -131,14 +118,14 @@ func TestServiceStatusStrategy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testStatusStrategy.PrepareForUpdate(ctx, newService, oldService)
|
StatusStrategy.PrepareForUpdate(ctx, newService, oldService)
|
||||||
if newService.Status.LoadBalancer.Ingress[0].IP != "127.0.0.2" {
|
if newService.Status.LoadBalancer.Ingress[0].IP != "127.0.0.2" {
|
||||||
t.Errorf("Service status updates should allow change of status fields")
|
t.Errorf("Service status updates should allow change of status fields")
|
||||||
}
|
}
|
||||||
if newService.Spec.SessionAffinity != "None" {
|
if newService.Spec.SessionAffinity != "None" {
|
||||||
t.Errorf("PrepareForUpdate should have preserved old spec")
|
t.Errorf("PrepareForUpdate should have preserved old spec")
|
||||||
}
|
}
|
||||||
errs := testStatusStrategy.ValidateUpdate(ctx, newService, oldService)
|
errs := StatusStrategy.ValidateUpdate(ctx, newService, oldService)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("Unexpected error %v", errs)
|
t.Errorf("Unexpected error %v", errs)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user