Merge pull request #69941 from miguelbernadi/fix-golint-issues-68026

Fix golint issues in plugin/pkg/admission
k3s-v1.15.3
Kubernetes Prow Robot 2019-05-30 08:38:26 -07:00 committed by GitHub
commit b8eecd671d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 297 additions and 226 deletions

View File

@ -320,20 +320,10 @@ pkg/volume/testing
pkg/volume/util/fs pkg/volume/util/fs
pkg/volume/util/volumepathhandler pkg/volume/util/volumepathhandler
pkg/volume/vsphere_volume pkg/volume/vsphere_volume
plugin/pkg/admission/antiaffinity
plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1 plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1
plugin/pkg/admission/limitranger plugin/pkg/admission/limitranger
plugin/pkg/admission/noderestriction
plugin/pkg/admission/podnodeselector
plugin/pkg/admission/podpreset
plugin/pkg/admission/podtolerationrestriction
plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1
plugin/pkg/admission/resourcequota
plugin/pkg/admission/resourcequota/apis/resourcequota
plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1 plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1
plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1 plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1
plugin/pkg/admission/security/podsecuritypolicy
plugin/pkg/admission/serviceaccount
plugin/pkg/auth/authorizer/node plugin/pkg/auth/authorizer/node
plugin/pkg/auth/authorizer/rbac plugin/pkg/auth/authorizer/rbac
plugin/pkg/auth/authorizer/rbac/bootstrappolicy plugin/pkg/auth/authorizer/rbac/bootstrappolicy

View File

@ -26,6 +26,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
// PluginName is a string with the name of the plugin
const PluginName = "LimitPodHardAntiAffinityTopology" const PluginName = "LimitPodHardAntiAffinityTopology"
// Register registers a plugin // Register registers a plugin

View File

@ -14,15 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// LimitPodHardAntiAffinityTopology admission controller rejects any pod // Package antiaffinity provides the LimitPodHardAntiAffinityTopology
// that specifies "hard" (RequiredDuringScheduling) anti-affinity // admission controller. It rejects any pod that specifies "hard"
// with a TopologyKey other than v1.LabelHostname. // (RequiredDuringScheduling) anti-affinity with a TopologyKey other
// Because anti-affinity is symmetric, without this admission controller, // than v1.LabelHostname. Because anti-affinity is symmetric, without
// a user could maliciously or accidentally specify that their pod (once it has scheduled) // this admission controller, a user could maliciously or accidentally
// should block other pods from scheduling into the same zone or some other large topology, // specify that their pod (once it has scheduled) should block other
// essentially DoSing the cluster. // pods from scheduling into the same zone or some other large
// In the future we will address this problem more fully by using quota and priority, // topology, essentially DoSing the cluster. In the future we will
// but for now this admission controller provides a simple protection, // address this problem more fully by using quota and priority, but
// on the assumption that the only legitimate use of hard pod anti-affinity // for now this admission controller provides a simple protection, on
// is to exclude other pods from the same node. // the assumption that the only legitimate use of hard pod
// anti-affinity is to exclude other pods from the same node.
package antiaffinity // import "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity" package antiaffinity // import "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity"

View File

@ -24,7 +24,7 @@ import (
var ( var (
// SchemeBuilder is the scheme builder with scheme init functions to run for this API package // SchemeBuilder is the scheme builder with scheme init functions to run for this API package
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is a global function that registers this API group & version to a scheme // AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = SchemeBuilder.AddToScheme AddToScheme = SchemeBuilder.AddToScheme
) )

View File

@ -30,8 +30,11 @@ var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha
var ( var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// SchemeBuilder is a pointer used to call AddToScheme
SchemeBuilder runtime.SchemeBuilder SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder localSchemeBuilder = &SchemeBuilder
// AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = localSchemeBuilder.AddToScheme AddToScheme = localSchemeBuilder.AddToScheme
) )

View File

@ -78,16 +78,19 @@ type liveLookupEntry struct {
items []*corev1.LimitRange items []*corev1.LimitRange
} }
// SetExternalKubeInformerFactory registers an informer factory into the LimitRanger
func (l *LimitRanger) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { func (l *LimitRanger) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
limitRangeInformer := f.Core().V1().LimitRanges() limitRangeInformer := f.Core().V1().LimitRanges()
l.SetReadyFunc(limitRangeInformer.Informer().HasSynced) l.SetReadyFunc(limitRangeInformer.Informer().HasSynced)
l.lister = limitRangeInformer.Lister() l.lister = limitRangeInformer.Lister()
} }
func (a *LimitRanger) SetExternalKubeClientSet(client kubernetes.Interface) { // SetExternalKubeClientSet registers the client into LimitRanger
a.client = client func (l *LimitRanger) SetExternalKubeClientSet(client kubernetes.Interface) {
l.client = client
} }
// ValidateInitialization verifies the LimitRanger object has been properly initialized
func (l *LimitRanger) ValidateInitialization() error { func (l *LimitRanger) ValidateInitialization() error {
if l.lister == nil { if l.lister == nil {
return fmt.Errorf("missing limitRange lister") return fmt.Errorf("missing limitRange lister")
@ -146,6 +149,8 @@ func (l *LimitRanger) runLimitFunc(a admission.Attributes, limitFn func(limitRan
return nil return nil
} }
// GetLimitRanges returns a LimitRange object with the items held in
// the indexer if available, or do alive lookup of the value.
func (l *LimitRanger) GetLimitRanges(a admission.Attributes) ([]*corev1.LimitRange, error) { func (l *LimitRanger) GetLimitRanges(a admission.Attributes) ([]*corev1.LimitRange, error) {
items, err := l.lister.LimitRanges(a.GetNamespace()).List(labels.Everything()) items, err := l.lister.LimitRanges(a.GetNamespace()).List(labels.Everything())
if err != nil { if err != nil {
@ -306,13 +311,13 @@ func minConstraint(limitType string, resourceName string, enforced resource.Quan
observedReqValue, observedLimValue, enforcedValue := requestLimitEnforcedValues(req, lim, enforced) observedReqValue, observedLimValue, enforcedValue := requestLimitEnforcedValues(req, lim, enforced)
if !reqExists { if !reqExists {
return fmt.Errorf("minimum %s usage per %s is %s. No request is specified.", resourceName, limitType, enforced.String()) return fmt.Errorf("minimum %s usage per %s is %s. No request is specified", resourceName, limitType, enforced.String())
} }
if observedReqValue < enforcedValue { if observedReqValue < enforcedValue {
return fmt.Errorf("minimum %s usage per %s is %s, but request is %s.", resourceName, limitType, enforced.String(), req.String()) return fmt.Errorf("minimum %s usage per %s is %s, but request is %s", resourceName, limitType, enforced.String(), req.String())
} }
if limExists && (observedLimValue < enforcedValue) { if limExists && (observedLimValue < enforcedValue) {
return fmt.Errorf("minimum %s usage per %s is %s, but limit is %s.", resourceName, limitType, enforced.String(), lim.String()) return fmt.Errorf("minimum %s usage per %s is %s, but limit is %s", resourceName, limitType, enforced.String(), lim.String())
} }
return nil return nil
} }
@ -324,10 +329,10 @@ func maxRequestConstraint(limitType string, resourceName string, enforced resour
observedReqValue, _, enforcedValue := requestLimitEnforcedValues(req, resource.Quantity{}, enforced) observedReqValue, _, enforcedValue := requestLimitEnforcedValues(req, resource.Quantity{}, enforced)
if !reqExists { if !reqExists {
return fmt.Errorf("maximum %s usage per %s is %s. No request is specified.", resourceName, limitType, enforced.String()) return fmt.Errorf("maximum %s usage per %s is %s. No request is specified", resourceName, limitType, enforced.String())
} }
if observedReqValue > enforcedValue { if observedReqValue > enforcedValue {
return fmt.Errorf("maximum %s usage per %s is %s, but request is %s.", resourceName, limitType, enforced.String(), req.String()) return fmt.Errorf("maximum %s usage per %s is %s, but request is %s", resourceName, limitType, enforced.String(), req.String())
} }
return nil return nil
} }
@ -339,13 +344,13 @@ func maxConstraint(limitType string, resourceName string, enforced resource.Quan
observedReqValue, observedLimValue, enforcedValue := requestLimitEnforcedValues(req, lim, enforced) observedReqValue, observedLimValue, enforcedValue := requestLimitEnforcedValues(req, lim, enforced)
if !limExists { if !limExists {
return fmt.Errorf("maximum %s usage per %s is %s. No limit is specified.", resourceName, limitType, enforced.String()) return fmt.Errorf("maximum %s usage per %s is %s. No limit is specified", resourceName, limitType, enforced.String())
} }
if observedLimValue > enforcedValue { if observedLimValue > enforcedValue {
return fmt.Errorf("maximum %s usage per %s is %s, but limit is %s.", resourceName, limitType, enforced.String(), lim.String()) return fmt.Errorf("maximum %s usage per %s is %s, but limit is %s", resourceName, limitType, enforced.String(), lim.String())
} }
if reqExists && (observedReqValue > enforcedValue) { if reqExists && (observedReqValue > enforcedValue) {
return fmt.Errorf("maximum %s usage per %s is %s, but request is %s.", resourceName, limitType, enforced.String(), req.String()) return fmt.Errorf("maximum %s usage per %s is %s, but request is %s", resourceName, limitType, enforced.String(), req.String())
} }
return nil return nil
} }
@ -357,10 +362,10 @@ func limitRequestRatioConstraint(limitType string, resourceName string, enforced
observedReqValue, observedLimValue, _ := requestLimitEnforcedValues(req, lim, enforced) observedReqValue, observedLimValue, _ := requestLimitEnforcedValues(req, lim, enforced)
if !reqExists || (observedReqValue == int64(0)) { if !reqExists || (observedReqValue == int64(0)) {
return fmt.Errorf("%s max limit to request ratio per %s is %s, but no request is specified or request is 0.", resourceName, limitType, enforced.String()) return fmt.Errorf("%s max limit to request ratio per %s is %s, but no request is specified or request is 0", resourceName, limitType, enforced.String())
} }
if !limExists || (observedLimValue == int64(0)) { if !limExists || (observedLimValue == int64(0)) {
return fmt.Errorf("%s max limit to request ratio per %s is %s, but no limit is specified or limit is 0.", resourceName, limitType, enforced.String()) return fmt.Errorf("%s max limit to request ratio per %s is %s, but no limit is specified or limit is 0", resourceName, limitType, enforced.String())
} }
observedRatio := float64(observedLimValue) / float64(observedReqValue) observedRatio := float64(observedLimValue) / float64(observedReqValue)
@ -372,7 +377,7 @@ func limitRequestRatioConstraint(limitType string, resourceName string, enforced
} }
if observedRatio > maxLimitRequestRatio { if observedRatio > maxLimitRequestRatio {
return fmt.Errorf("%s max limit to request ratio per %s is %s, but provided ratio is %f.", resourceName, limitType, enforced.String(), displayObservedRatio) return fmt.Errorf("%s max limit to request ratio per %s is %s, but provided ratio is %f", resourceName, limitType, enforced.String(), displayObservedRatio)
} }
return nil return nil
@ -423,9 +428,10 @@ type DefaultLimitRangerActions struct{}
// ensure DefaultLimitRangerActions implements the LimitRangerActions interface. // ensure DefaultLimitRangerActions implements the LimitRangerActions interface.
var _ LimitRangerActions = &DefaultLimitRangerActions{} var _ LimitRangerActions = &DefaultLimitRangerActions{}
// Limit enforces resource requirements of incoming resources against enumerated constraints // MutateLimit enforces resource requirements of incoming resources
// on the LimitRange. It may modify the incoming object to apply default resource requirements // against enumerated constraints on the LimitRange. It may modify
// if not specified, and enumerated on the LimitRange // the incoming object to apply default resource requirements if not
// specified, and enumerated on the LimitRange
func (d *DefaultLimitRangerActions) MutateLimit(limitRange *corev1.LimitRange, resourceName string, obj runtime.Object) error { func (d *DefaultLimitRangerActions) MutateLimit(limitRange *corev1.LimitRange, resourceName string, obj runtime.Object) error {
switch resourceName { switch resourceName {
case "pods": case "pods":
@ -434,9 +440,9 @@ func (d *DefaultLimitRangerActions) MutateLimit(limitRange *corev1.LimitRange, r
return nil return nil
} }
// Limit enforces resource requirements of incoming resources against enumerated constraints // ValidateLimit verifies the resource requirements of incoming
// on the LimitRange. It may modify the incoming object to apply default resource requirements // resources against enumerated constraints on the LimitRange are
// if not specified, and enumerated on the LimitRange // valid
func (d *DefaultLimitRangerActions) ValidateLimit(limitRange *corev1.LimitRange, resourceName string, obj runtime.Object) error { func (d *DefaultLimitRangerActions) ValidateLimit(limitRange *corev1.LimitRange, resourceName string, obj runtime.Object) error {
switch resourceName { switch resourceName {
case "pods": case "pods":

View File

@ -656,42 +656,42 @@ func TestPodLimitFuncApplyDefault(t *testing.T) {
for i := range testPod.Spec.Containers { for i := range testPod.Spec.Containers {
container := testPod.Spec.Containers[i] container := testPod.Spec.Containers[i]
limitMemory := container.Resources.Limits.Memory().String() limitMemory := container.Resources.Limits.Memory().String()
limitCpu := container.Resources.Limits.Cpu().String() limitCPU := container.Resources.Limits.Cpu().String()
requestMemory := container.Resources.Requests.Memory().String() requestMemory := container.Resources.Requests.Memory().String()
requestCpu := container.Resources.Requests.Cpu().String() requestCPU := container.Resources.Requests.Cpu().String()
if limitMemory != "10Mi" { if limitMemory != "10Mi" {
t.Errorf("Unexpected limit memory value %s", limitMemory) t.Errorf("Unexpected limit memory value %s", limitMemory)
} }
if limitCpu != "75m" { if limitCPU != "75m" {
t.Errorf("Unexpected limit cpu value %s", limitCpu) t.Errorf("Unexpected limit cpu value %s", limitCPU)
} }
if requestMemory != "5Mi" { if requestMemory != "5Mi" {
t.Errorf("Unexpected request memory value %s", requestMemory) t.Errorf("Unexpected request memory value %s", requestMemory)
} }
if requestCpu != "50m" { if requestCPU != "50m" {
t.Errorf("Unexpected request cpu value %s", requestCpu) t.Errorf("Unexpected request cpu value %s", requestCPU)
} }
} }
for i := range testPod.Spec.InitContainers { for i := range testPod.Spec.InitContainers {
container := testPod.Spec.InitContainers[i] container := testPod.Spec.InitContainers[i]
limitMemory := container.Resources.Limits.Memory().String() limitMemory := container.Resources.Limits.Memory().String()
limitCpu := container.Resources.Limits.Cpu().String() limitCPU := container.Resources.Limits.Cpu().String()
requestMemory := container.Resources.Requests.Memory().String() requestMemory := container.Resources.Requests.Memory().String()
requestCpu := container.Resources.Requests.Cpu().String() requestCPU := container.Resources.Requests.Cpu().String()
if limitMemory != "10Mi" { if limitMemory != "10Mi" {
t.Errorf("Unexpected limit memory value %s", limitMemory) t.Errorf("Unexpected limit memory value %s", limitMemory)
} }
if limitCpu != "75m" { if limitCPU != "75m" {
t.Errorf("Unexpected limit cpu value %s", limitCpu) t.Errorf("Unexpected limit cpu value %s", limitCPU)
} }
if requestMemory != "5Mi" { if requestMemory != "5Mi" {
t.Errorf("Unexpected request memory value %s", requestMemory) t.Errorf("Unexpected request memory value %s", requestMemory)
} }
if requestCpu != "50m" { if requestCPU != "50m" {
t.Errorf("Unexpected request cpu value %s", requestCpu) t.Errorf("Unexpected request cpu value %s", requestCPU)
} }
} }
} }

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
) )
// LimitRangerActions is an interface defining actions to be carried over ranges to identify and manipulate their limits
type LimitRangerActions interface { type LimitRangerActions interface {
// MutateLimit is a pluggable function to set limits on the object. // MutateLimit is a pluggable function to set limits on the object.
MutateLimit(limitRange *corev1.LimitRange, kind string, obj runtime.Object) error MutateLimit(limitRange *corev1.LimitRange, kind string, obj runtime.Object) error

View File

@ -45,9 +45,8 @@ import (
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
) )
const ( // PluginName is a string with the name of the plugin
PluginName = "NodeRestriction" const PluginName = "NodeRestriction"
)
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
@ -58,16 +57,16 @@ func Register(plugins *admission.Plugins) {
// NewPlugin creates a new NodeRestriction admission plugin. // NewPlugin creates a new NodeRestriction admission plugin.
// This plugin identifies requests from nodes // This plugin identifies requests from nodes
func NewPlugin(nodeIdentifier nodeidentifier.NodeIdentifier) *nodePlugin { func NewPlugin(nodeIdentifier nodeidentifier.NodeIdentifier) *Plugin {
return &nodePlugin{ return &Plugin{
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete), Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
nodeIdentifier: nodeIdentifier, nodeIdentifier: nodeIdentifier,
features: utilfeature.DefaultFeatureGate, features: utilfeature.DefaultFeatureGate,
} }
} }
// nodePlugin holds state for and implements the admission plugin. // Plugin holds state for and implements the admission plugin.
type nodePlugin struct { type Plugin struct {
*admission.Handler *admission.Handler
nodeIdentifier nodeidentifier.NodeIdentifier nodeIdentifier nodeidentifier.NodeIdentifier
podsGetter corev1lister.PodLister podsGetter corev1lister.PodLister
@ -76,15 +75,17 @@ type nodePlugin struct {
} }
var ( var (
_ = admission.Interface(&nodePlugin{}) _ = admission.Interface(&Plugin{})
_ = apiserveradmission.WantsExternalKubeInformerFactory(&nodePlugin{}) _ = apiserveradmission.WantsExternalKubeInformerFactory(&Plugin{})
) )
func (p *nodePlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { // SetExternalKubeInformerFactory registers an informer factory into Plugin
func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
p.podsGetter = f.Core().V1().Pods().Lister() p.podsGetter = f.Core().V1().Pods().Lister()
} }
func (p *nodePlugin) ValidateInitialization() error { // ValidateInitialization validates the Plugin was initialized properly
func (p *Plugin) ValidateInitialization() error {
if p.nodeIdentifier == nil { if p.nodeIdentifier == nil {
return fmt.Errorf("%s requires a node identifier", PluginName) return fmt.Errorf("%s requires a node identifier", PluginName)
} }
@ -103,8 +104,9 @@ var (
csiNodeResource = storage.Resource("csinodes") csiNodeResource = storage.Resource("csinodes")
) )
func (c *nodePlugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error { // Admit checks the admission policy and triggers corresponding actions
nodeName, isNode := c.nodeIdentifier.NodeIdentity(a.GetUserInfo()) func (p *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
nodeName, isNode := p.nodeIdentifier.NodeIdentity(a.GetUserInfo())
// Our job is just to restrict nodes // Our job is just to restrict nodes
if !isNode { if !isNode {
@ -120,41 +122,41 @@ func (c *nodePlugin) Admit(a admission.Attributes, o admission.ObjectInterfaces)
case podResource: case podResource:
switch a.GetSubresource() { switch a.GetSubresource() {
case "": case "":
return c.admitPod(nodeName, a) return p.admitPod(nodeName, a)
case "status": case "status":
return c.admitPodStatus(nodeName, a) return p.admitPodStatus(nodeName, a)
case "eviction": case "eviction":
return c.admitPodEviction(nodeName, a) return p.admitPodEviction(nodeName, a)
default: default:
return admission.NewForbidden(a, fmt.Errorf("unexpected pod subresource %q, only 'status' and 'eviction' are allowed", a.GetSubresource())) return admission.NewForbidden(a, fmt.Errorf("unexpected pod subresource %q, only 'status' and 'eviction' are allowed", a.GetSubresource()))
} }
case nodeResource: case nodeResource:
return c.admitNode(nodeName, a) return p.admitNode(nodeName, a)
case pvcResource: case pvcResource:
switch a.GetSubresource() { switch a.GetSubresource() {
case "status": case "status":
return c.admitPVCStatus(nodeName, a) return p.admitPVCStatus(nodeName, a)
default: default:
return admission.NewForbidden(a, fmt.Errorf("may only update PVC status")) return admission.NewForbidden(a, fmt.Errorf("may only update PVC status"))
} }
case svcacctResource: case svcacctResource:
if c.features.Enabled(features.TokenRequest) { if p.features.Enabled(features.TokenRequest) {
return c.admitServiceAccount(nodeName, a) return p.admitServiceAccount(nodeName, a)
} }
return nil return nil
case leaseResource: case leaseResource:
if c.features.Enabled(features.NodeLease) { if p.features.Enabled(features.NodeLease) {
return c.admitLease(nodeName, a) return p.admitLease(nodeName, a)
} }
return admission.NewForbidden(a, fmt.Errorf("disabled by feature gate %s", features.NodeLease)) return admission.NewForbidden(a, fmt.Errorf("disabled by feature gate %s", features.NodeLease))
case csiNodeResource: case csiNodeResource:
if c.features.Enabled(features.KubeletPluginsWatcher) && c.features.Enabled(features.CSINodeInfo) { if p.features.Enabled(features.KubeletPluginsWatcher) && p.features.Enabled(features.CSINodeInfo) {
return c.admitCSINode(nodeName, a) return p.admitCSINode(nodeName, a)
} }
return admission.NewForbidden(a, fmt.Errorf("disabled by feature gates %s and %s", features.KubeletPluginsWatcher, features.CSINodeInfo)) return admission.NewForbidden(a, fmt.Errorf("disabled by feature gates %s and %s", features.KubeletPluginsWatcher, features.CSINodeInfo))
@ -163,7 +165,9 @@ func (c *nodePlugin) Admit(a admission.Attributes, o admission.ObjectInterfaces)
} }
} }
func (c *nodePlugin) admitPod(nodeName string, a admission.Attributes) error { // admitPod allows creating or deleting a pod if it is assigned to the
// current node and fulfills related criteria.
func (p *Plugin) admitPod(nodeName string, a admission.Attributes) error {
switch a.GetOperation() { switch a.GetOperation() {
case admission.Create: case admission.Create:
// require a pod object // require a pod object
@ -206,7 +210,7 @@ func (c *nodePlugin) admitPod(nodeName string, a admission.Attributes) error {
case admission.Delete: case admission.Delete:
// get the existing pod // get the existing pod
existingPod, err := c.podsGetter.Pods(a.GetNamespace()).Get(a.GetName()) existingPod, err := p.podsGetter.Pods(a.GetNamespace()).Get(a.GetName())
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
return err return err
} }
@ -224,7 +228,9 @@ func (c *nodePlugin) admitPod(nodeName string, a admission.Attributes) error {
} }
} }
func (c *nodePlugin) admitPodStatus(nodeName string, a admission.Attributes) error { // admitPodStatus allows to update the status of a pod if it is
// assigned to the current node.
func (p *Plugin) admitPodStatus(nodeName string, a admission.Attributes) error {
switch a.GetOperation() { switch a.GetOperation() {
case admission.Update: case admission.Update:
// require an existing pod // require an existing pod
@ -243,7 +249,8 @@ func (c *nodePlugin) admitPodStatus(nodeName string, a admission.Attributes) err
} }
} }
func (c *nodePlugin) admitPodEviction(nodeName string, a admission.Attributes) error { // admitPodEviction allows to evict a pod if it is assigned to the current node.
func (p *Plugin) admitPodEviction(nodeName string, a admission.Attributes) error {
switch a.GetOperation() { switch a.GetOperation() {
case admission.Create: case admission.Create:
// require eviction to an existing pod object // require eviction to an existing pod object
@ -260,7 +267,7 @@ func (c *nodePlugin) admitPodEviction(nodeName string, a admission.Attributes) e
podName = eviction.Name podName = eviction.Name
} }
// get the existing pod // get the existing pod
existingPod, err := c.podsGetter.Pods(a.GetNamespace()).Get(podName) existingPod, err := p.podsGetter.Pods(a.GetNamespace()).Get(podName)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
return err return err
} }
@ -278,10 +285,10 @@ func (c *nodePlugin) admitPodEviction(nodeName string, a admission.Attributes) e
} }
} }
func (c *nodePlugin) admitPVCStatus(nodeName string, a admission.Attributes) error { func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
switch a.GetOperation() { switch a.GetOperation() {
case admission.Update: case admission.Update:
if !c.features.Enabled(features.ExpandPersistentVolumes) { if !p.features.Enabled(features.ExpandPersistentVolumes) {
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update persistentvolumeclaim metadata", nodeName)) return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update persistentvolumeclaim metadata", nodeName))
} }
@ -328,7 +335,7 @@ func (c *nodePlugin) admitPVCStatus(nodeName string, a admission.Attributes) err
} }
} }
func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error { func (p *Plugin) admitNode(nodeName string, a admission.Attributes) error {
requestedName := a.GetName() requestedName := a.GetName()
if a.GetOperation() == admission.Create { if a.GetOperation() == admission.Create {
node, ok := a.GetObject().(*api.Node) node, ok := a.GetObject().(*api.Node)
@ -345,12 +352,12 @@ func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error {
// Don't allow a node to register with labels outside the allowed set. // Don't allow a node to register with labels outside the allowed set.
// This would allow a node to add or modify its labels in a way that would let it steer privileged workloads to itself. // This would allow a node to add or modify its labels in a way that would let it steer privileged workloads to itself.
modifiedLabels := getModifiedLabels(node.Labels, nil) modifiedLabels := getModifiedLabels(node.Labels, nil)
if forbiddenLabels := c.getForbiddenCreateLabels(modifiedLabels); len(forbiddenLabels) > 0 { if forbiddenLabels := p.getForbiddenCreateLabels(modifiedLabels); len(forbiddenLabels) > 0 {
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to set the following labels: %s", nodeName, strings.Join(forbiddenLabels.List(), ", "))) return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to set the following labels: %s", nodeName, strings.Join(forbiddenLabels.List(), ", ")))
} }
// check and warn if nodes set labels on create that would have been forbidden on update // check and warn if nodes set labels on create that would have been forbidden on update
// TODO(liggitt): in 1.17, expand getForbiddenCreateLabels to match getForbiddenUpdateLabels and drop this // TODO(liggitt): in 1.17, expand getForbiddenCreateLabels to match getForbiddenUpdateLabels and drop this
if forbiddenUpdateLabels := c.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 { if forbiddenUpdateLabels := p.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 {
klog.Warningf("node %q added disallowed labels on node creation: %s", nodeName, strings.Join(forbiddenUpdateLabels.List(), ", ")) klog.Warningf("node %q added disallowed labels on node creation: %s", nodeName, strings.Join(forbiddenUpdateLabels.List(), ", "))
} }
@ -389,7 +396,7 @@ func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error {
// Don't allow a node to update labels outside the allowed set. // Don't allow a node to update labels outside the allowed set.
// This would allow a node to add or modify its labels in a way that would let it steer privileged workloads to itself. // This would allow a node to add or modify its labels in a way that would let it steer privileged workloads to itself.
modifiedLabels := getModifiedLabels(node.Labels, oldNode.Labels) modifiedLabels := getModifiedLabels(node.Labels, oldNode.Labels)
if forbiddenUpdateLabels := c.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 { if forbiddenUpdateLabels := p.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 {
return admission.NewForbidden(a, fmt.Errorf("is not allowed to modify labels: %s", strings.Join(forbiddenUpdateLabels.List(), ", "))) return admission.NewForbidden(a, fmt.Errorf("is not allowed to modify labels: %s", strings.Join(forbiddenUpdateLabels.List(), ", ")))
} }
} }
@ -433,7 +440,7 @@ func getLabelNamespace(key string) string {
// getForbiddenCreateLabels returns the set of labels that may not be set by the node. // getForbiddenCreateLabels returns the set of labels that may not be set by the node.
// TODO(liggitt): in 1.17, expand to match getForbiddenUpdateLabels() // TODO(liggitt): in 1.17, expand to match getForbiddenUpdateLabels()
func (c *nodePlugin) getForbiddenCreateLabels(modifiedLabels sets.String) sets.String { func (p *Plugin) getForbiddenCreateLabels(modifiedLabels sets.String) sets.String {
if len(modifiedLabels) == 0 { if len(modifiedLabels) == 0 {
return nil return nil
} }
@ -450,7 +457,7 @@ func (c *nodePlugin) getForbiddenCreateLabels(modifiedLabels sets.String) sets.S
} }
// getForbiddenLabels returns the set of labels that may not be set by the node on update. // getForbiddenLabels returns the set of labels that may not be set by the node on update.
func (c *nodePlugin) getForbiddenUpdateLabels(modifiedLabels sets.String) sets.String { func (p *Plugin) getForbiddenUpdateLabels(modifiedLabels sets.String) sets.String {
if len(modifiedLabels) == 0 { if len(modifiedLabels) == 0 {
return nil return nil
} }
@ -471,7 +478,7 @@ func (c *nodePlugin) getForbiddenUpdateLabels(modifiedLabels sets.String) sets.S
return forbiddenLabels return forbiddenLabels
} }
func (c *nodePlugin) admitServiceAccount(nodeName string, a admission.Attributes) error { func (p *Plugin) admitServiceAccount(nodeName string, a admission.Attributes) error {
if a.GetOperation() != admission.Create { if a.GetOperation() != admission.Create {
return nil return nil
} }
@ -495,7 +502,7 @@ func (c *nodePlugin) admitServiceAccount(nodeName string, a admission.Attributes
if ref.UID == "" { if ref.UID == "" {
return admission.NewForbidden(a, fmt.Errorf("node requested token with a pod binding without a uid")) return admission.NewForbidden(a, fmt.Errorf("node requested token with a pod binding without a uid"))
} }
pod, err := c.podsGetter.Pods(a.GetNamespace()).Get(ref.Name) pod, err := p.podsGetter.Pods(a.GetNamespace()).Get(ref.Name)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
return err return err
} }
@ -512,7 +519,7 @@ func (c *nodePlugin) admitServiceAccount(nodeName string, a admission.Attributes
return nil return nil
} }
func (r *nodePlugin) admitLease(nodeName string, a admission.Attributes) error { func (p *Plugin) admitLease(nodeName string, a admission.Attributes) error {
// the request must be against the system namespace reserved for node leases // the request must be against the system namespace reserved for node leases
if a.GetNamespace() != api.NamespaceNodeLease { if a.GetNamespace() != api.NamespaceNodeLease {
return admission.NewForbidden(a, fmt.Errorf("can only access leases in the %q system namespace", api.NamespaceNodeLease)) return admission.NewForbidden(a, fmt.Errorf("can only access leases in the %q system namespace", api.NamespaceNodeLease))
@ -537,7 +544,7 @@ func (r *nodePlugin) admitLease(nodeName string, a admission.Attributes) error {
return nil return nil
} }
func (c *nodePlugin) admitCSINode(nodeName string, a admission.Attributes) error { func (p *Plugin) admitCSINode(nodeName string, a admission.Attributes) error {
// the request must come from a node with the same name as the CSINode object // the request must come from a node with the same name as the CSINode object
if a.GetOperation() == admission.Create { if a.GetOperation() == admission.Create {
// a.GetName() won't return the name on create, so we drill down to the proposed object // a.GetName() won't return the name on create, so we drill down to the proposed object

View File

@ -36,10 +36,12 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
// The annotation key scheduler.alpha.kubernetes.io/node-selector is for assigning // NamespaceNodeSelectors is for assigning node selectors labels to
// node selectors labels to namespaces // namespaces. Default value is the annotation key
// scheduler.alpha.kubernetes.io/node-selector
var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"} var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"}
// PluginName is a string with the name of the plugin
const PluginName = "PodNodeSelector" const PluginName = "PodNodeSelector"
// Register registers a plugin // Register registers a plugin
@ -52,8 +54,8 @@ func Register(plugins *admission.Plugins) {
}) })
} }
// podNodeSelector is an implementation of admission.Interface. // Plugin is an implementation of admission.Interface.
type podNodeSelector struct { type Plugin struct {
*admission.Handler *admission.Handler
client kubernetes.Interface client kubernetes.Interface
namespaceLister corev1listers.NamespaceLister namespaceLister corev1listers.NamespaceLister
@ -61,8 +63,8 @@ type podNodeSelector struct {
clusterNodeSelectors map[string]string clusterNodeSelectors map[string]string
} }
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&podNodeSelector{}) var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Plugin{})
var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&podNodeSelector{}) var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Plugin{})
type pluginConfig struct { type pluginConfig struct {
PodNodeSelectorPluginConfig map[string]string PodNodeSelectorPluginConfig map[string]string
@ -94,7 +96,7 @@ func readConfig(config io.Reader) *pluginConfig {
} }
// Admit enforces that pod and its namespace node label selectors matches at least a node in the cluster. // Admit enforces that pod and its namespace node label selectors matches at least a node in the cluster.
func (p *podNodeSelector) Admit(a admission.Attributes, o admission.ObjectInterfaces) error { func (p *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
if shouldIgnore(a) { if shouldIgnore(a) {
return nil return nil
} }
@ -121,7 +123,7 @@ func (p *podNodeSelector) Admit(a admission.Attributes, o admission.ObjectInterf
} }
// Validate ensures that the pod node selector is allowed // Validate ensures that the pod node selector is allowed
func (p *podNodeSelector) Validate(a admission.Attributes, o admission.ObjectInterfaces) error { func (p *Plugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
if shouldIgnore(a) { if shouldIgnore(a) {
return nil return nil
} }
@ -152,7 +154,7 @@ func (p *podNodeSelector) Validate(a admission.Attributes, o admission.ObjectInt
return nil return nil
} }
func (p *podNodeSelector) getNamespaceNodeSelectorMap(namespaceName string) (labels.Set, error) { func (p *Plugin) getNamespaceNodeSelectorMap(namespaceName string) (labels.Set, error) {
namespace, err := p.namespaceLister.Get(namespaceName) namespace, err := p.namespaceLister.Get(namespaceName)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
namespace, err = p.defaultGetNamespace(namespaceName) namespace, err = p.defaultGetNamespace(namespaceName)
@ -188,24 +190,28 @@ func shouldIgnore(a admission.Attributes) bool {
return false return false
} }
func NewPodNodeSelector(clusterNodeSelectors map[string]string) *podNodeSelector { // NewPodNodeSelector initializes a podNodeSelector
return &podNodeSelector{ func NewPodNodeSelector(clusterNodeSelectors map[string]string) *Plugin {
return &Plugin{
Handler: admission.NewHandler(admission.Create), Handler: admission.NewHandler(admission.Create),
clusterNodeSelectors: clusterNodeSelectors, clusterNodeSelectors: clusterNodeSelectors,
} }
} }
func (a *podNodeSelector) SetExternalKubeClientSet(client kubernetes.Interface) { // SetExternalKubeClientSet sets the plugin's client
a.client = client func (p *Plugin) SetExternalKubeClientSet(client kubernetes.Interface) {
p.client = client
} }
func (p *podNodeSelector) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { // SetExternalKubeInformerFactory configures the plugin's informer factory
func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
namespaceInformer := f.Core().V1().Namespaces() namespaceInformer := f.Core().V1().Namespaces()
p.namespaceLister = namespaceInformer.Lister() p.namespaceLister = namespaceInformer.Lister()
p.SetReadyFunc(namespaceInformer.Informer().HasSynced) p.SetReadyFunc(namespaceInformer.Informer().HasSynced)
} }
func (p *podNodeSelector) ValidateInitialization() error { // ValidateInitialization verifies the object has been properly initialized
func (p *Plugin) ValidateInitialization() error {
if p.namespaceLister == nil { if p.namespaceLister == nil {
return fmt.Errorf("missing namespaceLister") return fmt.Errorf("missing namespaceLister")
} }
@ -215,7 +221,7 @@ func (p *podNodeSelector) ValidateInitialization() error {
return nil return nil
} }
func (p *podNodeSelector) defaultGetNamespace(name string) (*corev1.Namespace, error) { func (p *Plugin) defaultGetNamespace(name string) (*corev1.Namespace, error) {
namespace, err := p.client.CoreV1().Namespaces().Get(name, metav1.GetOptions{}) namespace, err := p.client.CoreV1().Namespaces().Get(name, metav1.GetOptions{})
if err != nil { if err != nil {
return nil, fmt.Errorf("namespace %s does not exist", name) return nil, fmt.Errorf("namespace %s does not exist", name)
@ -223,7 +229,7 @@ func (p *podNodeSelector) defaultGetNamespace(name string) (*corev1.Namespace, e
return namespace, nil return namespace, nil
} }
func (p *podNodeSelector) getNodeSelectorMap(namespace *corev1.Namespace) (labels.Set, error) { func (p *Plugin) getNodeSelectorMap(namespace *corev1.Namespace) (labels.Set, error) {
selector := labels.Set{} selector := labels.Set{}
labelsMap := labels.Set{} labelsMap := labels.Set{}
var err error var err error

View File

@ -194,7 +194,7 @@ func TestHandles(t *testing.T) {
} }
// newHandlerForTest returns the admission controller configured for testing. // newHandlerForTest returns the admission controller configured for testing.
func newHandlerForTest(c kubernetes.Interface) (*podNodeSelector, informers.SharedInformerFactory, error) { func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute) f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewPodNodeSelector(nil) handler := NewPodNodeSelector(nil)
pluginInitializer := genericadmissioninitializer.New(c, f, nil) pluginInitializer := genericadmissioninitializer.New(c, f, nil)

View File

@ -40,6 +40,7 @@ import (
const ( const (
annotationPrefix = "podpreset.admission.kubernetes.io" annotationPrefix = "podpreset.admission.kubernetes.io"
// PluginName is a string with the name of the plugin
PluginName = "PodPreset" PluginName = "PodPreset"
) )
@ -50,47 +51,50 @@ func Register(plugins *admission.Plugins) {
}) })
} }
// podPresetPlugin is an implementation of admission.Interface. // Plugin is an implementation of admission.Interface.
type podPresetPlugin struct { type Plugin struct {
*admission.Handler *admission.Handler
client kubernetes.Interface client kubernetes.Interface
lister settingsv1alpha1listers.PodPresetLister lister settingsv1alpha1listers.PodPresetLister
} }
var _ admission.MutationInterface = &podPresetPlugin{} var _ admission.MutationInterface = &Plugin{}
var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&podPresetPlugin{}) var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Plugin{})
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&podPresetPlugin{}) var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Plugin{})
// NewPlugin creates a new pod preset admission plugin. // NewPlugin creates a new pod preset admission plugin.
func NewPlugin() *podPresetPlugin { func NewPlugin() *Plugin {
return &podPresetPlugin{ return &Plugin{
Handler: admission.NewHandler(admission.Create, admission.Update), Handler: admission.NewHandler(admission.Create, admission.Update),
} }
} }
func (plugin *podPresetPlugin) ValidateInitialization() error { // ValidateInitialization validates the Plugin was initialized properly
if plugin.client == nil { func (p *Plugin) ValidateInitialization() error {
if p.client == nil {
return fmt.Errorf("%s requires a client", PluginName) return fmt.Errorf("%s requires a client", PluginName)
} }
if plugin.lister == nil { if p.lister == nil {
return fmt.Errorf("%s requires a lister", PluginName) return fmt.Errorf("%s requires a lister", PluginName)
} }
return nil return nil
} }
func (a *podPresetPlugin) SetExternalKubeClientSet(client kubernetes.Interface) { // SetExternalKubeClientSet registers the client into Plugin
a.client = client func (p *Plugin) SetExternalKubeClientSet(client kubernetes.Interface) {
p.client = client
} }
func (a *podPresetPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { // SetExternalKubeInformerFactory registers an informer factory into Plugin
func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
podPresetInformer := f.Settings().V1alpha1().PodPresets() podPresetInformer := f.Settings().V1alpha1().PodPresets()
a.lister = podPresetInformer.Lister() p.lister = podPresetInformer.Lister()
a.SetReadyFunc(podPresetInformer.Informer().HasSynced) p.SetReadyFunc(podPresetInformer.Informer().HasSynced)
} }
// Admit injects a pod with the specific fields for each pod preset it matches. // Admit injects a pod with the specific fields for each pod preset it matches.
func (c *podPresetPlugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error { func (p *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
// Ignore all calls to subresources or resources other than pods. // Ignore all calls to subresources or resources other than pods.
// Ignore all operations other than CREATE. // Ignore all operations other than CREATE.
if len(a.GetSubresource()) != 0 || a.GetResource().GroupResource() != api.Resource("pods") || a.GetOperation() != admission.Create { if len(a.GetSubresource()) != 0 || a.GetResource().GroupResource() != api.Resource("pods") || a.GetOperation() != admission.Create {
@ -114,7 +118,7 @@ func (c *podPresetPlugin) Admit(a admission.Attributes, o admission.ObjectInterf
} }
} }
list, err := c.lister.PodPresets(a.GetNamespace()).List(labels.Everything()) list, err := p.lister.PodPresets(a.GetNamespace()).List(labels.Everything())
if err != nil { if err != nil {
return fmt.Errorf("listing pod presets failed: %v", err) return fmt.Errorf("listing pod presets failed: %v", err)
} }

View File

@ -401,7 +401,7 @@ func NewTestAdmission(lister settingsv1alpha1listers.PodPresetLister, objects ..
// Build a test client that the admission plugin can use to look up the service account missing from its cache // Build a test client that the admission plugin can use to look up the service account missing from its cache
client := fake.NewSimpleClientset(objects...) client := fake.NewSimpleClientset(objects...)
return &podPresetPlugin{ return &Plugin{
client: client, client: client,
Handler: kadmission.NewHandler(kadmission.Create), Handler: kadmission.NewHandler(kadmission.Create),
lister: lister, lister: lister,

View File

@ -35,6 +35,7 @@ go_library(
srcs = [ srcs = [
"admission.go", "admission.go",
"config.go", "config.go",
"doc.go",
], ],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction", importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction",
deps = [ deps = [

View File

@ -39,6 +39,7 @@ import (
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
) )
// PluginName is a string with the name of the plugin
const PluginName = "PodTolerationRestriction" const PluginName = "PodTolerationRestriction"
// Register registers a plugin // Register registers a plugin
@ -58,29 +59,21 @@ const (
NSWLTolerations string = "scheduler.alpha.kubernetes.io/tolerationsWhitelist" NSWLTolerations string = "scheduler.alpha.kubernetes.io/tolerationsWhitelist"
) )
var _ admission.MutationInterface = &podTolerationsPlugin{} var _ admission.MutationInterface = &Plugin{}
var _ admission.ValidationInterface = &podTolerationsPlugin{} var _ admission.ValidationInterface = &Plugin{}
var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&podTolerationsPlugin{}) var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Plugin{})
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&podTolerationsPlugin{}) var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Plugin{})
type podTolerationsPlugin struct { // Plugin contains the client used by the admission controller
type Plugin struct {
*admission.Handler *admission.Handler
client kubernetes.Interface client kubernetes.Interface
namespaceLister corev1listers.NamespaceLister namespaceLister corev1listers.NamespaceLister
pluginConfig *pluginapi.Configuration pluginConfig *pluginapi.Configuration
} }
// This plugin first verifies any conflict between a pod's tolerations and // Admit checks the admission policy and triggers corresponding actions
// its namespace's tolerations, and rejects the pod if there's a conflict. func (p *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
// If there's no conflict, the pod's tolerations are merged with its namespace's
// toleration. Resulting pod's tolerations are verified against its namespace's
// whitelist of tolerations. If the verification is successful, the pod is admitted
// otherwise rejected. If a namespace does not have associated default or whitelist
// of tolerations, then cluster level default or whitelist of tolerations are used
// instead if specified. Tolerations to a namespace are assigned via
// scheduler.alpha.kubernetes.io/defaultTolerations and scheduler.alpha.kubernetes.io/tolerationsWhitelist
// annotations keys.
func (p *podTolerationsPlugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
if shouldIgnore(a) { if shouldIgnore(a) {
return nil return nil
} }
@ -136,7 +129,9 @@ func (p *podTolerationsPlugin) Admit(a admission.Attributes, o admission.ObjectI
pod.Spec.Tolerations = tolerations.MergeTolerations(finalTolerations, []api.Toleration{}) pod.Spec.Tolerations = tolerations.MergeTolerations(finalTolerations, []api.Toleration{})
return p.Validate(a, o) return p.Validate(a, o)
} }
func (p *podTolerationsPlugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
// Validate we can obtain a whitelist of tolerations
func (p *Plugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
if shouldIgnore(a) { if shouldIgnore(a) {
return nil return nil
} }
@ -190,25 +185,29 @@ func shouldIgnore(a admission.Attributes) bool {
return false return false
} }
func NewPodTolerationsPlugin(pluginConfig *pluginapi.Configuration) *podTolerationsPlugin { // NewPodTolerationsPlugin initializes a Plugin
return &podTolerationsPlugin{ func NewPodTolerationsPlugin(pluginConfig *pluginapi.Configuration) *Plugin {
return &Plugin{
Handler: admission.NewHandler(admission.Create, admission.Update), Handler: admission.NewHandler(admission.Create, admission.Update),
pluginConfig: pluginConfig, pluginConfig: pluginConfig,
} }
} }
func (a *podTolerationsPlugin) SetExternalKubeClientSet(client kubernetes.Interface) { // SetExternalKubeClientSet sets th client
a.client = client func (p *Plugin) SetExternalKubeClientSet(client kubernetes.Interface) {
p.client = client
} }
func (p *podTolerationsPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { // SetExternalKubeInformerFactory initializes the Informer Factory
func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
namespaceInformer := f.Core().V1().Namespaces() namespaceInformer := f.Core().V1().Namespaces()
p.namespaceLister = namespaceInformer.Lister() p.namespaceLister = namespaceInformer.Lister()
p.SetReadyFunc(namespaceInformer.Informer().HasSynced) p.SetReadyFunc(namespaceInformer.Informer().HasSynced)
} }
func (p *podTolerationsPlugin) ValidateInitialization() error { // ValidateInitialization checks the object is properly initialized
func (p *Plugin) ValidateInitialization() error {
if p.namespaceLister == nil { if p.namespaceLister == nil {
return fmt.Errorf("missing namespaceLister") return fmt.Errorf("missing namespaceLister")
} }
@ -219,7 +218,7 @@ func (p *podTolerationsPlugin) ValidateInitialization() error {
} }
// in exceptional cases, this can result in two live calls, but once the cache catches up, that will stop. // in exceptional cases, this can result in two live calls, but once the cache catches up, that will stop.
func (p *podTolerationsPlugin) getNamespace(nsName string) (*corev1.Namespace, error) { func (p *Plugin) getNamespace(nsName string) (*corev1.Namespace, error) {
namespace, err := p.namespaceLister.Get(nsName) namespace, err := p.namespaceLister.Get(nsName)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
// in case of latency in our caches, make a call direct to storage to verify that it truly exists or not // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not
@ -237,7 +236,7 @@ func (p *podTolerationsPlugin) getNamespace(nsName string) (*corev1.Namespace, e
return namespace, nil return namespace, nil
} }
func (p *podTolerationsPlugin) getNamespaceDefaultTolerations(nsName string) ([]api.Toleration, error) { func (p *Plugin) getNamespaceDefaultTolerations(nsName string) ([]api.Toleration, error) {
ns, err := p.getNamespace(nsName) ns, err := p.getNamespace(nsName)
if err != nil { if err != nil {
return nil, err return nil, err
@ -245,7 +244,7 @@ func (p *podTolerationsPlugin) getNamespaceDefaultTolerations(nsName string) ([]
return extractNSTolerations(ns, NSDefaultTolerations) return extractNSTolerations(ns, NSDefaultTolerations)
} }
func (p *podTolerationsPlugin) getNamespaceTolerationsWhitelist(nsName string) ([]api.Toleration, error) { func (p *Plugin) getNamespaceTolerationsWhitelist(nsName string) ([]api.Toleration, error) {
ns, err := p.getNamespace(nsName) ns, err := p.getNamespace(nsName)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -350,7 +350,7 @@ func TestIgnoreUpdatingInitializedPod(t *testing.T) {
} }
// newHandlerForTest returns the admission controller configured for testing. // newHandlerForTest returns the admission controller configured for testing.
func newHandlerForTest(c kubernetes.Interface) (*podTolerationsPlugin, informers.SharedInformerFactory, error) { func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute) f := informers.NewSharedInformerFactory(c, 5*time.Minute)
pluginConfig, err := loadConfiguration(nil) pluginConfig, err := loadConfiguration(nil)
// must not fail // must not fail

View File

@ -30,7 +30,7 @@ var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.
var ( var (
// SchemeBuilder is the scheme builder with scheme init functions to run for this API package // SchemeBuilder is the scheme builder with scheme init functions to run for this API package
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is a global function that registers this API group & version to a scheme // AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = SchemeBuilder.AddToScheme AddToScheme = SchemeBuilder.AddToScheme
) )

View File

@ -30,8 +30,11 @@ var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha
var ( var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// SchemeBuilder is a pointer used to call AddToScheme
SchemeBuilder runtime.SchemeBuilder SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder localSchemeBuilder = &SchemeBuilder
// AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = localSchemeBuilder.AddToScheme AddToScheme = localSchemeBuilder.AddToScheme
) )

View File

@ -0,0 +1,30 @@
/*
Copyright 2018 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 podtolerationrestriction is a plugin that first verifies
// any conflict between a pod's tolerations and its namespace's
// tolerations, and rejects the pod if there's a conflict. If there's
// no conflict, the pod's tolerations are merged with its namespace's
// toleration. Resulting pod's tolerations are verified against its
// namespace's whitelist of tolerations. If the verification is
// successful, the pod is admitted otherwise rejected. If a namespace
// does not have associated default or whitelist of tolerations, then
// cluster level default or whitelist of tolerations are used instead
// if specified. Tolerations to a namespace are assigned via
// scheduler.alpha.kubernetes.io/defaultTolerations and
// scheduler.alpha.kubernetes.io/tolerationsWhitelist annotations
// keys.
package podtolerationrestriction // import "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"

View File

@ -33,6 +33,7 @@ import (
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation" "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation"
) )
// PluginName is a string with the name of the plugin
const PluginName = "ResourceQuota" const PluginName = "ResourceQuota"
// Register registers a plugin // Register registers a plugin
@ -93,14 +94,17 @@ func NewResourceQuota(config *resourcequotaapi.Configuration, numEvaluators int,
}, nil }, nil
} }
// SetExternalKubeClientSet registers the client into QuotaAdmission
func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) { func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) {
a.quotaAccessor.client = client a.quotaAccessor.client = client
} }
// SetExternalKubeInformerFactory registers an informer factory into QuotaAdmission
func (a *QuotaAdmission) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { func (a *QuotaAdmission) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
a.quotaAccessor.lister = f.Core().V1().ResourceQuotas().Lister() a.quotaAccessor.lister = f.Core().V1().ResourceQuotas().Lister()
} }
// SetQuotaConfiguration assigns and initializes configuration and evaluator for QuotaAdmission
func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) { func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) {
a.quotaConfiguration = c a.quotaConfiguration = c
a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration.IgnoredResources(), generic.NewRegistry(a.quotaConfiguration.Evaluators()), nil, a.config, a.numEvaluators, a.stopCh) a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration.IgnoredResources(), generic.NewRegistry(a.quotaConfiguration.Evaluators()), nil, a.config, a.numEvaluators, a.stopCh)

View File

@ -22,7 +22,9 @@ import (
) )
var ( var (
// SchemeBuilder is a pointer used to call AddToScheme
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = SchemeBuilder.AddToScheme AddToScheme = SchemeBuilder.AddToScheme
) )

View File

@ -30,8 +30,11 @@ var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha
var ( var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// SchemeBuilder is a pointer used to call AddToScheme
SchemeBuilder runtime.SchemeBuilder SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder localSchemeBuilder = &SchemeBuilder
// AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = localSchemeBuilder.AddToScheme AddToScheme = localSchemeBuilder.AddToScheme
) )

View File

@ -30,8 +30,11 @@ var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1
var ( var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// SchemeBuilder is a pointer used to call AddToScheme
SchemeBuilder runtime.SchemeBuilder SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder localSchemeBuilder = &SchemeBuilder
// AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = localSchemeBuilder.AddToScheme AddToScheme = localSchemeBuilder.AddToScheme
) )

View File

@ -14,6 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// resourcequota enforces all incoming requests against any applied quota // Package resourcequota enforces all incoming requests against any applied quota
// in the namespace context of the request // in the namespace context of the request
package resourcequota // import "k8s.io/kubernetes/plugin/pkg/admission/resourcequota" package resourcequota // import "k8s.io/kubernetes/plugin/pkg/admission/resourcequota"

View File

@ -43,9 +43,8 @@ import (
"k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount"
) )
const ( // PluginName is a string with the name of the plugin
PluginName = "PodSecurityPolicy" const PluginName = "PodSecurityPolicy"
)
// Register registers a plugin // Register registers a plugin
func Register(plugins *admission.Plugins) { func Register(plugins *admission.Plugins) {
@ -55,8 +54,8 @@ func Register(plugins *admission.Plugins) {
}) })
} }
// PodSecurityPolicyPlugin holds state for and implements the admission plugin. // Plugin holds state for and implements the admission plugin.
type PodSecurityPolicyPlugin struct { type Plugin struct {
*admission.Handler *admission.Handler
strategyFactory psp.StrategyFactory strategyFactory psp.StrategyFactory
failOnNoPolicies bool failOnNoPolicies bool
@ -65,40 +64,41 @@ type PodSecurityPolicyPlugin struct {
} }
// SetAuthorizer sets the authorizer. // SetAuthorizer sets the authorizer.
func (plugin *PodSecurityPolicyPlugin) SetAuthorizer(authz authorizer.Authorizer) { func (p *Plugin) SetAuthorizer(authz authorizer.Authorizer) {
plugin.authz = authz p.authz = authz
} }
// ValidateInitialization ensures an authorizer is set. // ValidateInitialization ensures an authorizer is set.
func (plugin *PodSecurityPolicyPlugin) ValidateInitialization() error { func (p *Plugin) ValidateInitialization() error {
if plugin.authz == nil { if p.authz == nil {
return fmt.Errorf("%s requires an authorizer", PluginName) return fmt.Errorf("%s requires an authorizer", PluginName)
} }
if plugin.lister == nil { if p.lister == nil {
return fmt.Errorf("%s requires a lister", PluginName) return fmt.Errorf("%s requires a lister", PluginName)
} }
return nil return nil
} }
var _ admission.MutationInterface = &PodSecurityPolicyPlugin{} var _ admission.MutationInterface = &Plugin{}
var _ admission.ValidationInterface = &PodSecurityPolicyPlugin{} var _ admission.ValidationInterface = &Plugin{}
var _ genericadmissioninit.WantsAuthorizer = &PodSecurityPolicyPlugin{} var _ genericadmissioninit.WantsAuthorizer = &Plugin{}
var _ genericadmissioninit.WantsExternalKubeInformerFactory = &PodSecurityPolicyPlugin{} var _ genericadmissioninit.WantsExternalKubeInformerFactory = &Plugin{}
var auditKeyPrefix = strings.ToLower(PluginName) + "." + policy.GroupName + ".k8s.io" var auditKeyPrefix = strings.ToLower(PluginName) + "." + policy.GroupName + ".k8s.io"
// newPlugin creates a new PSP admission plugin. // newPlugin creates a new PSP admission plugin.
func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *PodSecurityPolicyPlugin { func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *Plugin {
return &PodSecurityPolicyPlugin{ return &Plugin{
Handler: admission.NewHandler(admission.Create, admission.Update), Handler: admission.NewHandler(admission.Create, admission.Update),
strategyFactory: strategyFactory, strategyFactory: strategyFactory,
failOnNoPolicies: failOnNoPolicies, failOnNoPolicies: failOnNoPolicies,
} }
} }
func (a *PodSecurityPolicyPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { // SetExternalKubeInformerFactory registers an informer
func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
podSecurityPolicyInformer := f.Policy().V1beta1().PodSecurityPolicies() podSecurityPolicyInformer := f.Policy().V1beta1().PodSecurityPolicies()
a.lister = podSecurityPolicyInformer.Lister() p.lister = podSecurityPolicyInformer.Lister()
a.SetReadyFunc(podSecurityPolicyInformer.Informer().HasSynced) p.SetReadyFunc(podSecurityPolicyInformer.Informer().HasSynced)
} }
// Admit determines if the pod should be admitted based on the requested security context // Admit determines if the pod should be admitted based on the requested security context
@ -109,7 +109,7 @@ func (a *PodSecurityPolicyPlugin) SetExternalKubeInformerFactory(f informers.Sha
// 3. Try to generate and validate a PSP with providers. If we find one then admit the pod // 3. Try to generate and validate a PSP with providers. If we find one then admit the pod
// with the validated PSP. If we don't find any reject the pod and give all errors from the // with the validated PSP. If we don't find any reject the pod and give all errors from the
// failed attempts. // failed attempts.
func (c *PodSecurityPolicyPlugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error { func (p *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
if ignore, err := shouldIgnore(a); err != nil { if ignore, err := shouldIgnore(a); err != nil {
return err return err
} else if ignore { } else if ignore {
@ -125,7 +125,7 @@ func (c *PodSecurityPolicyPlugin) Admit(a admission.Attributes, o admission.Obje
pod := a.GetObject().(*api.Pod) pod := a.GetObject().(*api.Pod)
// compute the context. Mutation is allowed. ValidatedPSPAnnotation is not taken into account. // compute the context. Mutation is allowed. ValidatedPSPAnnotation is not taken into account.
allowedPod, pspName, validationErrs, err := c.computeSecurityContext(a, pod, true, "") allowedPod, pspName, validationErrs, err := p.computeSecurityContext(a, pod, true, "")
if err != nil { if err != nil {
return admission.NewForbidden(a, err) return admission.NewForbidden(a, err)
} }
@ -149,7 +149,8 @@ func (c *PodSecurityPolicyPlugin) Admit(a admission.Attributes, o admission.Obje
return admission.NewForbidden(a, fmt.Errorf("unable to validate against any pod security policy: %v", validationErrs)) return admission.NewForbidden(a, fmt.Errorf("unable to validate against any pod security policy: %v", validationErrs))
} }
func (c *PodSecurityPolicyPlugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) error { // Validate verifies attributes against the PodSecurityPolicy
func (p *Plugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
if ignore, err := shouldIgnore(a); err != nil { if ignore, err := shouldIgnore(a); err != nil {
return err return err
} else if ignore { } else if ignore {
@ -159,7 +160,7 @@ func (c *PodSecurityPolicyPlugin) Validate(a admission.Attributes, o admission.O
pod := a.GetObject().(*api.Pod) pod := a.GetObject().(*api.Pod)
// compute the context. Mutation is not allowed. ValidatedPSPAnnotation is used as a hint to gain same speed-up. // compute the context. Mutation is not allowed. ValidatedPSPAnnotation is used as a hint to gain same speed-up.
allowedPod, pspName, validationErrs, err := c.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation]) allowedPod, pspName, validationErrs, err := p.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation])
if err != nil { if err != nil {
return admission.NewForbidden(a, err) return admission.NewForbidden(a, err)
} }
@ -205,7 +206,7 @@ func shouldIgnore(a admission.Attributes) (bool, error) {
// if there is a matching policy with the same security context as given, it will be reused. If there is no // if there is a matching policy with the same security context as given, it will be reused. If there is no
// matching policy the returned pod will be nil and the pspName empty. validatedPSPHint is the validated psp name // matching policy the returned pod will be nil and the pspName empty. validatedPSPHint is the validated psp name
// saved in kubernetes.io/psp annotation. This psp is usually the one we are looking for. // saved in kubernetes.io/psp annotation. This psp is usually the one we are looking for.
func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes, pod *api.Pod, specMutationAllowed bool, validatedPSPHint string) (*api.Pod, string, field.ErrorList, error) { func (p *Plugin) computeSecurityContext(a admission.Attributes, pod *api.Pod, specMutationAllowed bool, validatedPSPHint string) (*api.Pod, string, field.ErrorList, error) {
// get all constraints that are usable by the user // get all constraints that are usable by the user
klog.V(4).Infof("getting pod security policies for pod %s (generate: %s)", pod.Name, pod.GenerateName) klog.V(4).Infof("getting pod security policies for pod %s (generate: %s)", pod.Name, pod.GenerateName)
var saInfo user.Info var saInfo user.Info
@ -213,14 +214,14 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
saInfo = serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "") saInfo = serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "")
} }
policies, err := c.lister.List(labels.Everything()) policies, err := p.lister.List(labels.Everything())
if err != nil { if err != nil {
return nil, "", nil, err return nil, "", nil, err
} }
// if we have no policies and want to succeed then return. Otherwise we'll end up with no // if we have no policies and want to succeed then return. Otherwise we'll end up with no
// providers and fail with "unable to validate against any pod security policy" below. // providers and fail with "unable to validate against any pod security policy" below.
if len(policies) == 0 && !c.failOnNoPolicies { if len(policies) == 0 && !p.failOnNoPolicies {
return pod, "", nil, nil return pod, "", nil, nil
} }
@ -239,7 +240,7 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
return strings.Compare(policies[i].Name, policies[j].Name) < 0 return strings.Compare(policies[i].Name, policies[j].Name) < 0
}) })
providers, errs := c.createProvidersFromPolicies(policies, pod.Namespace) providers, errs := p.createProvidersFromPolicies(policies, pod.Namespace)
for _, err := range errs { for _, err := range errs {
klog.V(4).Infof("provider creation error: %v", err) klog.V(4).Infof("provider creation error: %v", err)
} }
@ -269,7 +270,7 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
continue continue
} }
if !isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), provider.GetPSPName(), c.authz) { if !isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), provider.GetPSPName(), p.authz) {
continue continue
} }
@ -293,7 +294,7 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
// Pod is rejected. Filter the validation errors to only include errors from authorized PSPs. // Pod is rejected. Filter the validation errors to only include errors from authorized PSPs.
aggregate := field.ErrorList{} aggregate := field.ErrorList{}
for psp, errs := range validationErrs { for psp, errs := range validationErrs {
if isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), psp, c.authz) { if isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), psp, p.authz) {
aggregate = append(aggregate, errs...) aggregate = append(aggregate, errs...)
} }
} }
@ -317,7 +318,7 @@ func assignSecurityContext(provider psp.Provider, pod *api.Pod) field.ErrorList
} }
// createProvidersFromPolicies creates providers from the constraints supplied. // createProvidersFromPolicies creates providers from the constraints supplied.
func (c *PodSecurityPolicyPlugin) createProvidersFromPolicies(psps []*policyv1beta1.PodSecurityPolicy, namespace string) ([]psp.Provider, []error) { func (p *Plugin) createProvidersFromPolicies(psps []*policyv1beta1.PodSecurityPolicy, namespace string) ([]psp.Provider, []error) {
var ( var (
// collected providers // collected providers
providers []psp.Provider providers []psp.Provider
@ -326,7 +327,7 @@ func (c *PodSecurityPolicyPlugin) createProvidersFromPolicies(psps []*policyv1be
) )
for _, constraint := range psps { for _, constraint := range psps {
provider, err := psp.NewSimpleProvider(constraint, namespace, c.strategyFactory) provider, err := psp.NewSimpleProvider(constraint, namespace, p.strategyFactory)
if err != nil { if err != nil {
errs = append(errs, fmt.Errorf("error creating provider for PSP %s: %v", constraint.Name, err)) errs = append(errs, fmt.Errorf("error creating provider for PSP %s: %v", constraint.Name, err))
continue continue

View File

@ -53,7 +53,7 @@ import (
const defaultContainerName = "test-c" const defaultContainerName = "test-c"
// NewTestAdmission provides an admission plugin with test implementations of internal structs. // NewTestAdmission provides an admission plugin with test implementations of internal structs.
func NewTestAdmission(psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer) *PodSecurityPolicyPlugin { func NewTestAdmission(psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer) *Plugin {
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
store := informerFactory.Policy().V1beta1().PodSecurityPolicies().Informer().GetStore() store := informerFactory.Policy().V1beta1().PodSecurityPolicies().Informer().GetStore()
for _, psp := range psps { for _, psp := range psps {
@ -63,7 +63,7 @@ func NewTestAdmission(psps []*policy.PodSecurityPolicy, authz authorizer.Authori
if authz == nil { if authz == nil {
authz = &TestAuthorizer{} authz = &TestAuthorizer{}
} }
return &PodSecurityPolicyPlugin{ return &Plugin{
Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update), Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update),
strategyFactory: kpsp.NewSimpleStrategyFactory(), strategyFactory: kpsp.NewSimpleStrategyFactory(),
authz: authz, authz: authz,
@ -1963,7 +1963,7 @@ func TestCreateProvidersFromConstraints(t *testing.T) {
} }
for k, v := range testCases { for k, v := range testCases {
admit := &PodSecurityPolicyPlugin{ admit := &Plugin{
Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update), Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update),
strategyFactory: kpsp.NewSimpleStrategyFactory(), strategyFactory: kpsp.NewSimpleStrategyFactory(),
} }

View File

@ -52,6 +52,7 @@ const (
// The value must be true to have this annotation take effect // The value must be true to have this annotation take effect
EnforceMountableSecretsAnnotation = "kubernetes.io/enforce-mountable-secrets" EnforceMountableSecretsAnnotation = "kubernetes.io/enforce-mountable-secrets"
// ServiceAccountVolumeName is the prefix name that will be added to volumes that mount ServiceAccount secrets
ServiceAccountVolumeName = "kube-api-access" ServiceAccountVolumeName = "kube-api-access"
// DefaultAPITokenMountPath is the path that ServiceAccountToken secrets are automounted to. // DefaultAPITokenMountPath is the path that ServiceAccountToken secrets are automounted to.
@ -70,9 +71,10 @@ func Register(plugins *admission.Plugins) {
}) })
} }
var _ = admission.Interface(&serviceAccount{}) var _ = admission.Interface(&Plugin{})
type serviceAccount struct { // Plugin contains the client used by the admission controller
type Plugin struct {
*admission.Handler *admission.Handler
// LimitSecretReferences rejects pods that reference secrets their service accounts do not reference // LimitSecretReferences rejects pods that reference secrets their service accounts do not reference
@ -92,10 +94,10 @@ type serviceAccount struct {
featureGate featuregate.FeatureGate featureGate featuregate.FeatureGate
} }
var _ admission.MutationInterface = &serviceAccount{} var _ admission.MutationInterface = &Plugin{}
var _ admission.ValidationInterface = &serviceAccount{} var _ admission.ValidationInterface = &Plugin{}
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&serviceAccount{}) var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Plugin{})
var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&serviceAccount{}) var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Plugin{})
// NewServiceAccount returns an admission.Interface implementation which limits admission of Pod CREATE requests based on the pod's 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" // 1. If the pod does not specify a ServiceAccount, it sets the pod's ServiceAccount to "default"
@ -103,8 +105,8 @@ var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&serviceAcc
// 3. If LimitSecretReferences is true, it rejects the pod if the pod references Secret objects which the pod's ServiceAccount does not reference // 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. // 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 // 5. If MountServiceAccountToken is true, it adds a VolumeMount with the pod's ServiceAccount's api token secret to containers
func NewServiceAccount() *serviceAccount { func NewServiceAccount() *Plugin {
return &serviceAccount{ return &Plugin{
Handler: admission.NewHandler(admission.Create), Handler: admission.NewHandler(admission.Create),
// TODO: enable this once we've swept secret usage to account for adding secret references to service accounts // TODO: enable this once we've swept secret usage to account for adding secret references to service accounts
LimitSecretReferences: false, LimitSecretReferences: false,
@ -119,11 +121,13 @@ func NewServiceAccount() *serviceAccount {
} }
} }
func (s *serviceAccount) SetExternalKubeClientSet(cl kubernetes.Interface) { // SetExternalKubeClientSet sets the client for the plugin
func (s *Plugin) SetExternalKubeClientSet(cl kubernetes.Interface) {
s.client = cl s.client = cl
} }
func (s *serviceAccount) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { // SetExternalKubeInformerFactory registers informers with the plugin
func (s *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
serviceAccountInformer := f.Core().V1().ServiceAccounts() serviceAccountInformer := f.Core().V1().ServiceAccounts()
s.serviceAccountLister = serviceAccountInformer.Lister() s.serviceAccountLister = serviceAccountInformer.Lister()
@ -136,7 +140,7 @@ func (s *serviceAccount) SetExternalKubeInformerFactory(f informers.SharedInform
} }
// ValidateInitialization ensures an authorizer is set. // ValidateInitialization ensures an authorizer is set.
func (s *serviceAccount) ValidateInitialization() error { func (s *Plugin) ValidateInitialization() error {
if s.client == nil { if s.client == nil {
return fmt.Errorf("missing client") return fmt.Errorf("missing client")
} }
@ -149,7 +153,8 @@ func (s *serviceAccount) ValidateInitialization() error {
return nil return nil
} }
func (s *serviceAccount) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) { // Admit verifies if the pod should be admitted
func (s *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
if shouldIgnore(a) { if shouldIgnore(a) {
return nil return nil
} }
@ -190,7 +195,8 @@ func (s *serviceAccount) Admit(a admission.Attributes, o admission.ObjectInterfa
return s.Validate(a, o) return s.Validate(a, o)
} }
func (s *serviceAccount) Validate(a admission.Attributes, o admission.ObjectInterfaces) (err error) { // Validate the data we obtained
func (s *Plugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
if shouldIgnore(a) { if shouldIgnore(a) {
return nil return nil
} }
@ -271,7 +277,7 @@ func shouldAutomount(sa *corev1.ServiceAccount, pod *api.Pod) bool {
// enforceMountableSecrets indicates whether mountable secrets should be enforced for a particular service account // enforceMountableSecrets indicates whether mountable secrets should be enforced for a particular service account
// A global setting of true will override any flag set on the individual service account // A global setting of true will override any flag set on the individual service account
func (s *serviceAccount) enforceMountableSecrets(serviceAccount *corev1.ServiceAccount) bool { func (s *Plugin) enforceMountableSecrets(serviceAccount *corev1.ServiceAccount) bool {
if s.LimitSecretReferences { if s.LimitSecretReferences {
return true return true
} }
@ -285,7 +291,7 @@ func (s *serviceAccount) enforceMountableSecrets(serviceAccount *corev1.ServiceA
} }
// getServiceAccount returns the ServiceAccount for the given namespace and name if it exists // getServiceAccount returns the ServiceAccount for the given namespace and name if it exists
func (s *serviceAccount) getServiceAccount(namespace string, name string) (*corev1.ServiceAccount, error) { func (s *Plugin) getServiceAccount(namespace string, name string) (*corev1.ServiceAccount, error) {
serviceAccount, err := s.serviceAccountLister.ServiceAccounts(namespace).Get(name) serviceAccount, err := s.serviceAccountLister.ServiceAccounts(namespace).Get(name)
if err == nil { if err == nil {
return serviceAccount, nil return serviceAccount, nil
@ -318,7 +324,7 @@ func (s *serviceAccount) getServiceAccount(namespace string, name string) (*core
} }
// getReferencedServiceAccountToken returns the name of the first referenced secret which is a ServiceAccountToken for the service account // getReferencedServiceAccountToken returns the name of the first referenced secret which is a ServiceAccountToken for the service account
func (s *serviceAccount) getReferencedServiceAccountToken(serviceAccount *corev1.ServiceAccount) (string, error) { func (s *Plugin) getReferencedServiceAccountToken(serviceAccount *corev1.ServiceAccount) (string, error) {
if len(serviceAccount.Secrets) == 0 { if len(serviceAccount.Secrets) == 0 {
return "", nil return "", nil
} }
@ -343,7 +349,7 @@ func (s *serviceAccount) getReferencedServiceAccountToken(serviceAccount *corev1
} }
// getServiceAccountTokens returns all ServiceAccountToken secrets for the given ServiceAccount // getServiceAccountTokens returns all ServiceAccountToken secrets for the given ServiceAccount
func (s *serviceAccount) getServiceAccountTokens(serviceAccount *corev1.ServiceAccount) ([]*corev1.Secret, error) { func (s *Plugin) getServiceAccountTokens(serviceAccount *corev1.ServiceAccount) ([]*corev1.Secret, error) {
secrets, err := s.secretLister.Secrets(serviceAccount.Namespace).List(labels.Everything()) secrets, err := s.secretLister.Secrets(serviceAccount.Namespace).List(labels.Everything())
if err != nil { if err != nil {
return nil, err return nil, err
@ -363,7 +369,7 @@ func (s *serviceAccount) getServiceAccountTokens(serviceAccount *corev1.ServiceA
return tokens, nil return tokens, nil
} }
func (s *serviceAccount) limitSecretReferences(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error { func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error {
// Ensure all secrets the pod references are allowed by the service account // Ensure all secrets the pod references are allowed by the service account
mountableSecrets := sets.NewString() mountableSecrets := sets.NewString()
for _, s := range serviceAccount.Secrets { for _, s := range serviceAccount.Secrets {
@ -413,7 +419,7 @@ func (s *serviceAccount) limitSecretReferences(serviceAccount *corev1.ServiceAcc
return nil return nil
} }
func (s *serviceAccount) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error { func (s *Plugin) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error {
// Find the name of a referenced ServiceAccountToken secret we can mount // Find the name of a referenced ServiceAccountToken secret we can mount
serviceAccountToken, err := s.getReferencedServiceAccountToken(serviceAccount) serviceAccountToken, err := s.getReferencedServiceAccountToken(serviceAccount)
if err != nil { if err != nil {
@ -502,7 +508,7 @@ func (s *serviceAccount) mountServiceAccountToken(serviceAccount *corev1.Service
return nil return nil
} }
func (s *serviceAccount) createVolume(tokenVolumeName, secretName string) api.Volume { func (s *Plugin) createVolume(tokenVolumeName, secretName string) api.Volume {
if s.featureGate.Enabled(kubefeatures.BoundServiceAccountTokenVolume) { if s.featureGate.Enabled(kubefeatures.BoundServiceAccountTokenVolume) {
return api.Volume{ return api.Volume{
Name: tokenVolumeName, Name: tokenVolumeName,

View File

@ -275,7 +275,7 @@ func TestDeniesInvalidServiceAccount(t *testing.T) {
} }
func TestAutomountsAPIToken(t *testing.T) { func TestAutomountsAPIToken(t *testing.T) {
testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*serviceAccount) *serviceAccount) { testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) {
admit := applyFeatures(NewServiceAccount()) admit := applyFeatures(NewServiceAccount())
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
@ -385,7 +385,7 @@ func TestAutomountsAPIToken(t *testing.T) {
} }
func TestRespectsExistingMount(t *testing.T) { func TestRespectsExistingMount(t *testing.T) {
testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*serviceAccount) *serviceAccount) { testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) {
ns := "myns" ns := "myns"
tokenName := "token-name" tokenName := "token-name"
serviceAccountName := DefaultServiceAccountName serviceAccountName := DefaultServiceAccountName
@ -914,7 +914,7 @@ func newSecret(secretType corev1.SecretType, namespace, name, serviceAccountName
} }
func TestGetServiceAccountTokens(t *testing.T) { func TestGetServiceAccountTokens(t *testing.T) {
testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*serviceAccount) *serviceAccount) { testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) {
admit := applyFeatures(NewServiceAccount()) admit := applyFeatures(NewServiceAccount())
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
admit.secretLister = corev1listers.NewSecretLister(indexer) admit.secretLister = corev1listers.NewSecretLister(indexer)
@ -1070,16 +1070,16 @@ func testGenerateName(n string) string {
var generatedVolumeName = testGenerateName(ServiceAccountVolumeName + "-") var generatedVolumeName = testGenerateName(ServiceAccountVolumeName + "-")
func testBoundServiceAccountTokenVolumePhases(t *testing.T, f func(*testing.T, func(*serviceAccount) *serviceAccount)) { func testBoundServiceAccountTokenVolumePhases(t *testing.T, f func(*testing.T, func(*Plugin) *Plugin)) {
t.Run("BoundServiceAccountTokenVolume disabled", func(t *testing.T) { t.Run("BoundServiceAccountTokenVolume disabled", func(t *testing.T) {
f(t, func(s *serviceAccount) *serviceAccount { f(t, func(s *Plugin) *Plugin {
s.featureGate = deprecationDisabledFeature s.featureGate = deprecationDisabledFeature
return s return s
}) })
}) })
t.Run("BoundServiceAccountTokenVolume enabled", func(t *testing.T) { t.Run("BoundServiceAccountTokenVolume enabled", func(t *testing.T) {
f(t, func(s *serviceAccount) *serviceAccount { f(t, func(s *Plugin) *Plugin {
s.featureGate = deprecationEnabledFeature s.featureGate = deprecationEnabledFeature
return s return s
}) })

View File

@ -14,6 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// serviceaccount enforces all pods having an associated serviceaccount, // Package serviceaccount enforces all pods having an associated serviceaccount,
// and all containers mounting the API token for that serviceaccount at a known location // and all containers mounting the API token for that serviceaccount at a known location
package serviceaccount // import "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" package serviceaccount // import "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"