mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #62659 from enj/enj/i/gc_admission_dynamic_restmapper
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Use a dynamic RESTMapper for admission plugins **What this PR does / why we need it**: This change updates the REST mapper used by all admission plugins to be backed by cached discovery information. This cache is updated every ten seconds via a post start hook and will not attempt to update on calls to `RESTMapping`. It solely relies on the hook to keep the cache in sync with discovery. This prevents issues with the `OwnerReferencesPermissionEnforcement` admission plugin when it is used with custom resources that set `blockOwnerDeletion`. **Which issue(s) this PR fixes**: `Fixes #...` **Special notes for your reviewer**: There are probably other ways the post start hook could be wired. **Release note**: ```release-note NONE ``` Signed-off-by: Monis Khan <mkhan@redhat.com> @kubernetes/sig-api-machinery-misc
This commit is contained in:
commit
b37564d9f2
@ -59,6 +59,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver:go_default_library",
|
"//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver:go_default_library",
|
||||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion:go_default_library",
|
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion:go_default_library",
|
||||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/cmd/server:go_default_library",
|
"//vendor/k8s.io/apiextensions-apiserver/pkg/cmd/server:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||||
@ -78,6 +79,8 @@ go_library(
|
|||||||
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/storage/etcd3/preflight:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/storage/etcd3/preflight:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/discovery:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/discovery/cached:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
@ -54,6 +55,8 @@ import (
|
|||||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3/preflight"
|
"k8s.io/apiserver/pkg/storage/etcd3/preflight"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/client-go/discovery"
|
||||||
|
cacheddiscovery "k8s.io/client-go/discovery/cached"
|
||||||
clientgoinformers "k8s.io/client-go/informers"
|
clientgoinformers "k8s.io/client-go/informers"
|
||||||
clientgoclientset "k8s.io/client-go/kubernetes"
|
clientgoclientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
@ -156,7 +159,7 @@ func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeAPIServerConfig, sharedInformers, versionedInformers, insecureServingOptions, serviceResolver, pluginInitializer, err := CreateKubeAPIServerConfig(completedOptions, nodeTunneler, proxyTransport)
|
kubeAPIServerConfig, sharedInformers, versionedInformers, insecureServingOptions, serviceResolver, pluginInitializer, admissionPostStartHook, err := CreateKubeAPIServerConfig(completedOptions, nodeTunneler, proxyTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -171,7 +174,7 @@ func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer, sharedInformers, versionedInformers)
|
kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer, sharedInformers, versionedInformers, admissionPostStartHook)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -218,15 +221,17 @@ func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateKubeAPIServer creates and wires a workable kube-apiserver
|
// CreateKubeAPIServer creates and wires a workable kube-apiserver
|
||||||
func CreateKubeAPIServer(kubeAPIServerConfig *master.Config, delegateAPIServer genericapiserver.DelegationTarget, sharedInformers informers.SharedInformerFactory, versionedInformers clientgoinformers.SharedInformerFactory) (*master.Master, error) {
|
func CreateKubeAPIServer(kubeAPIServerConfig *master.Config, delegateAPIServer genericapiserver.DelegationTarget, sharedInformers informers.SharedInformerFactory, versionedInformers clientgoinformers.SharedInformerFactory, admissionPostStartHook genericapiserver.PostStartHookFunc) (*master.Master, error) {
|
||||||
kubeAPIServer, err := kubeAPIServerConfig.Complete(versionedInformers).New(delegateAPIServer)
|
kubeAPIServer, err := kubeAPIServerConfig.Complete(versionedInformers).New(delegateAPIServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
kubeAPIServer.GenericAPIServer.AddPostStartHook("start-kube-apiserver-informers", func(context genericapiserver.PostStartHookContext) error {
|
|
||||||
|
kubeAPIServer.GenericAPIServer.AddPostStartHookOrDie("start-kube-apiserver-informers", func(context genericapiserver.PostStartHookContext) error {
|
||||||
sharedInformers.Start(context.StopCh)
|
sharedInformers.Start(context.StopCh)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
kubeAPIServer.GenericAPIServer.AddPostStartHookOrDie("start-kube-apiserver-admission-initializer", admissionPostStartHook)
|
||||||
|
|
||||||
return kubeAPIServer, nil
|
return kubeAPIServer, nil
|
||||||
}
|
}
|
||||||
@ -288,10 +293,11 @@ func CreateKubeAPIServerConfig(
|
|||||||
insecureServingInfo *kubeserver.InsecureServingInfo,
|
insecureServingInfo *kubeserver.InsecureServingInfo,
|
||||||
serviceResolver aggregatorapiserver.ServiceResolver,
|
serviceResolver aggregatorapiserver.ServiceResolver,
|
||||||
pluginInitializers []admission.PluginInitializer,
|
pluginInitializers []admission.PluginInitializer,
|
||||||
|
admissionPostStartHook genericapiserver.PostStartHookFunc,
|
||||||
lastErr error,
|
lastErr error,
|
||||||
) {
|
) {
|
||||||
var genericConfig *genericapiserver.Config
|
var genericConfig *genericapiserver.Config
|
||||||
genericConfig, sharedInformers, versionedInformers, insecureServingInfo, serviceResolver, pluginInitializers, lastErr = BuildGenericConfig(s.ServerRunOptions, proxyTransport)
|
genericConfig, sharedInformers, versionedInformers, insecureServingInfo, serviceResolver, pluginInitializers, admissionPostStartHook, lastErr = BuildGenericConfig(s.ServerRunOptions, proxyTransport)
|
||||||
if lastErr != nil {
|
if lastErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -413,6 +419,7 @@ func BuildGenericConfig(
|
|||||||
insecureServingInfo *kubeserver.InsecureServingInfo,
|
insecureServingInfo *kubeserver.InsecureServingInfo,
|
||||||
serviceResolver aggregatorapiserver.ServiceResolver,
|
serviceResolver aggregatorapiserver.ServiceResolver,
|
||||||
pluginInitializers []admission.PluginInitializer,
|
pluginInitializers []admission.PluginInitializer,
|
||||||
|
admissionPostStartHook genericapiserver.PostStartHookFunc,
|
||||||
lastErr error,
|
lastErr error,
|
||||||
) {
|
) {
|
||||||
genericConfig = genericapiserver.NewConfig(legacyscheme.Codecs)
|
genericConfig = genericapiserver.NewConfig(legacyscheme.Codecs)
|
||||||
@ -539,7 +546,7 @@ func BuildGenericConfig(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pluginInitializers, err = BuildAdmissionPluginInitializers(
|
pluginInitializers, admissionPostStartHook, err = BuildAdmissionPluginInitializers(
|
||||||
s,
|
s,
|
||||||
client,
|
client,
|
||||||
sharedInformers,
|
sharedInformers,
|
||||||
@ -571,7 +578,7 @@ func BuildAdmissionPluginInitializers(
|
|||||||
sharedInformers informers.SharedInformerFactory,
|
sharedInformers informers.SharedInformerFactory,
|
||||||
serviceResolver aggregatorapiserver.ServiceResolver,
|
serviceResolver aggregatorapiserver.ServiceResolver,
|
||||||
webhookAuthWrapper webhookconfig.AuthenticationInfoResolverWrapper,
|
webhookAuthWrapper webhookconfig.AuthenticationInfoResolverWrapper,
|
||||||
) ([]admission.PluginInitializer, error) {
|
) ([]admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
|
||||||
var cloudConfig []byte
|
var cloudConfig []byte
|
||||||
|
|
||||||
if s.CloudProvider.CloudConfigFile != "" {
|
if s.CloudProvider.CloudConfigFile != "" {
|
||||||
@ -582,15 +589,33 @@ func BuildAdmissionPluginInitializers(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a dynamic restmapper. See https://github.com/kubernetes/kubernetes/pull/42615.
|
// TODO: drop this REST mapper once client is guaranteed to be non-nil
|
||||||
|
// See KUBE_API_VERSIONS comment above
|
||||||
restMapper := legacyscheme.Registry.RESTMapper()
|
restMapper := legacyscheme.Registry.RESTMapper()
|
||||||
|
admissionPostStartHook := func(_ genericapiserver.PostStartHookContext) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a functional client so we can use that to build our discovery backed REST mapper
|
||||||
|
if client != nil && client.Discovery() != nil {
|
||||||
|
// Use a discovery client capable of being refreshed.
|
||||||
|
discoveryClient := cacheddiscovery.NewMemCacheClient(client.Discovery())
|
||||||
|
discoveryRESTMapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)
|
||||||
|
|
||||||
|
restMapper = discoveryRESTMapper
|
||||||
|
admissionPostStartHook = func(context genericapiserver.PostStartHookContext) error {
|
||||||
|
discoveryRESTMapper.Reset()
|
||||||
|
go utilwait.Until(discoveryRESTMapper.Reset, 10*time.Second, context.StopCh)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
quotaConfiguration := quotainstall.NewQuotaConfigurationForAdmission()
|
quotaConfiguration := quotainstall.NewQuotaConfigurationForAdmission()
|
||||||
|
|
||||||
kubePluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, cloudConfig, restMapper, quotaConfiguration)
|
kubePluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, cloudConfig, restMapper, quotaConfiguration)
|
||||||
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthWrapper, serviceResolver)
|
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthWrapper, serviceResolver)
|
||||||
|
|
||||||
return []admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer}, nil
|
return []admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer}, admissionPostStartHook, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildAuthenticator constructs the authenticator
|
// BuildAuthenticator constructs the authenticator
|
||||||
|
@ -744,14 +744,14 @@ func startRealMasterOrDie(t *testing.T, certDir string) (*allClient, clientv3.KV
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
kubeAPIServerConfig, sharedInformers, versionedInformers, _, _, _, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
|
kubeAPIServerConfig, sharedInformers, versionedInformers, _, _, _, admissionPostStartHook, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeAPIServerConfig.ExtraConfig.APIResourceConfigSource = &allResourceSource{} // force enable all resources
|
kubeAPIServerConfig.ExtraConfig.APIResourceConfigSource = &allResourceSource{} // force enable all resources
|
||||||
|
|
||||||
kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), sharedInformers, versionedInformers)
|
kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), sharedInformers, versionedInformers, admissionPostStartHook)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ func TestAggregatedAPIServer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
kubeAPIServerConfig, sharedInformers, versionedInformers, _, _, _, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
|
kubeAPIServerConfig, sharedInformers, versionedInformers, _, _, _, admissionPostStartHook, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ func TestAggregatedAPIServer(t *testing.T) {
|
|||||||
kubeAPIServerClientConfig.ServerName = ""
|
kubeAPIServerClientConfig.ServerName = ""
|
||||||
kubeClientConfigValue.Store(kubeAPIServerClientConfig)
|
kubeClientConfigValue.Store(kubeAPIServerClientConfig)
|
||||||
|
|
||||||
kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), sharedInformers, versionedInformers)
|
kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), sharedInformers, versionedInformers, admissionPostStartHook)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ func TestAggregatedAPIServer(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// just use json because everyone speaks it
|
// just use json because everyone speaks it
|
||||||
err = wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
|
err = wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) {
|
||||||
obj := kubeClientConfigValue.Load()
|
obj := kubeClientConfigValue.Load()
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user