refactored admission to avoid internal client references

pull/6/head
deads2k 2016-12-21 16:16:16 -05:00
parent 834f193b25
commit 2861509b6d
65 changed files with 559 additions and 424 deletions

View File

@ -30,6 +30,7 @@ go_library(
"//pkg/generated/openapi:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/filters:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/kubeapiserver/authenticator:go_default_library",
"//pkg/master:go_default_library",
"//pkg/registry/cachesize:go_default_library",

View File

@ -49,6 +49,7 @@ import (
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/filters"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/registry/cachesize"
@ -266,8 +267,8 @@ func Run(s *options.ServerRunOptions) error {
}
admissionControlPluginNames := strings.Split(s.GenericServerRunOptions.AdmissionControl, ",")
pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.GenericServerRunOptions.AdmissionControlConfigFile, pluginInitializer)
pluginInitializer := kubeadmission.NewPluginInitializer(client, sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(admissionControlPluginNames, s.GenericServerRunOptions.AdmissionControlConfigFile, pluginInitializer)
if err != nil {
return fmt.Errorf("failed to initialize plugins: %v", err)
}

View File

@ -41,6 +41,7 @@ go_library(
"//pkg/generated/openapi:go_default_library",
"//pkg/genericapiserver:go_default_library",
"//pkg/genericapiserver/filters:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/registry/batch/job/etcd:go_default_library",
"//pkg/registry/cachesize:go_default_library",
"//pkg/registry/core/configmap/etcd:go_default_library",

View File

@ -37,6 +37,7 @@ import (
"k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/filters"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/generic"
genericregistry "k8s.io/kubernetes/pkg/registry/generic/registry"
@ -156,8 +157,8 @@ func Run(s *options.ServerRunOptions) error {
}
admissionControlPluginNames := strings.Split(s.GenericServerRunOptions.AdmissionControl, ",")
pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.GenericServerRunOptions.AdmissionControlConfigFile, pluginInitializer)
pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(admissionControlPluginNames, s.GenericServerRunOptions.AdmissionControlConfigFile, pluginInitializer)
if err != nil {
return fmt.Errorf("failed to initialize plugins: %v", err)
}

View File

@ -15,19 +15,14 @@ go_library(
"chain.go",
"errors.go",
"handler.go",
"init.go",
"interfaces.go",
"plugins.go",
"types.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/errors:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/auth/authorizer:go_default_library",
"//pkg/auth/user:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/errors:go_default_library",
@ -38,14 +33,8 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"chain_test.go",
"init_test.go",
],
srcs = ["chain_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/auth/authorizer:go_default_library",
"//pkg/runtime/schema:go_default_library",
],
deps = ["//pkg/runtime/schema:go_default_library"],
)

View File

@ -16,29 +16,9 @@ limitations under the License.
package admission
import clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
// chainAdmissionHandler is an instance of admission.Interface that performs admission control using a chain of admission handlers
type chainAdmissionHandler []Interface
// NewFromPlugins returns an admission.Interface that will enforce admission control decisions of all
// the given plugins.
func NewFromPlugins(client clientset.Interface, pluginNames []string, configFilePath string, plugInit PluginInitializer) (Interface, error) {
plugins := []Interface{}
for _, pluginName := range pluginNames {
plugin := InitPlugin(pluginName, client, configFilePath)
if plugin != nil {
plugins = append(plugins, plugin)
}
}
plugInit.Initialize(plugins)
// ensure that plugins have been properly initialized
if err := Validate(plugins); err != nil {
return nil, err
}
return chainAdmissionHandler(plugins), nil
}
// NewChainHandler creates a new chain handler from an array of handlers. Used for testing.
func NewChainHandler(handlers ...Interface) Interface {
return chainAdmissionHandler(handlers)

View File

@ -1,70 +0,0 @@
/*
Copyright 2016 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/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/controller/informers"
)
// PluginInitializer is used for initialization of shareable resources between admission plugins.
// After initialization the resources have to be set separately
type PluginInitializer interface {
Initialize(plugins []Interface)
}
type pluginInitializer struct {
informers informers.SharedInformerFactory
authorizer authorizer.Authorizer
}
// NewPluginInitializer constructs new instance of PluginInitializer
func NewPluginInitializer(sharedInformers informers.SharedInformerFactory, authz authorizer.Authorizer) PluginInitializer {
plugInit := &pluginInitializer{
informers: sharedInformers,
authorizer: authz,
}
return plugInit
}
// Initialize checks the initialization interfaces implemented by each plugin
// and provide the appropriate initialization data
func (i *pluginInitializer) Initialize(plugins []Interface) {
for _, plugin := range plugins {
if wantsInformerFactory, ok := plugin.(WantsInformerFactory); ok {
wantsInformerFactory.SetInformerFactory(i.informers)
}
if wantsAuthorizer, ok := plugin.(WantsAuthorizer); ok {
wantsAuthorizer.SetAuthorizer(i.authorizer)
}
}
}
// Validate will call the Validate function in each plugin if they implement
// the Validator interface.
func Validate(plugins []Interface) error {
for _, plugin := range plugins {
if validater, ok := plugin.(Validator); ok {
err := validater.Validate()
if err != nil {
return err
}
}
}
return nil
}

View File

@ -69,3 +69,15 @@ const (
Delete Operation = "DELETE"
Connect Operation = "CONNECT"
)
// PluginInitializer is used for initialization of shareable resources between admission plugins.
// After initialization the resources have to be set separately
type PluginInitializer interface {
Initialize(plugin Interface)
}
// Validator holds Validate functions, which are responsible for validation of initialized shared resources
// and should be implemented on admission plugins
type Validator interface {
Validate() error
}

View File

@ -18,6 +18,7 @@ package admission
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
@ -26,15 +27,13 @@ import (
"sync"
"github.com/golang/glog"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
)
// Factory is a function that returns an Interface for admission decisions.
// The config parameter provides an io.Reader handler to the factory in
// order to load specific configurations. If no configuration is provided
// the parameter is nil.
type Factory func(client clientset.Interface, config io.Reader) (Interface, error)
type Factory func(config io.Reader) (Interface, error)
// All registered admission options.
var (
@ -79,7 +78,7 @@ func RegisterPlugin(name string, plugin Factory) {
// the name is not known. The error is returned only when the named provider was
// known but failed to initialize. The config parameter specifies the io.Reader
// handler of the configuration file for the cloud provider, or nil for no configuration.
func getPlugin(name string, client clientset.Interface, config io.Reader) (Interface, bool, error) {
func getPlugin(name string, config io.Reader) (Interface, bool, error) {
pluginsMutex.Lock()
defer pluginsMutex.Unlock()
f, found := plugins[name]
@ -95,7 +94,7 @@ func getPlugin(name string, client clientset.Interface, config io.Reader) (Inter
return nil, true, nil
}
ret, err := f(client, config2)
ret, err := f(config2)
return ret, true, err
}
@ -113,8 +112,24 @@ func splitStream(config io.Reader) (io.Reader, io.Reader, error) {
return bytes.NewBuffer(configBytes), bytes.NewBuffer(configBytes), nil
}
// NewFromPlugins returns an admission.Interface that will enforce admission control decisions of all
// the given plugins.
func NewFromPlugins(pluginNames []string, configFilePath string, pluginInitializer PluginInitializer) (Interface, error) {
plugins := []Interface{}
for _, pluginName := range pluginNames {
plugin, err := InitPlugin(pluginName, configFilePath, pluginInitializer)
if err != nil {
return nil, err
}
if plugin != nil {
plugins = append(plugins, plugin)
}
}
return chainAdmissionHandler(plugins), nil
}
// InitPlugin creates an instance of the named interface.
func InitPlugin(name string, client clientset.Interface, configFilePath string) Interface {
func InitPlugin(name string, configFilePath string, pluginInitializer PluginInitializer) (Interface, error) {
var (
config *os.File
err error
@ -122,7 +137,7 @@ func InitPlugin(name string, client clientset.Interface, configFilePath string)
if name == "" {
glog.Info("No admission plugin specified.")
return nil
return nil, nil
}
if configFilePath != "" {
@ -135,13 +150,39 @@ func InitPlugin(name string, client clientset.Interface, configFilePath string)
defer config.Close()
}
plugin, found, err := getPlugin(name, client, config)
plugin, found, err := getPlugin(name, config)
if err != nil {
glog.Fatalf("Couldn't init admission plugin %q: %v", name, err)
return nil, fmt.Errorf("Couldn't init admission plugin %q: %v", name, err)
}
if !found {
glog.Fatalf("Unknown admission plugin: %s", name)
return nil, fmt.Errorf("Unknown admission plugin: %s", name)
}
return plugin
pluginInitializer.Initialize(plugin)
// ensure that plugins have been properly initialized
if err := Validate(plugin); err != nil {
return nil, err
}
return plugin, nil
}
// Validate will call the Validate function in each plugin if they implement
// the Validator interface.
func Validate(plugin Interface) error {
if validater, ok := plugin.(Validator); ok {
err := validater.Validate()
if err != nil {
return err
}
}
return nil
}
type PluginInitializers []PluginInitializer
func (pp PluginInitializers) Initialize(plugin Interface) {
for _, p := range pp {
p.Initialize(plugin)
}
}

View File

@ -1,40 +0,0 @@
/*
Copyright 2016 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/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/controller/informers"
)
// Validator holds Validate functions, which are responsible for validation of initialized shared resources
// and should be implemented on admission plugins
type Validator interface {
Validate() error
}
// WantsInformerFactory defines a function which sets InformerFactory for admission plugins that need it
type WantsInformerFactory interface {
SetInformerFactory(informers.SharedInformerFactory)
Validator
}
// WantsAuthorizer defines a function which sets Authorizer for admission plugins that need it.
type WantsAuthorizer interface {
SetAuthorizer(authorizer.Authorizer)
Validator
}

View File

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["init_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/auth/authorizer:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["initializer.go"],
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/auth/authorizer:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/informers:go_default_library",
],
)

View File

@ -19,6 +19,7 @@ package admission
import (
"testing"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/auth/authorizer"
)
@ -40,19 +41,19 @@ type WantAuthorizerAdmission struct {
func (self *WantAuthorizerAdmission) SetAuthorizer(a authorizer.Authorizer) {
self.auth = a
}
func (self *WantAuthorizerAdmission) Admit(a Attributes) error { return nil }
func (self *WantAuthorizerAdmission) Handles(o Operation) bool { return false }
func (self *WantAuthorizerAdmission) Validate() error { return nil }
func (self *WantAuthorizerAdmission) Admit(a admission.Attributes) error { return nil }
func (self *WantAuthorizerAdmission) Handles(o admission.Operation) bool { return false }
func (self *WantAuthorizerAdmission) Validate() error { return nil }
var _ Interface = &WantAuthorizerAdmission{}
var _ admission.Interface = &WantAuthorizerAdmission{}
var _ WantsAuthorizer = &WantAuthorizerAdmission{}
// TestWantsAuthorizer ensures that the authorizer is injected when the WantsAuthorizer
// interface is implemented.
func TestWantsAuthorizer(t *testing.T) {
initializer := NewPluginInitializer(nil, &TestAuthorizer{})
initializer := NewPluginInitializer(nil, nil, &TestAuthorizer{})
wantAuthorizerAdmission := &WantAuthorizerAdmission{}
initializer.Initialize([]Interface{wantAuthorizerAdmission})
initializer.Initialize(wantAuthorizerAdmission)
if wantAuthorizerAdmission.auth == nil {
t.Errorf("expected authorizer to be initialized but found nil")
}

View File

@ -0,0 +1,77 @@
/*
Copyright 2016 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/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller/informers"
)
// TODO add a `WantsToRun` which takes a stopCh. Might make it generic.
// WantsInformerFactory defines a function which sets InformerFactory for admission plugins that need it
type WantsInternalClientSet interface {
SetInternalClientSet(internalclientset.Interface)
admission.Validator
}
// WantsInformerFactory defines a function which sets InformerFactory for admission plugins that need it
type WantsInformerFactory interface {
SetInformerFactory(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
}
type pluginInitializer struct {
internalClient internalclientset.Interface
informers informers.SharedInformerFactory
authorizer authorizer.Authorizer
}
var _ admission.PluginInitializer = pluginInitializer{}
// NewPluginInitializer constructs new instance of PluginInitializer
func NewPluginInitializer(internalClient internalclientset.Interface, sharedInformers informers.SharedInformerFactory, authz authorizer.Authorizer) admission.PluginInitializer {
return pluginInitializer{
internalClient: internalClient,
informers: sharedInformers,
authorizer: authz,
}
}
// 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.(WantsInternalClientSet); ok {
wants.SetInternalClientSet(i.internalClient)
}
if wants, ok := plugin.(WantsInformerFactory); ok {
wants.SetInformerFactory(i.informers)
}
if wants, ok := plugin.(WantsAuthorizer); ok {
wants.SetAuthorizer(i.authorizer)
}
}

View File

@ -12,10 +12,7 @@ go_library(
name = "go_default_library",
srcs = ["admission.go"],
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
],
deps = ["//pkg/admission:go_default_library"],
)
go_test(

View File

@ -19,13 +19,11 @@ package admit
import (
"io"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
)
func init() {
admission.RegisterPlugin("AlwaysAdmit", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("AlwaysAdmit", func(config io.Reader) (admission.Interface, error) {
return NewAlwaysAdmit(), nil
})
}

View File

@ -16,7 +16,6 @@ go_library(
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
],
)

View File

@ -27,15 +27,13 @@ package alwayspullimages
import (
"io"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
apierrors "k8s.io/kubernetes/pkg/api/errors"
)
func init() {
admission.RegisterPlugin("AlwaysPullImages", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("AlwaysPullImages", func(config io.Reader) (admission.Interface, error) {
return NewAlwaysPullImages(), nil
})
}

View File

@ -20,7 +20,6 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//vendor:github.com/golang/glog",
],
)

View File

@ -25,26 +25,23 @@ import (
"k8s.io/kubernetes/pkg/api"
apierrors "k8s.io/kubernetes/pkg/api/errors"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
)
func init() {
admission.RegisterPlugin("LimitPodHardAntiAffinityTopology", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewInterPodAntiAffinity(client), nil
admission.RegisterPlugin("LimitPodHardAntiAffinityTopology", func(config io.Reader) (admission.Interface, error) {
return NewInterPodAntiAffinity(), nil
})
}
// plugin contains the client used by the admission controller
type plugin struct {
*admission.Handler
client clientset.Interface
}
// NewInterPodAntiAffinity creates a new instance of the LimitPodHardAntiAffinityTopology admission controller
func NewInterPodAntiAffinity(client clientset.Interface) admission.Interface {
func NewInterPodAntiAffinity() admission.Interface {
return &plugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
client: client,
}
}

View File

@ -27,7 +27,7 @@ import (
// ensures the hard PodAntiAffinity is denied if it defines TopologyKey other than kubernetes.io/hostname.
func TestInterPodAffinityAdmission(t *testing.T) {
handler := NewInterPodAntiAffinity(nil)
handler := NewInterPodAntiAffinity()
pod := api.Pod{
Spec: api.PodSpec{},
}
@ -226,7 +226,7 @@ func TestInterPodAffinityAdmission(t *testing.T) {
}
}
func TestHandles(t *testing.T) {
handler := NewInterPodAntiAffinity(nil)
handler := NewInterPodAntiAffinity()
tests := map[admission.Operation]bool{
admission.Update: true,
admission.Create: true,

View File

@ -12,10 +12,7 @@ go_library(
name = "go_default_library",
srcs = ["admission.go"],
tags = ["automanaged"],
deps = [
"//pkg/admission:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
],
deps = ["//pkg/admission:go_default_library"],
)
go_test(

View File

@ -20,13 +20,11 @@ import (
"errors"
"io"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
)
func init() {
admission.RegisterPlugin("AlwaysDeny", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("AlwaysDeny", func(config io.Reader) (admission.Interface, error) {
return NewAlwaysDeny(), nil
})
}

View File

@ -19,6 +19,7 @@ go_library(
"//pkg/api/rest:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
],
)

View File

@ -20,24 +20,24 @@ import (
"fmt"
"io"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
func init() {
admission.RegisterPlugin("DenyEscalatingExec", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewDenyEscalatingExec(client), nil
admission.RegisterPlugin("DenyEscalatingExec", func(config io.Reader) (admission.Interface, error) {
return NewDenyEscalatingExec(), nil
})
// This is for legacy support of the DenyExecOnPrivileged admission controller. Most
// of the time DenyEscalatingExec should be preferred.
admission.RegisterPlugin("DenyExecOnPrivileged", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewDenyExecOnPrivileged(client), nil
admission.RegisterPlugin("DenyExecOnPrivileged", func(config io.Reader) (admission.Interface, error) {
return NewDenyExecOnPrivileged(), nil
})
}
@ -45,7 +45,7 @@ func init() {
// a pod using host based configurations.
type denyExec struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
// these flags control which items will be checked to deny exec/attach
hostIPC bool
@ -53,12 +53,13 @@ type denyExec struct {
privileged bool
}
var _ = kubeapiserveradmission.WantsInternalClientSet(&denyExec{})
// NewDenyEscalatingExec creates a new admission controller that denies an exec operation on a pod
// using host based configurations.
func NewDenyEscalatingExec(client clientset.Interface) admission.Interface {
func NewDenyEscalatingExec() admission.Interface {
return &denyExec{
Handler: admission.NewHandler(admission.Connect),
client: client,
hostIPC: true,
hostPID: true,
privileged: true,
@ -68,10 +69,9 @@ func NewDenyEscalatingExec(client clientset.Interface) admission.Interface {
// NewDenyExecOnPrivileged creates a new admission controller that is only checking the privileged
// option. This is for legacy support of the DenyExecOnPrivileged admission controller. Most
// of the time NewDenyEscalatingExec should be preferred.
func NewDenyExecOnPrivileged(client clientset.Interface) admission.Interface {
func NewDenyExecOnPrivileged() admission.Interface {
return &denyExec{
Handler: admission.NewHandler(admission.Connect),
client: client,
hostIPC: false,
hostPID: false,
privileged: true,
@ -127,3 +127,14 @@ func isPrivileged(pod *api.Pod) bool {
}
return false
}
func (d *denyExec) SetInternalClientSet(client internalclientset.Interface) {
d.client = client
}
func (d *denyExec) Validate() error {
if d.client == nil {
return fmt.Errorf("missing client")
}
return nil
}

View File

@ -17,7 +17,6 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/auth/authorizer:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/runtime:go_default_library",
],
)

View File

@ -24,12 +24,11 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/auth/authorizer"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/runtime"
)
func init() {
admission.RegisterPlugin("OwnerReferencesPermissionEnforcement", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("OwnerReferencesPermissionEnforcement", func(config io.Reader) (admission.Interface, error) {
return &gcPermissionsEnforcement{
Handler: admission.NewHandler(admission.Create, admission.Update),
}, nil

View File

@ -21,7 +21,6 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/apis/imagepolicy/v1alpha1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/cache:go_default_library",
@ -46,7 +45,6 @@ go_test(
"//pkg/apis/imagepolicy/install:go_default_library",
"//pkg/apis/imagepolicy/v1alpha1:go_default_library",
"//pkg/auth/user:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/unversioned/clientcmd/api/v1:go_default_library",
],
)

View File

@ -31,7 +31,6 @@ import (
"k8s.io/kubernetes/pkg/api"
apierrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/yaml"
@ -47,8 +46,8 @@ var (
)
func init() {
admission.RegisterPlugin("ImagePolicyWebhook", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
newImagePolicyWebhook, err := NewImagePolicyWebhook(client, config)
admission.RegisterPlugin("ImagePolicyWebhook", func(config io.Reader) (admission.Interface, error) {
newImagePolicyWebhook, err := NewImagePolicyWebhook(config)
if err != nil {
return nil, err
}
@ -211,7 +210,7 @@ func (a *imagePolicyWebhook) admitPod(attributes admission.Attributes, review *v
//
// For additional HTTP configuration, refer to the kubeconfig documentation
// http://kubernetes.io/v1.1/docs/user-guide/kubeconfig-file.html.
func NewImagePolicyWebhook(client clientset.Interface, configFile io.Reader) (admission.Interface, error) {
func NewImagePolicyWebhook(configFile io.Reader) (admission.Interface, error) {
var config AdmissionConfig
d := yaml.NewYAMLOrJSONDecoder(configFile, 4096)
err := d.Decode(&config)

View File

@ -32,7 +32,6 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1"
"fmt"
@ -244,7 +243,7 @@ current-context: default
}
defer configFile.Close()
_, err = NewImagePolicyWebhook(fake.NewSimpleClientset(), configFile)
_, err = NewImagePolicyWebhook(configFile)
return err
}()
if err != nil && !tt.wantErr {
@ -404,7 +403,7 @@ func newImagePolicyWebhook(callbackURL string, clientCert, clientKey, ca []byte,
return nil, fmt.Errorf("failed to read test config: %v", err)
}
defer configFile.Close()
wh, err := NewImagePolicyWebhook(fake.NewSimpleClientset(), configFile)
wh, err := NewImagePolicyWebhook(configFile)
return wh.(*imagePolicyWebhook), err
}

View File

@ -23,7 +23,6 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//vendor:cloud.google.com/go/compute/metadata",

View File

@ -24,8 +24,6 @@ import (
"strings"
"time"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
@ -48,7 +46,7 @@ const (
// WARNING: this feature is experimental and will definitely change.
func init() {
admission.RegisterPlugin("InitialResources", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("InitialResources", func(config io.Reader) (admission.Interface, error) {
s, err := newDataSource(*source)
if err != nil {
return nil, err

View File

@ -23,6 +23,7 @@ go_library(
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/listers/core/internalversion:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/errors:go_default_library",
@ -44,6 +45,7 @@ go_test(
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/testing/core:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/wait:go_default_library",
],

View File

@ -25,14 +25,14 @@ import (
lru "github.com/hashicorp/golang-lru"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
coreinternallisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
coreinternallisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
"k8s.io/kubernetes/pkg/controller/informers"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
@ -43,15 +43,15 @@ const (
)
func init() {
admission.RegisterPlugin("LimitRanger", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewLimitRanger(client, &DefaultLimitRangerActions{})
admission.RegisterPlugin("LimitRanger", func(config io.Reader) (admission.Interface, error) {
return NewLimitRanger(&DefaultLimitRangerActions{})
})
}
// limitRanger enforces usage limits on a per resource basis in the namespace
type limitRanger struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
actions LimitRangerActions
lister coreinternallisters.LimitRangeLister
@ -77,6 +77,9 @@ func (l *limitRanger) Validate() error {
if l.lister == nil {
return fmt.Errorf("missing limitRange lister")
}
if l.client == nil {
return fmt.Errorf("missing client")
}
return nil
}
@ -145,7 +148,7 @@ func (l *limitRanger) Admit(a admission.Attributes) (err error) {
}
// NewLimitRanger returns an object that enforces limits based on the supplied limit function
func NewLimitRanger(client clientset.Interface, actions LimitRangerActions) (admission.Interface, error) {
func NewLimitRanger(actions LimitRangerActions) (admission.Interface, error) {
liveLookupCache, err := lru.New(10000)
if err != nil {
return nil, err
@ -157,13 +160,18 @@ func NewLimitRanger(client clientset.Interface, actions LimitRangerActions) (adm
return &limitRanger{
Handler: admission.NewHandler(admission.Create, admission.Update),
client: client,
actions: actions,
liveLookupCache: liveLookupCache,
liveTTL: time.Duration(30 * time.Second),
}, nil
}
var _ = kubeapiserveradmission.WantsInternalClientSet(&limitRanger{})
func (a *limitRanger) SetInternalClientSet(client internalclientset.Interface) {
a.client = client
}
// defaultContainerResourceRequirements returns the default requirements for a container
// the requirement.Limits are taken from the LimitRange defaults (if specified)
// the requirement.Requests are taken from the LimitRange default request (if specified)

View File

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/controller/informers"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/wait"
)
@ -589,14 +590,13 @@ func newMockClientForTest(limitRanges []api.LimitRange) *fake.Clientset {
// newHandlerForTest returns a handler configured for testing.
func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(nil, c, 5*time.Minute)
handler, err := NewLimitRanger(c, &DefaultLimitRangerActions{})
handler, err := NewLimitRanger(&DefaultLimitRangerActions{})
if err != nil {
return nil, f, err
}
plugins := []admission.Interface{handler}
pluginInitializer := admission.NewPluginInitializer(f, nil)
pluginInitializer.Initialize(plugins)
err = admission.Validate(plugins)
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil)
pluginInitializer.Initialize(handler)
err = admission.Validate(handler)
return handler, f, err
}

View File

@ -19,6 +19,7 @@ go_library(
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
],
)
@ -36,6 +37,7 @@ go_test(
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/testing/core:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/wait:go_default_library",
],

View File

@ -17,22 +17,21 @@ limitations under the License.
package autoprovision
import (
"io"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"fmt"
"io"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller/informers"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
func init() {
admission.RegisterPlugin("NamespaceAutoProvision", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewProvision(client), nil
admission.RegisterPlugin("NamespaceAutoProvision", func(config io.Reader) (admission.Interface, error) {
return NewProvision(), nil
})
}
@ -41,11 +40,12 @@ func init() {
// It is useful in deployments that do not want to restrict creation of a namespace prior to its usage.
type provision struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
namespaceInformer cache.SharedIndexInformer
}
var _ = admission.WantsInformerFactory(&provision{})
var _ = kubeapiserveradmission.WantsInformerFactory(&provision{})
var _ = kubeapiserveradmission.WantsInformerFactory(&provision{})
func (p *provision) Admit(a admission.Attributes) (err error) {
// if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do
@ -80,13 +80,16 @@ func (p *provision) Admit(a admission.Attributes) (err error) {
}
// NewProvision creates a new namespace provision admission control handler
func NewProvision(c clientset.Interface) admission.Interface {
func NewProvision() admission.Interface {
return &provision{
Handler: admission.NewHandler(admission.Create),
client: c,
}
}
func (p *provision) SetInternalClientSet(client internalclientset.Interface) {
p.client = client
}
func (p *provision) SetInformerFactory(f informers.SharedInformerFactory) {
p.namespaceInformer = f.InternalNamespaces().Informer()
p.SetReadyFunc(p.namespaceInformer.HasSynced)
@ -96,5 +99,8 @@ func (p *provision) Validate() error {
if p.namespaceInformer == nil {
return fmt.Errorf("missing namespaceInformer")
}
if p.client == nil {
return fmt.Errorf("missing client")
}
return nil
}

View File

@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/controller/informers"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/wait"
)
@ -36,11 +37,10 @@ import (
// newHandlerForTest returns the admission controller configured for testing.
func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(nil, c, 5*time.Minute)
handler := NewProvision(c)
plugins := []admission.Interface{handler}
pluginInitializer := admission.NewPluginInitializer(f, nil)
pluginInitializer.Initialize(plugins)
err := admission.Validate(plugins)
handler := NewProvision()
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil)
pluginInitializer.Initialize(handler)
err := admission.Validate(handler)
return handler, f, err
}

View File

@ -20,6 +20,7 @@ go_library(
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
],
)
@ -36,6 +37,7 @@ go_test(
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/testing/core:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/wait:go_default_library",
],

View File

@ -17,23 +17,22 @@ limitations under the License.
package exists
import (
"io"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"fmt"
"io"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller/informers"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
func init() {
admission.RegisterPlugin("NamespaceExists", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewExists(client), nil
admission.RegisterPlugin("NamespaceExists", func(config io.Reader) (admission.Interface, error) {
return NewExists(), nil
})
}
@ -42,11 +41,12 @@ func init() {
// It is useful in deployments that want to enforce pre-declaration of a Namespace resource.
type exists struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
namespaceInformer cache.SharedIndexInformer
}
var _ = admission.WantsInformerFactory(&exists{})
var _ = kubeapiserveradmission.WantsInformerFactory(&exists{})
var _ = kubeapiserveradmission.WantsInternalClientSet(&exists{})
func (e *exists) Admit(a admission.Attributes) (err error) {
// if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do
@ -88,13 +88,16 @@ func (e *exists) Admit(a admission.Attributes) (err error) {
}
// NewExists creates a new namespace exists admission control handler
func NewExists(c clientset.Interface) admission.Interface {
func NewExists() admission.Interface {
return &exists{
client: c,
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
}
}
func (e *exists) SetInternalClientSet(client internalclientset.Interface) {
e.client = client
}
func (e *exists) SetInformerFactory(f informers.SharedInformerFactory) {
e.namespaceInformer = f.InternalNamespaces().Informer()
e.SetReadyFunc(e.namespaceInformer.HasSynced)
@ -104,5 +107,8 @@ func (e *exists) Validate() error {
if e.namespaceInformer == nil {
return fmt.Errorf("missing namespaceInformer")
}
if e.client == nil {
return fmt.Errorf("missing client")
}
return nil
}

View File

@ -28,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/controller/informers"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/wait"
)
@ -35,11 +36,10 @@ import (
// newHandlerForTest returns the admission controller configured for testing.
func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(nil, c, 5*time.Minute)
handler := NewExists(c)
plugins := []admission.Interface{handler}
pluginInitializer := admission.NewPluginInitializer(f, nil)
pluginInitializer.Initialize(plugins)
err := admission.Validate(plugins)
handler := NewExists()
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil)
pluginInitializer.Initialize(handler)
err := admission.Validate(handler)
return handler, f, err
}

View File

@ -20,6 +20,7 @@ go_library(
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/util/cache:go_default_library",
"//pkg/util/clock:go_default_library",
"//pkg/util/sets:go_default_library",
@ -40,6 +41,7 @@ go_test(
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/testing/core:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/clock:go_default_library",
"//pkg/util/sets:go_default_library",

View File

@ -23,14 +23,14 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller/informers"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
utilcache "k8s.io/kubernetes/pkg/util/cache"
"k8s.io/kubernetes/pkg/util/clock"
"k8s.io/kubernetes/pkg/util/sets"
@ -50,8 +50,8 @@ const (
)
func init() {
admission.RegisterPlugin(PluginName, func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewLifecycle(client, sets.NewString(api.NamespaceDefault, api.NamespaceSystem))
admission.RegisterPlugin(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewLifecycle(sets.NewString(api.NamespaceDefault, api.NamespaceSystem))
})
}
@ -59,7 +59,7 @@ func init() {
// It enforces life-cycle constraints around a Namespace depending on its Phase
type lifecycle struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
immortalNamespaces sets.String
namespaceInformer cache.SharedIndexInformer
// forceLiveLookupCache holds a list of entries for namespaces that we have a strong reason to believe are stale in our local cache.
@ -71,7 +71,8 @@ type forceLiveLookupEntry struct {
expiry time.Time
}
var _ = admission.WantsInformerFactory(&lifecycle{})
var _ = kubeapiserveradmission.WantsInformerFactory(&lifecycle{})
var _ = kubeapiserveradmission.WantsInternalClientSet(&lifecycle{})
func makeNamespaceKey(namespace string) *api.Namespace {
return &api.Namespace{
@ -167,15 +168,14 @@ func (l *lifecycle) Admit(a admission.Attributes) error {
}
// NewLifecycle creates a new namespace lifecycle admission control handler
func NewLifecycle(c clientset.Interface, immortalNamespaces sets.String) (admission.Interface, error) {
return newLifecycleWithClock(c, immortalNamespaces, clock.RealClock{})
func NewLifecycle(immortalNamespaces sets.String) (admission.Interface, error) {
return newLifecycleWithClock(immortalNamespaces, clock.RealClock{})
}
func newLifecycleWithClock(c clientset.Interface, immortalNamespaces sets.String, clock utilcache.Clock) (admission.Interface, error) {
func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock) (admission.Interface, error) {
forceLiveLookupCache := utilcache.NewLRUExpireCacheWithClock(100, clock)
return &lifecycle{
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
client: c,
immortalNamespaces: immortalNamespaces,
forceLiveLookupCache: forceLiveLookupCache,
}, nil
@ -186,9 +186,16 @@ func (l *lifecycle) SetInformerFactory(f informers.SharedInformerFactory) {
l.SetReadyFunc(l.namespaceInformer.HasSynced)
}
func (l *lifecycle) SetInternalClientSet(client internalclientset.Interface) {
l.client = client
}
func (l *lifecycle) Validate() error {
if l.namespaceInformer == nil {
return fmt.Errorf("missing namespaceInformer")
}
if l.client == nil {
return fmt.Errorf("missing client")
}
return nil
}

View File

@ -28,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/controller/informers"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/clock"
"k8s.io/kubernetes/pkg/util/sets"
@ -42,14 +43,13 @@ func newHandlerForTest(c clientset.Interface) (admission.Interface, informers.Sh
// newHandlerForTestWithClock returns a configured handler for testing.
func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (admission.Interface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(nil, c, 5*time.Minute)
handler, err := newLifecycleWithClock(c, sets.NewString(api.NamespaceDefault, api.NamespaceSystem), cacheClock)
handler, err := newLifecycleWithClock(sets.NewString(api.NamespaceDefault, api.NamespaceSystem), cacheClock)
if err != nil {
return nil, f, err
}
plugins := []admission.Interface{handler}
pluginInitializer := admission.NewPluginInitializer(f, nil)
pluginInitializer.Initialize(plugins)
err = admission.Validate(plugins)
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil)
pluginInitializer.Initialize(handler)
err = admission.Validate(handler)
return handler, f, err
}

View File

@ -19,7 +19,6 @@ go_library(
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/cloudprovider:go_default_library",
"//pkg/cloudprovider/providers/aws:go_default_library",
"//pkg/cloudprovider/providers/gce:go_default_library",

View File

@ -21,8 +21,6 @@ import (
"io"
"sync"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
@ -33,7 +31,7 @@ import (
)
func init() {
admission.RegisterPlugin("PersistentVolumeLabel", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("PersistentVolumeLabel", func(config io.Reader) (admission.Interface, error) {
persistentVolumeLabelAdmission := NewPersistentVolumeLabel()
return persistentVolumeLabelAdmission, nil
})

View File

@ -20,6 +20,7 @@ go_library(
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/util/yaml:go_default_library",
"//vendor:github.com/golang/glog",
@ -37,6 +38,7 @@ go_test(
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/util/wait:go_default_library",
],

View File

@ -28,8 +28,9 @@ import (
"k8s.io/kubernetes/pkg/api/errors"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller/informers"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util/yaml"
)
@ -39,9 +40,9 @@ import (
var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"}
func init() {
admission.RegisterPlugin("PodNodeSelector", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
admission.RegisterPlugin("PodNodeSelector", func(config io.Reader) (admission.Interface, error) {
pluginConfig := readConfig(config)
plugin := NewPodNodeSelector(client, pluginConfig.PodNodeSelectorPluginConfig)
plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig)
return plugin, nil
})
}
@ -49,12 +50,14 @@ func init() {
// podNodeSelector is an implementation of admission.Interface.
type podNodeSelector struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
namespaceInformer cache.SharedIndexInformer
// global default node selector and namespace whitelists in a cluster.
clusterNodeSelectors map[string]string
}
var _ = kubeapiserveradmission.WantsInternalClientSet(&podNodeSelector{})
type pluginConfig struct {
PodNodeSelectorPluginConfig map[string]string
}
@ -157,14 +160,17 @@ func (p *podNodeSelector) Admit(a admission.Attributes) error {
return nil
}
func NewPodNodeSelector(client clientset.Interface, clusterNodeSelectors map[string]string) *podNodeSelector {
func NewPodNodeSelector(clusterNodeSelectors map[string]string) *podNodeSelector {
return &podNodeSelector{
Handler: admission.NewHandler(admission.Create),
client: client,
clusterNodeSelectors: clusterNodeSelectors,
}
}
func (a *podNodeSelector) SetInternalClientSet(client internalclientset.Interface) {
a.client = client
}
func (p *podNodeSelector) SetInformerFactory(f informers.SharedInformerFactory) {
p.namespaceInformer = f.InternalNamespaces().Informer()
p.SetReadyFunc(p.namespaceInformer.HasSynced)
@ -174,6 +180,9 @@ func (p *podNodeSelector) Validate() error {
if p.namespaceInformer == nil {
return fmt.Errorf("missing namespaceInformer")
}
if p.client == nil {
return fmt.Errorf("missing client")
}
return nil
}

View File

@ -25,6 +25,7 @@ import (
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/controller/informers"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util/wait"
)
@ -169,7 +170,7 @@ func TestHandles(t *testing.T) {
admission.Connect: false,
admission.Delete: false,
} {
nodeEnvionment := NewPodNodeSelector(nil, nil)
nodeEnvionment := NewPodNodeSelector(nil)
if e, a := shouldHandle, nodeEnvionment.Handles(op); e != a {
t.Errorf("%v: shouldHandle=%t, handles=%t", op, e, a)
}
@ -179,10 +180,9 @@ func TestHandles(t *testing.T) {
// newHandlerForTest returns the admission controller configured for testing.
func newHandlerForTest(c clientset.Interface) (*podNodeSelector, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(nil, c, 5*time.Minute)
handler := NewPodNodeSelector(c, nil)
plugins := []admission.Interface{handler}
pluginInitializer := admission.NewPluginInitializer(f, nil)
pluginInitializer.Initialize(plugins)
err := admission.Validate(plugins)
handler := NewPodNodeSelector(nil)
pluginInitializer := kubeadmission.NewPluginInitializer(c, f, nil)
pluginInitializer.Initialize(handler)
err := admission.Validate(handler)
return handler, f, err
}

View File

@ -24,6 +24,7 @@ go_library(
"//pkg/api/v1:go_default_library",
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/quota:go_default_library",
"//pkg/quota/install:go_default_library",
"//pkg/runtime:go_default_library",

View File

@ -17,24 +17,25 @@ limitations under the License.
package resourcequota
import (
"fmt"
"io"
"time"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/install"
)
func init() {
admission.RegisterPlugin("ResourceQuota",
func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
func(config io.Reader) (admission.Interface, error) {
// NOTE: we do not provide informers to the registry because admission level decisions
// does not require us to open watches for all items tracked by quota.
registry := install.NewRegistry(nil, nil)
return NewResourceQuota(client, registry, 5, make(chan struct{}))
return NewResourceQuota(registry, 5, make(chan struct{}))
})
}
@ -42,9 +43,14 @@ func init() {
type quotaAdmission struct {
*admission.Handler
evaluator Evaluator
stopCh <-chan struct{}
registry quota.Registry
numEvaluators int
evaluator Evaluator
}
var _ = kubeapiserveradmission.WantsInternalClientSet(&quotaAdmission{})
type liveLookupEntry struct {
expiry time.Time
items []*api.ResourceQuota
@ -53,19 +59,33 @@ type liveLookupEntry struct {
// NewResourceQuota configures an admission controller that can enforce quota constraints
// using the provided registry. The registry must have the capability to handle group/kinds that
// are persisted by the server this admission controller is intercepting
func NewResourceQuota(client clientset.Interface, registry quota.Registry, numEvaluators int, stopCh <-chan struct{}) (admission.Interface, error) {
func NewResourceQuota(registry quota.Registry, numEvaluators int, stopCh <-chan struct{}) (admission.Interface, error) {
return &quotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
stopCh: stopCh,
registry: registry,
numEvaluators: numEvaluators,
}, nil
}
func (a *quotaAdmission) SetInternalClientSet(client internalclientset.Interface) {
var err error
quotaAccessor, err := newQuotaAccessor(client)
if err != nil {
return nil, err
// TODO handle errors more cleanly
panic(err)
}
go quotaAccessor.Run(stopCh)
go quotaAccessor.Run(a.stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, registry, nil, numEvaluators, stopCh)
a.evaluator = NewQuotaEvaluator(quotaAccessor, a.registry, nil, a.numEvaluators, a.stopCh)
}
return &quotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
evaluator: evaluator,
}, nil
// Validate ensures an authorizer is set.
func (a *quotaAdmission) Validate() error {
if a.evaluator == nil {
return fmt.Errorf("missing evaluator")
}
return nil
}
// Admit makes admission decisions while enforcing quota

View File

@ -122,14 +122,21 @@ func TestPrettyPrint(t *testing.T) {
// TestAdmissionIgnoresDelete verifies that the admission controller ignores delete operations
func TestAdmissionIgnoresDelete(t *testing.T) {
kubeClient := fake.NewSimpleClientset()
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc})
stopCh := make(chan struct{})
defer close(stopCh)
handler, err := NewResourceQuota(kubeClient, install.NewRegistry(nil, nil), 5, stopCh)
if err != nil {
t.Errorf("Unexpected error %v", err)
quotaAccessor, _ := newQuotaAccessor(kubeClient)
quotaAccessor.indexer = indexer
go quotaAccessor.Run(stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, install.NewRegistry(nil, nil), nil, 5, stopCh)
handler := &quotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
evaluator: evaluator,
}
namespace := "default"
err = handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), namespace, "name", api.Resource("pods").WithVersion("version"), "", admission.Delete, nil))
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), namespace, "name", api.Resource("pods").WithVersion("version"), "", admission.Delete, nil))
if err != nil {
t.Errorf("ResourceQuota should admit all deletes: %v", err)
}

View File

@ -22,6 +22,7 @@ go_library(
"//pkg/auth/user:go_default_library",
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/security/podsecuritypolicy:go_default_library",
"//pkg/security/podsecuritypolicy/util:go_default_library",
@ -46,8 +47,6 @@ go_test(
"//pkg/auth/authorizer:go_default_library",
"//pkg/auth/user:go_default_library",
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/security/apparmor:go_default_library",
"//pkg/security/podsecuritypolicy:go_default_library",
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",

View File

@ -31,7 +31,8 @@ import (
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/runtime"
psp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
@ -47,9 +48,8 @@ const (
)
func init() {
admission.RegisterPlugin(PluginName, func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
plugin := NewPlugin(client, psp.NewSimpleStrategyFactory(), getMatchingPolicies, true)
plugin.Run()
admission.RegisterPlugin(PluginName, func(config io.Reader) (admission.Interface, error) {
plugin := NewPlugin(psp.NewSimpleStrategyFactory(), getMatchingPolicies, true)
return plugin, nil
})
}
@ -60,7 +60,6 @@ type PSPMatchFn func(store cache.Store, user user.Info, sa user.Info, authz auth
// podSecurityPolicyPlugin holds state for and implements the admission plugin.
type podSecurityPolicyPlugin struct {
*admission.Handler
client clientset.Interface
strategyFactory psp.StrategyFactory
pspMatcher PSPMatchFn
failOnNoPolicies bool
@ -81,43 +80,50 @@ func (plugin *podSecurityPolicyPlugin) Validate() error {
if plugin.authz == nil {
return fmt.Errorf("%s requires an authorizer", PluginName)
}
if plugin.store == nil {
return fmt.Errorf("%s requires an client", PluginName)
}
if plugin.store == nil {
return fmt.Errorf("%s requires an client", PluginName)
}
return nil
}
var _ admission.Interface = &podSecurityPolicyPlugin{}
var _ admission.WantsAuthorizer = &podSecurityPolicyPlugin{}
var _ kubeapiserveradmission.WantsAuthorizer = &podSecurityPolicyPlugin{}
var _ kubeapiserveradmission.WantsInternalClientSet = &podSecurityPolicyPlugin{}
// NewPlugin creates a new PSP admission plugin.
func NewPlugin(kclient clientset.Interface, strategyFactory psp.StrategyFactory, pspMatcher PSPMatchFn, failOnNoPolicies bool) *podSecurityPolicyPlugin {
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
reflector := cache.NewReflector(
func NewPlugin(strategyFactory psp.StrategyFactory, pspMatcher PSPMatchFn, failOnNoPolicies bool) *podSecurityPolicyPlugin {
return &podSecurityPolicyPlugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
strategyFactory: strategyFactory,
pspMatcher: pspMatcher,
failOnNoPolicies: failOnNoPolicies,
}
}
func (a *podSecurityPolicyPlugin) SetInternalClientSet(client internalclientset.Interface) {
a.store = cache.NewStore(cache.MetaNamespaceKeyFunc)
a.reflector = cache.NewReflector(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
internalOptions := api.ListOptions{}
v1.Convert_v1_ListOptions_To_api_ListOptions(&options, &internalOptions, nil)
return kclient.Extensions().PodSecurityPolicies().List(internalOptions)
return client.Extensions().PodSecurityPolicies().List(internalOptions)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
internalOptions := api.ListOptions{}
v1.Convert_v1_ListOptions_To_api_ListOptions(&options, &internalOptions, nil)
return kclient.Extensions().PodSecurityPolicies().Watch(internalOptions)
return client.Extensions().PodSecurityPolicies().Watch(internalOptions)
},
},
&extensions.PodSecurityPolicy{},
store,
a.store,
0,
)
return &podSecurityPolicyPlugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
client: kclient,
strategyFactory: strategyFactory,
pspMatcher: pspMatcher,
failOnNoPolicies: failOnNoPolicies,
store: store,
reflector: reflector,
}
a.Run()
}
func (a *podSecurityPolicyPlugin) Run() {

View File

@ -30,8 +30,6 @@ import (
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientsetfake "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/security/apparmor"
kpsp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
@ -44,10 +42,9 @@ const defaultContainerName = "test-c"
// NewTestAdmission provides an admission plugin with test implementations of internal structs. It uses
// an authorizer that always returns true.
func NewTestAdmission(store cache.Store, kclient clientset.Interface) kadmission.Interface {
func NewTestAdmission(store cache.Store) kadmission.Interface {
return &podSecurityPolicyPlugin{
Handler: kadmission.NewHandler(kadmission.Create),
client: kclient,
store: store,
strategyFactory: kpsp.NewSimpleStrategyFactory(),
pspMatcher: getMatchingPolicies,
@ -1339,16 +1336,13 @@ func TestAdmitSysctls(t *testing.T) {
}
func testPSPAdmit(testCaseName string, psps []*extensions.PodSecurityPolicy, pod *kapi.Pod, shouldPass bool, expectedPSP string, t *testing.T) {
namespace := createNamespaceForTest()
serviceAccount := createSAForTest()
tc := clientsetfake.NewSimpleClientset(namespace, serviceAccount)
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
for _, psp := range psps {
store.Add(psp)
}
plugin := NewTestAdmission(store, tc)
plugin := NewTestAdmission(store)
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), "namespace", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{})
err := plugin.Admit(attrs)
@ -1512,10 +1506,8 @@ func TestCreateProvidersFromConstraints(t *testing.T) {
for k, v := range testCases {
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
tc := clientsetfake.NewSimpleClientset()
admit := &podSecurityPolicyPlugin{
Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update),
client: tc,
store: store,
strategyFactory: kpsp.NewSimpleStrategyFactory(),
}

View File

@ -16,7 +16,6 @@ go_library(
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
],
)

View File

@ -20,30 +20,25 @@ import (
"fmt"
"io"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
apierrors "k8s.io/kubernetes/pkg/api/errors"
)
func init() {
admission.RegisterPlugin("SecurityContextDeny", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
return NewSecurityContextDeny(client), nil
admission.RegisterPlugin("SecurityContextDeny", func(config io.Reader) (admission.Interface, error) {
return NewSecurityContextDeny(), nil
})
}
// plugin contains the client used by the SecurityContextDeny admission controller
type plugin struct {
*admission.Handler
client clientset.Interface
}
// NewSecurityContextDeny creates a new instance of the SecurityContextDeny admission controller
func NewSecurityContextDeny(client clientset.Interface) admission.Interface {
func NewSecurityContextDeny() admission.Interface {
return &plugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
client: client,
}
}

View File

@ -25,7 +25,7 @@ import (
// ensures the SecurityContext is denied if it defines anything more than Caps or Privileged
func TestAdmission(t *testing.T) {
handler := NewSecurityContextDeny(nil)
handler := NewSecurityContextDeny()
var runAsUser int64 = 1
priv := true
@ -106,7 +106,7 @@ func TestAdmission(t *testing.T) {
}
func TestPodSecurityContextAdmission(t *testing.T) {
handler := NewSecurityContextDeny(nil)
handler := NewSecurityContextDeny()
pod := api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
@ -153,7 +153,7 @@ func TestPodSecurityContextAdmission(t *testing.T) {
}
func TestHandles(t *testing.T) {
handler := NewSecurityContextDeny(nil)
handler := NewSecurityContextDeny()
tests := map[admission.Operation]bool{
admission.Update: true,
admission.Create: true,

View File

@ -24,6 +24,7 @@ go_library(
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",

View File

@ -23,18 +23,18 @@ import (
"strconv"
"time"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/fields"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
kubelet "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/watch"
@ -55,9 +55,8 @@ const DefaultAPITokenMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
const PluginName = "ServiceAccount"
func init() {
admission.RegisterPlugin(PluginName, func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
serviceAccountAdmission := NewServiceAccount(client)
serviceAccountAdmission.Run()
admission.RegisterPlugin(PluginName, func(config io.Reader) (admission.Interface, error) {
serviceAccountAdmission := NewServiceAccount()
return serviceAccountAdmission, nil
})
}
@ -74,7 +73,7 @@ type serviceAccount struct {
// MountServiceAccountToken creates Volume and VolumeMounts for the first referenced ServiceAccountToken for the pod's service account
MountServiceAccountToken bool
client clientset.Interface
client internalclientset.Interface
serviceAccounts cache.Indexer
secrets cache.Indexer
@ -84,14 +83,29 @@ type serviceAccount struct {
secretsReflector *cache.Reflector
}
var _ = kubeapiserveradmission.WantsInternalClientSet(&serviceAccount{})
// NewServiceAccount returns an admission.Interface implementation which limits admission of Pod CREATE requests based on the pod's ServiceAccount:
// 1. If the pod does not specify a ServiceAccount, it sets the pod's ServiceAccount to "default"
// 2. It ensures the ServiceAccount referenced by the pod exists
// 3. If LimitSecretReferences is true, it rejects the pod if the pod references Secret objects which the pod's ServiceAccount does not reference
// 4. If the pod does not contain any ImagePullSecrets, the ImagePullSecrets of the service account are added.
// 5. If MountServiceAccountToken is true, it adds a VolumeMount with the pod's ServiceAccount's api token secret to containers
func NewServiceAccount(cl clientset.Interface) *serviceAccount {
serviceAccountsIndexer, serviceAccountsReflector := cache.NewNamespaceKeyedIndexerAndReflector(
func NewServiceAccount() *serviceAccount {
return &serviceAccount{
Handler: admission.NewHandler(admission.Create),
// TODO: enable this once we've swept secret usage to account for adding secret references to service accounts
LimitSecretReferences: false,
// Auto mount service account API token secrets
MountServiceAccountToken: true,
// Reject pod creation until a service account token is available
RequireAPIToken: true,
}
}
func (a *serviceAccount) SetInternalClientSet(cl internalclientset.Interface) {
a.client = cl
a.serviceAccounts, a.serviceAccountsReflector = cache.NewNamespaceKeyedIndexerAndReflector(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
internalOptions := api.ListOptions{}
@ -109,7 +123,7 @@ func NewServiceAccount(cl clientset.Interface) *serviceAccount {
)
tokenSelector := fields.SelectorFromSet(map[string]string{api.SecretTypeField: string(api.SecretTypeServiceAccountToken)})
secretsIndexer, secretsReflector := cache.NewNamespaceKeyedIndexerAndReflector(
a.secrets, a.secretsReflector = cache.NewNamespaceKeyedIndexerAndReflector(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
internalOptions := api.ListOptions{}
@ -128,23 +142,31 @@ func NewServiceAccount(cl clientset.Interface) *serviceAccount {
0,
)
return &serviceAccount{
Handler: admission.NewHandler(admission.Create),
// TODO: enable this once we've swept secret usage to account for adding secret references to service accounts
LimitSecretReferences: false,
// Auto mount service account API token secrets
MountServiceAccountToken: true,
// Reject pod creation until a service account token is available
RequireAPIToken: true,
client: cl,
serviceAccounts: serviceAccountsIndexer,
serviceAccountsReflector: serviceAccountsReflector,
secrets: secretsIndexer,
secretsReflector: secretsReflector,
if cl != nil {
a.Run()
}
}
// Validate ensures an authorizer is set.
func (a *serviceAccount) Validate() error {
if a.client == nil {
return fmt.Errorf("missing client")
}
if a.secrets == nil {
return fmt.Errorf("missing secretsIndexer")
}
if a.secretsReflector == nil {
return fmt.Errorf("missing secretsReflector")
}
if a.serviceAccounts == nil {
return fmt.Errorf("missing serviceAccountsIndexer")
}
if a.serviceAccountsReflector == nil {
return fmt.Errorf("missing serviceAccountsReflector")
}
return nil
}
func (s *serviceAccount) Run() {
if s.stopChan == nil {
s.stopChan = make(chan struct{})

View File

@ -33,7 +33,7 @@ func TestIgnoresNonCreate(t *testing.T) {
pod := &api.Pod{}
for _, op := range []admission.Operation{admission.Update, admission.Delete, admission.Connect} {
attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", op, nil)
handler := admission.NewChainHandler(NewServiceAccount(nil))
handler := admission.NewChainHandler(NewServiceAccount())
err := handler.Admit(attrs)
if err != nil {
t.Errorf("Expected %s operation allowed, got err: %v", op, err)
@ -44,7 +44,7 @@ func TestIgnoresNonCreate(t *testing.T) {
func TestIgnoresNonPodResource(t *testing.T) {
pod := &api.Pod{}
attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("CustomResource").WithVersion("version"), "", admission.Create, nil)
err := NewServiceAccount(nil).Admit(attrs)
err := NewServiceAccount().Admit(attrs)
if err != nil {
t.Errorf("Expected non-pod resource allowed, got err: %v", err)
}
@ -52,7 +52,7 @@ func TestIgnoresNonPodResource(t *testing.T) {
func TestIgnoresNilObject(t *testing.T) {
attrs := admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
err := NewServiceAccount(nil).Admit(attrs)
err := NewServiceAccount().Admit(attrs)
if err != nil {
t.Errorf("Expected nil object allowed allowed, got err: %v", err)
}
@ -61,7 +61,7 @@ func TestIgnoresNilObject(t *testing.T) {
func TestIgnoresNonPodObject(t *testing.T) {
obj := &api.Namespace{}
attrs := admission.NewAttributesRecord(obj, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
err := NewServiceAccount(nil).Admit(attrs)
err := NewServiceAccount().Admit(attrs)
if err != nil {
t.Errorf("Expected non pod object allowed, got err: %v", err)
}
@ -81,7 +81,7 @@ func TestIgnoresMirrorPod(t *testing.T) {
},
}
attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
err := NewServiceAccount(nil).Admit(attrs)
err := NewServiceAccount().Admit(attrs)
if err != nil {
t.Errorf("Expected mirror pod without service account or secrets allowed, got err: %v", err)
}
@ -99,7 +99,7 @@ func TestRejectsMirrorPodWithServiceAccount(t *testing.T) {
},
}
attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
err := NewServiceAccount(nil).Admit(attrs)
err := NewServiceAccount().Admit(attrs)
if err == nil {
t.Errorf("Expected a mirror pod to be prevented from referencing a service account")
}
@ -119,7 +119,7 @@ func TestRejectsMirrorPodWithSecretVolumes(t *testing.T) {
},
}
attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
err := NewServiceAccount(nil).Admit(attrs)
err := NewServiceAccount().Admit(attrs)
if err == nil {
t.Errorf("Expected a mirror pod to be prevented from referencing a secret volume")
}
@ -128,7 +128,8 @@ func TestRejectsMirrorPodWithSecretVolumes(t *testing.T) {
func TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.MountServiceAccountToken = true
admit.RequireAPIToken = false
@ -154,7 +155,8 @@ func TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken(t *testing.T) {
func TestAssignsDefaultServiceAccountAndRejectsMissingAPIToken(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.MountServiceAccountToken = true
admit.RequireAPIToken = true
@ -185,7 +187,9 @@ func TestFetchesUncachedServiceAccount(t *testing.T) {
},
})
admit := NewServiceAccount(client)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.client = client
admit.RequireAPIToken = false
pod := &api.Pod{}
@ -205,7 +209,8 @@ func TestDeniesInvalidServiceAccount(t *testing.T) {
// Build a test client that the admission plugin can use to look up the service account missing from its cache
client := fake.NewSimpleClientset()
admit := NewServiceAccount(client)
admit := NewServiceAccount()
admit.SetInternalClientSet(client)
pod := &api.Pod{}
attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, nil)
@ -233,7 +238,8 @@ func TestAutomountsAPIToken(t *testing.T) {
MountPath: DefaultAPITokenMountPath,
}
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.MountServiceAccountToken = true
admit.RequireAPIToken = true
@ -332,7 +338,8 @@ func TestRespectsExistingMount(t *testing.T) {
MountPath: DefaultAPITokenMountPath,
}
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.MountServiceAccountToken = true
admit.RequireAPIToken = true
@ -428,7 +435,8 @@ func TestRespectsExistingMount(t *testing.T) {
func TestAllowsReferencedSecret(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = true
admit.RequireAPIToken = false
@ -507,7 +515,8 @@ func TestAllowsReferencedSecret(t *testing.T) {
func TestRejectsUnreferencedSecretVolumes(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = true
admit.RequireAPIToken = false
@ -583,7 +592,8 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) {
func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = false
admit.RequireAPIToken = false
@ -613,7 +623,8 @@ func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) {
func TestAllowsReferencedImagePullSecrets(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = true
admit.RequireAPIToken = false
@ -643,7 +654,8 @@ func TestAllowsReferencedImagePullSecrets(t *testing.T) {
func TestRejectsUnreferencedImagePullSecrets(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = true
admit.RequireAPIToken = false
@ -670,7 +682,8 @@ func TestRejectsUnreferencedImagePullSecrets(t *testing.T) {
func TestDoNotAddImagePullSecrets(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = true
admit.RequireAPIToken = false
@ -705,7 +718,8 @@ func TestDoNotAddImagePullSecrets(t *testing.T) {
func TestAddImagePullSecrets(t *testing.T) {
ns := "myns"
admit := NewServiceAccount(nil)
admit := NewServiceAccount()
admit.SetInternalClientSet(nil)
admit.LimitSecretReferences = true
admit.RequireAPIToken = false

View File

@ -21,6 +21,7 @@ go_library(
"//pkg/apis/storage/util:go_default_library",
"//pkg/client/cache:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/watch:go_default_library",
"//vendor:github.com/golang/glog",

View File

@ -29,7 +29,8 @@ import (
"k8s.io/kubernetes/pkg/apis/storage"
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/watch"
)
@ -39,9 +40,8 @@ const (
)
func init() {
admission.RegisterPlugin(PluginName, func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
plugin := newPlugin(client)
plugin.Run()
admission.RegisterPlugin(PluginName, func(config io.Reader) (admission.Interface, error) {
plugin := newPlugin()
return plugin, nil
})
}
@ -49,7 +49,7 @@ func init() {
// claimDefaulterPlugin holds state for and implements the admission plugin.
type claimDefaulterPlugin struct {
*admission.Handler
client clientset.Interface
client internalclientset.Interface
reflector *cache.Reflector
stopChan chan struct{}
@ -57,36 +57,55 @@ type claimDefaulterPlugin struct {
}
var _ admission.Interface = &claimDefaulterPlugin{}
var _ = kubeapiserveradmission.WantsInternalClientSet(&claimDefaulterPlugin{})
// newPlugin creates a new admission plugin.
func newPlugin(kclient clientset.Interface) *claimDefaulterPlugin {
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
reflector := cache.NewReflector(
func newPlugin() *claimDefaulterPlugin {
return &claimDefaulterPlugin{
Handler: admission.NewHandler(admission.Create),
}
}
func (a *claimDefaulterPlugin) SetInternalClientSet(client internalclientset.Interface) {
a.client = client
a.store = cache.NewStore(cache.MetaNamespaceKeyFunc)
a.reflector = cache.NewReflector(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
internalOptions := api.ListOptions{}
v1.Convert_v1_ListOptions_To_api_ListOptions(&options, &internalOptions, nil)
return kclient.Storage().StorageClasses().List(internalOptions)
return client.Storage().StorageClasses().List(internalOptions)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
internalOptions := api.ListOptions{}
v1.Convert_v1_ListOptions_To_api_ListOptions(&options, &internalOptions, nil)
return kclient.Storage().StorageClasses().Watch(internalOptions)
return client.Storage().StorageClasses().Watch(internalOptions)
},
},
&storage.StorageClass{},
store,
a.store,
0,
)
return &claimDefaulterPlugin{
Handler: admission.NewHandler(admission.Create),
client: kclient,
store: store,
reflector: reflector,
if client != nil {
a.Run()
}
}
// Validate ensures an authorizer is set.
func (a *claimDefaulterPlugin) Validate() error {
if a.client == nil {
return fmt.Errorf("missing client")
}
if a.reflector == nil {
return fmt.Errorf("missing reflector")
}
if a.store == nil {
return fmt.Errorf("missing store")
}
return nil
}
func (a *claimDefaulterPlugin) Run() {
if a.stopChan == nil {
a.stopChan = make(chan struct{})

View File

@ -192,7 +192,8 @@ func TestAdmission(t *testing.T) {
}
claim := clone.(*api.PersistentVolumeClaim)
ctrl := newPlugin(nil)
ctrl := newPlugin()
ctrl.SetInternalClientSet(nil)
for _, c := range test.classes {
ctrl.store.Add(c)
}

View File

@ -36,6 +36,7 @@ import (
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
"k8s.io/kubernetes/pkg/fields"
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/labels"
quotainstall "k8s.io/kubernetes/pkg/quota/install"
"k8s.io/kubernetes/pkg/runtime/schema"
@ -62,10 +63,11 @@ func TestQuota(t *testing.T) {
admissionCh := make(chan struct{})
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &registered.GroupOrDie(v1.GroupName).GroupVersion}})
internalClientset := internalclientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &registered.GroupOrDie(v1.GroupName).GroupVersion}})
admission, err := resourcequota.NewResourceQuota(internalClientset, quotainstall.NewRegistry(nil, nil), 5, admissionCh)
admission, err := resourcequota.NewResourceQuota(quotainstall.NewRegistry(nil, nil), 5, admissionCh)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
admission.(kubeadmission.WantsInternalClientSet).SetInternalClientSet(internalClientset)
defer close(admissionCh)
masterConfig := framework.NewIntegrationTestMasterConfig()

View File

@ -404,7 +404,8 @@ func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclie
})
// Set up admission plugin to auto-assign serviceaccounts to pods
serviceAccountAdmission := serviceaccountadmission.NewServiceAccount(internalRootClientset)
serviceAccountAdmission := serviceaccountadmission.NewServiceAccount()
serviceAccountAdmission.SetInternalClientSet(internalRootClientset)
masterConfig := framework.NewMasterConfig()
masterConfig.GenericConfig.EnableIndex = true