mirror of https://github.com/k3s-io/k3s
Merge pull request #69941 from miguelbernadi/fix-golint-issues-68026
Fix golint issues in plugin/pkg/admissionk3s-v1.15.3
commit
b8eecd671d
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,12 @@ 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 = localSchemeBuilder.AddToScheme
|
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||||
|
AddToScheme = localSchemeBuilder.AddToScheme
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -40,7 +40,8 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
annotationPrefix = "podpreset.admission.kubernetes.io"
|
annotationPrefix = "podpreset.admission.kubernetes.io"
|
||||||
PluginName = "PodPreset"
|
// PluginName is a string with the name of the plugin
|
||||||
|
PluginName = "PodPreset"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register registers a plugin
|
// Register registers a plugin
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = [
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,12 @@ 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 = localSchemeBuilder.AddToScheme
|
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||||
|
AddToScheme = localSchemeBuilder.AddToScheme
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -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"
|
|
@ -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)
|
||||||
|
|
|
@ -22,8 +22,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// SchemeBuilder is a pointer used to call AddToScheme
|
||||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||||
AddToScheme = SchemeBuilder.AddToScheme
|
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||||
|
AddToScheme = SchemeBuilder.AddToScheme
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupName is the group name use in this package
|
// GroupName is the group name use in this package
|
||||||
|
|
|
@ -30,9 +30,12 @@ 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 = localSchemeBuilder.AddToScheme
|
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||||
|
AddToScheme = localSchemeBuilder.AddToScheme
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -30,9 +30,12 @@ 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 = localSchemeBuilder.AddToScheme
|
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||||
|
AddToScheme = localSchemeBuilder.AddToScheme
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue