Merge pull request #53156 from p0lyn0mial/move_admission_initializer_interfaces_to_apiserver

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>.

moved admission interfaces WantsClientCert, WantsAuthorizer and Wants…

**What this PR does / why we need it**:
moves some admission interfaces to apiserver, hopefully moving the webhook admission in the future will be much easier.

**Release note**:

```
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2017-10-02 12:06:26 -07:00 committed by GitHub
commit dd99659dc1
15 changed files with 98 additions and 83 deletions

View File

@ -467,9 +467,28 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp
return nil, nil, nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %v", err)
}
// TODO: this is the wrong cert/key pair.
// Given the generic case of webhook admission from a generic apiserver,
// this key pair should be signed by the the API server's client CA.
// Read client cert/key for plugins that need to make calls out
certBytes, keyBytes := []byte{}, []byte{}
if len(s.ProxyClientCertFile) > 0 && len(s.ProxyClientKeyFile) > 0 {
var err error
certBytes, err = ioutil.ReadFile(s.ProxyClientCertFile)
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to read proxy client cert file from: %s, err: %v", s.ProxyClientCertFile, err)
}
keyBytes, err = ioutil.ReadFile(s.ProxyClientKeyFile)
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to read proxy client key file from: %s, err: %v", s.ProxyClientKeyFile, err)
}
}
err = s.Admission.ApplyTo(
genericConfig,
versionedInformers,
certBytes,
keyBytes,
pluginInitializer)
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to initialize admission: %v", err)
@ -498,19 +517,6 @@ func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client interna
pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, externalClient, sharedInformers, apiAuthorizer, cloudConfig, restMapper, quotaRegistry)
// Read client cert/key for plugins that need to make calls out
if len(s.ProxyClientCertFile) > 0 && len(s.ProxyClientKeyFile) > 0 {
certBytes, err := ioutil.ReadFile(s.ProxyClientCertFile)
if err != nil {
return nil, err
}
keyBytes, err := ioutil.ReadFile(s.ProxyClientKeyFile)
if err != nil {
return nil, err
}
pluginInitializer = pluginInitializer.SetClientCert(certBytes, keyBytes)
}
pluginInitializer = pluginInitializer.SetServiceResolver(serviceResolver)
pluginInitializer = pluginInitializer.SetProxyTransport(proxyTransport)

View File

@ -219,6 +219,8 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
err = s.Admission.ApplyTo(
genericConfig,
versionedInformers,
nil,
nil,
pluginInitializer,
)
if err != nil {

View File

@ -12,6 +12,7 @@ go_test(
library = ":go_default_library",
deps = [
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
],
)
@ -25,6 +26,7 @@ go_library(
"//pkg/quota:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],

View File

@ -21,6 +21,7 @@ import (
"testing"
"k8s.io/apiserver/pkg/admission"
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/authorization/authorizer"
)
@ -51,7 +52,7 @@ func (self *WantAuthorizerAdmission) SetAuthorizer(a authorizer.Authorizer) {
}
var _ admission.Interface = &WantAuthorizerAdmission{}
var _ WantsAuthorizer = &WantAuthorizerAdmission{}
var _ genericadmissioninit.WantsAuthorizer = &WantAuthorizerAdmission{}
// TestWantsAuthorizer ensures that the authorizer is injected when the WantsAuthorizer
// interface is implemented.
@ -106,19 +107,3 @@ func TestWantsServiceResolver(t *testing.T) {
t.Errorf("plumbing fail - %v %v#", ok, got)
}
}
type clientCertWanter struct {
doNothingAdmission
gotCert, gotKey []byte
}
func (s *clientCertWanter) SetClientCert(cert, key []byte) { s.gotCert, s.gotKey = cert, key }
func TestWantsClientCert(t *testing.T) {
i := &PluginInitializer{}
ccw := &clientCertWanter{}
i.SetClientCert([]byte("cert"), []byte("key")).Initialize(ccw)
if string(ccw.gotCert) != "cert" || string(ccw.gotKey) != "key" {
t.Errorf("plumbing fail - %v %v", ccw.gotCert, ccw.gotKey)
}
}

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apiserver/pkg/admission"
admissioninit "k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/authorization/authorizer"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
@ -37,24 +38,12 @@ type WantsInternalKubeClientSet interface {
admission.Validator
}
// WantsExternalKubeClientSet defines a function which sets ClientSet for admission plugins that need it
type WantsExternalKubeClientSet interface {
SetExternalKubeClientSet(clientset.Interface)
admission.Validator
}
// WantsInternalKubeInformerFactory defines a function which sets InformerFactory for admission plugins that need it
type WantsInternalKubeInformerFactory interface {
SetInternalKubeInformerFactory(informers.SharedInformerFactory)
admission.Validator
}
// WantsAuthorizer defines a function which sets Authorizer for admission plugins that need it.
type WantsAuthorizer interface {
SetAuthorizer(authorizer.Authorizer)
admission.Validator
}
// WantsCloudConfig defines a function which sets CloudConfig for admission plugins that need it.
type WantsCloudConfig interface {
SetCloudConfig([]byte)
@ -77,12 +66,6 @@ type WantsServiceResolver interface {
SetServiceResolver(ServiceResolver)
}
// WantsClientCert defines a fuction that accepts a cert & key for admission
// plugins that need to make calls and prove their identity.
type WantsClientCert interface {
SetClientCert(cert, key []byte)
}
// ServiceResolver knows how to convert a service reference into an actual
// location.
type ServiceResolver interface {
@ -106,8 +89,6 @@ type PluginInitializer struct {
serviceResolver ServiceResolver
// for proving we are apiserver in call-outs
clientCert []byte
clientKey []byte
proxyTransport *http.Transport
}
@ -142,14 +123,6 @@ func (i *PluginInitializer) SetServiceResolver(s ServiceResolver) *PluginInitial
return i
}
// SetClientCert sets the client cert & key (identity used for calling out to
// web hooks) which is needed by some plugins.
func (i *PluginInitializer) SetClientCert(cert, key []byte) *PluginInitializer {
i.clientCert = cert
i.clientKey = key
return i
}
// SetProxyTransport sets the proxyTransport which is needed by some plugins.
func (i *PluginInitializer) SetProxyTransport(proxyTransport *http.Transport) *PluginInitializer {
i.proxyTransport = proxyTransport
@ -163,7 +136,7 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
wants.SetInternalKubeClientSet(i.internalClient)
}
if wants, ok := plugin.(WantsExternalKubeClientSet); ok {
if wants, ok := plugin.(admissioninit.WantsExternalKubeClientSet); ok {
wants.SetExternalKubeClientSet(i.externalClient)
}
@ -171,7 +144,7 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
wants.SetInternalKubeInformerFactory(i.informers)
}
if wants, ok := plugin.(WantsAuthorizer); ok {
if wants, ok := plugin.(admissioninit.WantsAuthorizer); ok {
wants.SetAuthorizer(i.authorizer)
}
@ -191,13 +164,6 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
wants.SetServiceResolver(i.serviceResolver)
}
if wants, ok := plugin.(WantsClientCert); ok {
if i.clientCert == nil || i.clientKey == nil {
panic("An admission plugin wants a client cert/key, but they were not provided.")
}
wants.SetClientCert(i.clientCert, i.clientKey)
}
if wants, ok := plugin.(WantsProxyTransport); ok {
wants.SetProxyTransport(i.proxyTransport)
}

View File

@ -24,6 +24,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
],

View File

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/kubernetes/pkg/api"
@ -82,7 +83,7 @@ func (plugin *podSecurityPolicyPlugin) Validate() error {
}
var _ admission.Interface = &podSecurityPolicyPlugin{}
var _ kubeapiserveradmission.WantsAuthorizer = &podSecurityPolicyPlugin{}
var _ genericadmissioninit.WantsAuthorizer = &podSecurityPolicyPlugin{}
var _ kubeapiserveradmission.WantsInternalKubeInformerFactory = &podSecurityPolicyPlugin{}
// NewPlugin creates a new PSP admission plugin.

View File

@ -52,6 +52,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],

View File

@ -38,6 +38,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api"
@ -114,8 +115,8 @@ type GenericAdmissionWebhook struct {
var (
_ = admissioninit.WantsServiceResolver(&GenericAdmissionWebhook{})
_ = admissioninit.WantsClientCert(&GenericAdmissionWebhook{})
_ = admissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{})
_ = genericadmissioninit.WantsClientCert(&GenericAdmissionWebhook{})
_ = genericadmissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{})
)
func (a *GenericAdmissionWebhook) SetProxyTransport(pt *http.Transport) {
@ -140,6 +141,9 @@ func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Inte
}
func (a *GenericAdmissionWebhook) Validate() error {
if a.clientCert == nil || a.clientKey == nil {
return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a client certificate and the private key to be provided")
}
if a.hookSource == nil {
return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a Kubernetes client to be provided")
}

View File

@ -27,14 +27,21 @@ type pluginInitializer struct {
externalClient kubernetes.Interface
externalInformers informers.SharedInformerFactory
authorizer authorizer.Authorizer
// serverIdentifyingClientCert used to provide identity when calling out to admission plugins
serverIdentifyingClientCert []byte
// serverIdentifyingClientKey private key for the client certificate used when calling out to admission plugins
serverIdentifyingClientKey []byte
}
// New creates an instance of admission plugins initializer.
func New(extClientset kubernetes.Interface, extInformers informers.SharedInformerFactory, authz authorizer.Authorizer) (pluginInitializer, error) {
// TODO(p0lyn0mial): make the parameters public, this construction seems to be redundant.
func New(extClientset kubernetes.Interface, extInformers informers.SharedInformerFactory, authz authorizer.Authorizer, serverIdentifyingClientCert, serverIdentifyingClientKey []byte) (pluginInitializer, error) {
return pluginInitializer{
externalClient: extClientset,
externalInformers: extInformers,
authorizer: authz,
serverIdentifyingClientCert: serverIdentifyingClientCert,
serverIdentifyingClientKey: serverIdentifyingClientKey,
}, nil
}
@ -52,6 +59,10 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) {
if wants, ok := plugin.(WantsAuthorizer); ok {
wants.SetAuthorizer(i.authorizer)
}
if wants, ok := plugin.(WantsClientCert); ok {
wants.SetClientCert(i.serverIdentifyingClientCert, i.serverIdentifyingClientKey)
}
}
var _ admission.PluginInitializer = pluginInitializer{}

View File

@ -31,7 +31,7 @@ import (
// TestWantsAuthorizer ensures that the authorizer is injected
// when the WantsAuthorizer interface is implemented by a plugin.
func TestWantsAuthorizer(t *testing.T) {
target, err := initializer.New(nil, nil, &TestAuthorizer{})
target, err := initializer.New(nil, nil, &TestAuthorizer{}, nil, nil)
if err != nil {
t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error())
}
@ -46,7 +46,7 @@ func TestWantsAuthorizer(t *testing.T) {
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
func TestWantsExternalKubeClientSet(t *testing.T) {
cs := &fake.Clientset{}
target, err := initializer.New(cs, nil, &TestAuthorizer{})
target, err := initializer.New(cs, nil, &TestAuthorizer{}, nil, nil)
if err != nil {
t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error())
}
@ -62,7 +62,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) {
func TestWantsExternalKubeInformerFactory(t *testing.T) {
cs := &fake.Clientset{}
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
target, err := initializer.New(cs, sf, &TestAuthorizer{})
target, err := initializer.New(cs, sf, &TestAuthorizer{}, nil, nil)
if err != nil {
t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error())
}
@ -73,6 +73,20 @@ func TestWantsExternalKubeInformerFactory(t *testing.T) {
}
}
// TestWantsClientCert ensures that the client certificate and key are injected
// when the WantsClientCert interface is implemented by a plugin.
func TestWantsClientCert(t *testing.T) {
target, err := initializer.New(nil, nil, nil, []byte("cert"), []byte("key"))
if err != nil {
t.Fatalf("expected to create an instance of initializer but got an error = %s", err.Error())
}
wantClientCert := &clientCertWanter{}
target.Initialize(wantClientCert)
if string(wantClientCert.gotCert) != "cert" || string(wantClientCert.gotKey) != "key" {
t.Errorf("expected client cert to be initialized, clientCert = %v, clientKey = %v", wantClientCert.gotCert, wantClientCert.gotKey)
}
}
// WantExternalKubeInformerFactory is a test stub that fulfills the WantsExternalKubeInformerFactory interface
type WantExternalKubeInformerFactory struct {
sf informers.SharedInformerFactory
@ -114,9 +128,19 @@ func (self *WantAuthorizerAdmission) Validate() error { re
var _ admission.Interface = &WantAuthorizerAdmission{}
var _ initializer.WantsAuthorizer = &WantAuthorizerAdmission{}
// TestAuthorizer is a test stub for testing that fulfills the authorizer interface.
// TestAuthorizer is a test stub that fulfills the WantsAuthorizer interface.
type TestAuthorizer struct{}
func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized bool, reason string, err error) {
return false, "", nil
}
// wantClientCert is a test stub for testing that fulfulls the WantsClientCert interface.
type clientCertWanter struct {
gotCert, gotKey []byte
}
func (s *clientCertWanter) SetClientCert(cert, key []byte) { s.gotCert, s.gotKey = cert, key }
func (s *clientCertWanter) Admit(a admission.Attributes) error { return nil }
func (s *clientCertWanter) Handles(o admission.Operation) bool { return false }
func (s *clientCertWanter) Validate() error { return nil }

View File

@ -40,3 +40,10 @@ type WantsAuthorizer interface {
SetAuthorizer(authorizer.Authorizer)
admission.Validator
}
// WantsClientCert defines a fuction that accepts a cert & key for admission
// plugins that need to make calls and prove their identity.
type WantsClientCert interface {
SetClientCert(cert, key []byte)
admission.Validator
}

View File

@ -48,7 +48,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (
if err != nil {
return nil, f, err
}
pluginInitializer, err := kubeadmission.New(c, f, nil)
pluginInitializer, err := kubeadmission.New(c, f, nil, nil, nil)
if err != nil {
return handler, f, err
}

View File

@ -73,9 +73,14 @@ func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
// In addition the method lazily initializes a generic plugin that is appended to the list of pluginInitializers
// note this method uses:
// genericconfig.LoopbackClientConfig
// genericconfig.SharedInformerFactory
// genericconfig.Authorizer
func (a *AdmissionOptions) ApplyTo(c *server.Config, informers informers.SharedInformerFactory, pluginInitializers ...admission.PluginInitializer) error {
func (a *AdmissionOptions) ApplyTo(
c *server.Config,
informers informers.SharedInformerFactory,
serverIdentifyingClientCert []byte,
serverIdentifyingClientKey []byte,
pluginInitializers ...admission.PluginInitializer,
) error {
pluginNames := a.PluginNames
if len(a.PluginNames) == 0 {
pluginNames = a.enabledPluginNames()
@ -90,7 +95,7 @@ func (a *AdmissionOptions) ApplyTo(c *server.Config, informers informers.SharedI
if err != nil {
return err
}
genericInitializer, err := initializer.New(clientset, informers, c.Authorizer)
genericInitializer, err := initializer.New(clientset, informers, c.Authorizer, serverIdentifyingClientCert, serverIdentifyingClientKey)
if err != nil {
return err
}

View File

@ -119,7 +119,7 @@ func (o WardleServerOptions) Config() (*apiserver.Config, error) {
return nil, err
}
if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, admissionInitializer); err != nil {
if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, admissionInitializer); err != nil {
return nil, err
}