Skip to content

Commit

Permalink
Merge pull request #124621 from sttts/sttts-controlplane-admission-in…
Browse files Browse the repository at this point in the history
…itializers

kube-apiserver: split admission initializers into generic and non-generic
  • Loading branch information
k8s-ci-robot committed May 1, 2024
2 parents b276088 + acbb89d commit 6abdfb9
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 106 deletions.
34 changes: 23 additions & 11 deletions cmd/kube-apiserver/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
"k8s.io/klog/v2"
aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
controlplaneadmission "k8s.io/kubernetes/pkg/controlplane/apiserver/admission"

"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
"k8s.io/kubernetes/pkg/api/legacyscheme"
Expand Down Expand Up @@ -223,8 +224,29 @@ func CreateKubeAPIServerConfig(opts options.CompletedOptions) (
generatedopenapi.GetOpenAPIDefinitions,
)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, fmt.Errorf("failed to create generic config: %w", err)
}

// generic controlplane admission initializers
controlPlaneAdmissionConfig := &controlplaneadmission.Config{
ExternalInformers: versionedInformers,
LoopbackClientConfig: genericConfig.LoopbackClientConfig,
}
serviceResolver := buildServiceResolver(opts.EnableAggregatorRouting, genericConfig.LoopbackClientConfig.Host, versionedInformers)
pluginInitializers, err := controlPlaneAdmissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver, genericConfig.TracerProvider)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %w", err)
}

// additional kube admission initializers
kubeAdmissionConfig := &kubeapiserveradmission.Config{
CloudConfigFile: opts.CloudProvider.CloudConfigFile,
}
kubeInitializers, err := kubeAdmissionConfig.New()
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %w", err)
}
pluginInitializers = append(pluginInitializers, kubeInitializers...)

capabilities.Setup(opts.AllowPrivileged, opts.MaxConnectionBytesPerSec)

Expand Down Expand Up @@ -300,16 +322,6 @@ func CreateKubeAPIServerConfig(opts options.CompletedOptions) (
}

// setup admission
admissionConfig := &kubeapiserveradmission.Config{
ExternalInformers: versionedInformers,
LoopbackClientConfig: genericConfig.LoopbackClientConfig,
CloudConfigFile: opts.CloudProvider.CloudConfigFile,
}
serviceResolver := buildServiceResolver(opts.EnableAggregatorRouting, genericConfig.LoopbackClientConfig.Host, versionedInformers)
pluginInitializers, err := admissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver, genericConfig.TracerProvider)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %v", err)
}
clientgoExternalClient, err := clientset.NewForConfig(genericConfig.LoopbackClientConfig)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create real client-go external client: %w", err)
Expand Down
51 changes: 51 additions & 0 deletions pkg/controlplane/apiserver/admission/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package admission

import (
"net/http"

"go.opentelemetry.io/otel/trace"

"k8s.io/apiserver/pkg/admission"
webhookinit "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
egressselector "k8s.io/apiserver/pkg/server/egressselector"
"k8s.io/apiserver/pkg/util/webhook"
externalinformers "k8s.io/client-go/informers"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/exclusion"
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
)

// Config holds the configuration needed to for initialize the admission plugins
type Config struct {
LoopbackClientConfig *rest.Config
ExternalInformers externalinformers.SharedInformerFactory
}

// New sets up the plugins and admission start hooks needed for admission
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider) ([]admission.PluginInitializer, error) {
webhookAuthResolverWrapper := webhook.NewDefaultAuthenticationInfoResolverWrapper(proxyTransport, egressSelector, c.LoopbackClientConfig, tp)
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthResolverWrapper, serviceResolver)

kubePluginInitializer := NewPluginInitializer(
quotainstall.NewQuotaConfigurationForAdmission(),
exclusion.Excluded(),
)

return []admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer}, nil
}
55 changes: 55 additions & 0 deletions pkg/controlplane/apiserver/admission/initializer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package admission

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/initializer"
quota "k8s.io/apiserver/pkg/quota/v1"
)

// PluginInitializer is used for initialization of the generic controlplane admission plugins.
type PluginInitializer struct {
quotaConfiguration quota.Configuration
excludedAdmissionResources []schema.GroupResource
}

var _ admission.PluginInitializer = &PluginInitializer{}

// NewPluginInitializer constructs new instance of PluginInitializer
func NewPluginInitializer(
quotaConfiguration quota.Configuration,
excludedAdmissionResources []schema.GroupResource,
) *PluginInitializer {
return &PluginInitializer{
quotaConfiguration: quotaConfiguration,
excludedAdmissionResources: excludedAdmissionResources,
}
}

// Initialize checks the initialization interfaces implemented by each plugin
// and provide the appropriate initialization data
func (i *PluginInitializer) Initialize(plugin admission.Interface) {
if wants, ok := plugin.(initializer.WantsQuotaConfiguration); ok {
wants.SetQuotaConfiguration(i.quotaConfiguration)
}

if wants, ok := plugin.(initializer.WantsExcludedAdmissionResources); ok {
wants.SetExcludedAdmissionResources(i.excludedAdmissionResources)
}
}
65 changes: 65 additions & 0 deletions pkg/controlplane/apiserver/admission/initializer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package admission

import (
"context"
"testing"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
quota "k8s.io/apiserver/pkg/quota/v1"
)

type doNothingAdmission struct{}

func (doNothingAdmission) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
return nil
}
func (doNothingAdmission) Handles(o admission.Operation) bool { return false }
func (doNothingAdmission) Validate() error { return nil }

type doNothingPluginInitialization struct{}

func (doNothingPluginInitialization) ValidateInitialization() error { return nil }

type doNothingQuotaConfiguration struct{}

func (doNothingQuotaConfiguration) IgnoredResources() map[schema.GroupResource]struct{} { return nil }

func (doNothingQuotaConfiguration) Evaluators() []quota.Evaluator { return nil }

type WantsQuotaConfigurationAdmissionPlugin struct {
doNothingAdmission
doNothingPluginInitialization
config quota.Configuration
}

func (p *WantsQuotaConfigurationAdmissionPlugin) SetQuotaConfiguration(config quota.Configuration) {
p.config = config
}

func TestQuotaConfigurationAdmissionPlugin(t *testing.T) {
config := doNothingQuotaConfiguration{}
initializer := NewPluginInitializer(config, nil)
wantsQuotaConfigurationAdmission := &WantsQuotaConfigurationAdmissionPlugin{}
initializer.Initialize(wantsQuotaConfigurationAdmission)

if wantsQuotaConfigurationAdmission.config == nil {
t.Errorf("Expected quota configuration to be initialized but found nil")
}
}
26 changes: 3 additions & 23 deletions pkg/kubeapiserver/admission/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,20 @@ limitations under the License.
package admission

import (
"net/http"
"os"

"k8s.io/klog/v2"

"go.opentelemetry.io/otel/trace"

"k8s.io/apiserver/pkg/admission"
webhookinit "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
"k8s.io/apiserver/pkg/server/egressselector"
"k8s.io/apiserver/pkg/util/webhook"
externalinformers "k8s.io/client-go/informers"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/exclusion"
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
)

// Config holds the configuration needed to for initialize the admission plugins
type Config struct {
CloudConfigFile string
LoopbackClientConfig *rest.Config
ExternalInformers externalinformers.SharedInformerFactory
CloudConfigFile string
}

// New sets up the plugins and admission start hooks needed for admission
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider) ([]admission.PluginInitializer, error) {
webhookAuthResolverWrapper := webhook.NewDefaultAuthenticationInfoResolverWrapper(proxyTransport, egressSelector, c.LoopbackClientConfig, tp)
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthResolverWrapper, serviceResolver)

func (c *Config) New() ([]admission.PluginInitializer, error) {
var cloudConfig []byte
if c.CloudConfigFile != "" {
var err error
Expand All @@ -54,11 +39,6 @@ func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselec
klog.Fatalf("Error reading from cloud configuration file %s: %#v", c.CloudConfigFile, err)
}
}
kubePluginInitializer := NewPluginInitializer(
cloudConfig,
quotainstall.NewQuotaConfigurationForAdmission(),
exclusion.Excluded(),
)

return []admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer}, nil
return []admission.PluginInitializer{NewPluginInitializer(cloudConfig)}, nil
}
27 changes: 3 additions & 24 deletions pkg/kubeapiserver/admission/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ limitations under the License.
package admission

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/initializer"
quota "k8s.io/apiserver/pkg/quota/v1"
)

// TODO add a `WantsToRun` which takes a stopCh. Might make it generic.
Expand All @@ -32,25 +29,15 @@ type WantsCloudConfig interface {

// PluginInitializer is used for initialization of the Kubernetes specific admission plugins.
type PluginInitializer struct {
cloudConfig []byte
quotaConfiguration quota.Configuration
excludedAdmissionResources []schema.GroupResource
cloudConfig []byte
}

var _ admission.PluginInitializer = &PluginInitializer{}

// NewPluginInitializer constructs new instance of PluginInitializer
// TODO: switch these parameters to use the builder pattern or just make them
// all public, this construction method is pointless boilerplate.
func NewPluginInitializer(
cloudConfig []byte,
quotaConfiguration quota.Configuration,
excludedAdmissionResources []schema.GroupResource,
) *PluginInitializer {
func NewPluginInitializer(cloudConfig []byte) *PluginInitializer {
return &PluginInitializer{
cloudConfig: cloudConfig,
quotaConfiguration: quotaConfiguration,
excludedAdmissionResources: excludedAdmissionResources,
cloudConfig: cloudConfig,
}
}

Expand All @@ -60,12 +47,4 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
if wants, ok := plugin.(WantsCloudConfig); ok {
wants.SetCloudConfig(i.cloudConfig)
}

if wants, ok := plugin.(initializer.WantsQuotaConfiguration); ok {
wants.SetQuotaConfiguration(i.quotaConfiguration)
}

if wants, ok := plugin.(initializer.WantsExcludedAdmissionResources); ok {
wants.SetExcludedAdmissionResources(i.excludedAdmissionResources)
}
}
35 changes: 1 addition & 34 deletions pkg/kubeapiserver/admission/initializer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import (
"context"
"testing"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
quota "k8s.io/apiserver/pkg/quota/v1"
)

type doNothingAdmission struct{}
Expand All @@ -33,10 +31,6 @@ func (doNothingAdmission) Admit(ctx context.Context, a admission.Attributes, o a
func (doNothingAdmission) Handles(o admission.Operation) bool { return false }
func (doNothingAdmission) Validate() error { return nil }

type doNothingPluginInitialization struct{}

func (doNothingPluginInitialization) ValidateInitialization() error { return nil }

type WantsCloudConfigAdmissionPlugin struct {
doNothingAdmission
cloudConfig []byte
Expand All @@ -48,38 +42,11 @@ func (p *WantsCloudConfigAdmissionPlugin) SetCloudConfig(cloudConfig []byte) {

func TestCloudConfigAdmissionPlugin(t *testing.T) {
cloudConfig := []byte("cloud-configuration")
initializer := NewPluginInitializer(cloudConfig, nil, nil)
initializer := NewPluginInitializer(cloudConfig)
wantsCloudConfigAdmission := &WantsCloudConfigAdmissionPlugin{}
initializer.Initialize(wantsCloudConfigAdmission)

if wantsCloudConfigAdmission.cloudConfig == nil {
t.Errorf("Expected cloud config to be initialized but found nil")
}
}

type doNothingQuotaConfiguration struct{}

func (doNothingQuotaConfiguration) IgnoredResources() map[schema.GroupResource]struct{} { return nil }

func (doNothingQuotaConfiguration) Evaluators() []quota.Evaluator { return nil }

type WantsQuotaConfigurationAdmissionPlugin struct {
doNothingAdmission
doNothingPluginInitialization
config quota.Configuration
}

func (p *WantsQuotaConfigurationAdmissionPlugin) SetQuotaConfiguration(config quota.Configuration) {
p.config = config
}

func TestQuotaConfigurationAdmissionPlugin(t *testing.T) {
config := doNothingQuotaConfiguration{}
initializer := NewPluginInitializer(nil, config, nil)
wantsQuotaConfigurationAdmission := &WantsQuotaConfigurationAdmissionPlugin{}
initializer.Initialize(wantsQuotaConfigurationAdmission)

if wantsQuotaConfigurationAdmission.config == nil {
t.Errorf("Expected quota configuration to be initialized but found nil")
}
}

0 comments on commit 6abdfb9

Please sign in to comment.