mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
Merge pull request #55487 from deads2k/admission-16-limit
Automatic merge from submit-queue (batch tested with PRs 55283, 55461, 55288, 53970, 55487). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. split limitranger admission I think this is the last admission plugin that needs to be split into mutating and validating halves. @k8s-mirror-api-machinery-pr-reviews
This commit is contained in:
commit
d2153dff8e
@ -55,6 +55,8 @@ type Plugin struct {
|
||||
*admission.Handler
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = &Plugin{}
|
||||
|
||||
// NewDefaultTolerationSeconds creates a new instance of the DefaultTolerationSeconds admission controller
|
||||
func NewDefaultTolerationSeconds() *Plugin {
|
||||
return &Plugin{
|
||||
|
@ -64,6 +64,8 @@ type InitialResources struct {
|
||||
nsOnly bool
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = &InitialResources{}
|
||||
|
||||
func newInitialResources(source dataSource, percentile int64, nsOnly bool) *InitialResources {
|
||||
return &InitialResources{
|
||||
Handler: admission.NewHandler(admission.Create),
|
||||
|
@ -64,6 +64,10 @@ type LimitRanger struct {
|
||||
liveTTL time.Duration
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = &LimitRanger{}
|
||||
var _ admission.ValidationInterface = &LimitRanger{}
|
||||
var _ kubeapiserveradmission.WantsInternalKubeInformerFactory = &LimitRanger{}
|
||||
|
||||
type liveLookupEntry struct {
|
||||
expiry time.Time
|
||||
items []*api.LimitRange
|
||||
@ -87,6 +91,15 @@ func (l *LimitRanger) ValidateInitialization() error {
|
||||
|
||||
// Admit admits resources into cluster that do not violate any defined LimitRange in the namespace
|
||||
func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
|
||||
return l.runLimitFunc(a, l.actions.MutateLimit)
|
||||
}
|
||||
|
||||
// Validate admits resources into cluster that do not violate any defined LimitRange in the namespace
|
||||
func (l *LimitRanger) Validate(a admission.Attributes) (err error) {
|
||||
return l.runLimitFunc(a, l.actions.ValidateLimit)
|
||||
}
|
||||
|
||||
func (l *LimitRanger) runLimitFunc(a admission.Attributes, limitFn func(limitRange *api.LimitRange, kind string, obj runtime.Object) error) (err error) {
|
||||
if !l.actions.SupportsAttributes(a) {
|
||||
return nil
|
||||
}
|
||||
@ -100,9 +113,31 @@ func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
items, err := l.GetLimitRanges(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ensure it meets each prescribed min/max
|
||||
for i := range items {
|
||||
limitRange := items[i]
|
||||
|
||||
if !l.actions.SupportsLimit(limitRange) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = limitFn(limitRange, a.GetResource().Resource, a.GetObject())
|
||||
if err != nil {
|
||||
return admission.NewForbidden(a, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LimitRanger) GetLimitRanges(a admission.Attributes) ([]*api.LimitRange, error) {
|
||||
items, err := l.lister.LimitRanges(a.GetNamespace()).List(labels.Everything())
|
||||
if err != nil {
|
||||
return admission.NewForbidden(a, fmt.Errorf("unable to %s %v at this time because there was an error enforcing limit ranges", a.GetOperation(), a.GetResource()))
|
||||
return nil, admission.NewForbidden(a, fmt.Errorf("unable to %s %v at this time because there was an error enforcing limit ranges", a.GetOperation(), a.GetResource()))
|
||||
}
|
||||
|
||||
// if there are no items held in our indexer, check our live-lookup LRU, if that misses, do the live lookup to prime it.
|
||||
@ -116,7 +151,7 @@ func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
|
||||
// throttling - see #22422 for details.
|
||||
liveList, err := l.client.Core().LimitRanges(a.GetNamespace()).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return admission.NewForbidden(a, err)
|
||||
return nil, admission.NewForbidden(a, err)
|
||||
}
|
||||
newEntry := liveLookupEntry{expiry: time.Now().Add(l.liveTTL)}
|
||||
for i := range liveList.Items {
|
||||
@ -133,20 +168,7 @@ func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
|
||||
|
||||
}
|
||||
|
||||
// ensure it meets each prescribed min/max
|
||||
for i := range items {
|
||||
limitRange := items[i]
|
||||
|
||||
if !l.actions.SupportsLimit(limitRange) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = l.actions.Limit(limitRange, a.GetResource().Resource, a.GetObject())
|
||||
if err != nil {
|
||||
return admission.NewForbidden(a, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// NewLimitRanger returns an object that enforces limits based on the supplied limit function
|
||||
@ -399,12 +421,23 @@ var _ LimitRangerActions = &DefaultLimitRangerActions{}
|
||||
// Limit enforces resource requirements of incoming resources against enumerated constraints
|
||||
// on the LimitRange. It may modify the incoming object to apply default resource requirements
|
||||
// if not specified, and enumerated on the LimitRange
|
||||
func (d *DefaultLimitRangerActions) Limit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error {
|
||||
func (d *DefaultLimitRangerActions) MutateLimit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error {
|
||||
switch resourceName {
|
||||
case "pods":
|
||||
return PodLimitFunc(limitRange, obj.(*api.Pod))
|
||||
return PodMutateLimitFunc(limitRange, obj.(*api.Pod))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Limit enforces resource requirements of incoming resources against enumerated constraints
|
||||
// on the LimitRange. It may modify the incoming object to apply default resource requirements
|
||||
// if not specified, and enumerated on the LimitRange
|
||||
func (d *DefaultLimitRangerActions) ValidateLimit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error {
|
||||
switch resourceName {
|
||||
case "pods":
|
||||
return PodValidateLimitFunc(limitRange, obj.(*api.Pod))
|
||||
case "persistentvolumeclaims":
|
||||
return PersistentVolumeClaimLimitFunc(limitRange, obj.(*api.PersistentVolumeClaim))
|
||||
return PersistentVolumeClaimValidateLimitFunc(limitRange, obj.(*api.PersistentVolumeClaim))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -424,11 +457,11 @@ func (d *DefaultLimitRangerActions) SupportsLimit(limitRange *api.LimitRange) bo
|
||||
return true
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimLimitFunc enforces storage limits for PVCs.
|
||||
// PersistentVolumeClaimValidateLimitFunc enforces storage limits for PVCs.
|
||||
// Users request storage via pvc.Spec.Resources.Requests. Min/Max is enforced by an admin with LimitRange.
|
||||
// Claims will not be modified with default values because storage is a required part of pvc.Spec.
|
||||
// All storage enforced values *only* apply to pvc.Spec.Resources.Requests.
|
||||
func PersistentVolumeClaimLimitFunc(limitRange *api.LimitRange, pvc *api.PersistentVolumeClaim) error {
|
||||
func PersistentVolumeClaimValidateLimitFunc(limitRange *api.LimitRange, pvc *api.PersistentVolumeClaim) error {
|
||||
var errs []error
|
||||
for i := range limitRange.Spec.Limits {
|
||||
limit := limitRange.Spec.Limits[i]
|
||||
@ -452,14 +485,19 @@ func PersistentVolumeClaimLimitFunc(limitRange *api.LimitRange, pvc *api.Persist
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// PodLimitFunc enforces resource requirements enumerated by the pod against
|
||||
// PodMutateLimitFunc sets resource requirements enumerated by the pod against
|
||||
// the specified LimitRange. The pod may be modified to apply default resource
|
||||
// requirements if not specified, and enumerated on the LimitRange
|
||||
func PodLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
|
||||
var errs []error
|
||||
|
||||
func PodMutateLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
|
||||
defaultResources := defaultContainerResourceRequirements(limitRange)
|
||||
mergePodResourceRequirements(pod, &defaultResources)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PodValidateLimitFunc enforces resource requirements enumerated by the pod against
|
||||
// the specified LimitRange.
|
||||
func PodValidateLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
|
||||
var errs []error
|
||||
|
||||
for i := range limitRange.Spec.Limits {
|
||||
limit := limitRange.Spec.Limits[i]
|
||||
|
@ -430,7 +430,11 @@ func TestPodLimitFunc(t *testing.T) {
|
||||
}
|
||||
for i := range successCases {
|
||||
test := successCases[i]
|
||||
err := PodLimitFunc(&test.limitRange, &test.pod)
|
||||
err := PodMutateLimitFunc(&test.limitRange, &test.pod)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
|
||||
}
|
||||
err = PodValidateLimitFunc(&test.limitRange, &test.pod)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
|
||||
}
|
||||
@ -610,7 +614,11 @@ func TestPodLimitFunc(t *testing.T) {
|
||||
}
|
||||
for i := range errorCases {
|
||||
test := errorCases[i]
|
||||
err := PodLimitFunc(&test.limitRange, &test.pod)
|
||||
err := PodMutateLimitFunc(&test.limitRange, &test.pod)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
|
||||
}
|
||||
err = PodValidateLimitFunc(&test.limitRange, &test.pod)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for pod: %s", test.pod.Name)
|
||||
}
|
||||
@ -628,7 +636,7 @@ func getLocalStorageResourceList(ephemeralStorage string) api.ResourceList {
|
||||
func TestPodLimitFuncApplyDefault(t *testing.T) {
|
||||
limitRange := validLimitRange()
|
||||
testPod := validPodInit(validPod("foo", 1, getResourceRequirements(api.ResourceList{}, api.ResourceList{})), getResourceRequirements(api.ResourceList{}, api.ResourceList{}))
|
||||
err := PodLimitFunc(&limitRange, &testPod)
|
||||
err := PodMutateLimitFunc(&limitRange, &testPod)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for valid pod: %s, %v", testPod.Name, err)
|
||||
}
|
||||
@ -687,11 +695,15 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) {
|
||||
|
||||
testPod := validPod("testPod", 1, api.ResourceRequirements{})
|
||||
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error since the pod did not specify resource limits in its update call")
|
||||
}
|
||||
|
||||
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
|
||||
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
|
||||
if err != nil {
|
||||
t.Errorf("Should have ignored calls to any subresource of pod %v", err)
|
||||
}
|
||||
@ -709,11 +721,15 @@ func TestLimitRangerAdmitPod(t *testing.T) {
|
||||
|
||||
testPod := validPod("testPod", 1, api.ResourceRequirements{})
|
||||
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error since the pod did not specify resource limits in its update call")
|
||||
}
|
||||
|
||||
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
|
||||
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
|
||||
if err != nil {
|
||||
t.Errorf("Should have ignored calls to any subresource of pod %v", err)
|
||||
}
|
||||
@ -786,7 +802,7 @@ func TestPersistentVolumeClaimLimitFunc(t *testing.T) {
|
||||
}
|
||||
for i := range successCases {
|
||||
test := successCases[i]
|
||||
err := PersistentVolumeClaimLimitFunc(&test.limitRange, &test.pvc)
|
||||
err := PersistentVolumeClaimValidateLimitFunc(&test.limitRange, &test.pvc)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for pvc: %s, %v", test.pvc.Name, err)
|
||||
}
|
||||
@ -804,7 +820,7 @@ func TestPersistentVolumeClaimLimitFunc(t *testing.T) {
|
||||
}
|
||||
for i := range errorCases {
|
||||
test := errorCases[i]
|
||||
err := PersistentVolumeClaimLimitFunc(&test.limitRange, &test.pvc)
|
||||
err := PersistentVolumeClaimValidateLimitFunc(&test.limitRange, &test.pvc)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for pvc: %s", test.pvc.Name)
|
||||
}
|
||||
|
@ -23,8 +23,10 @@ import (
|
||||
)
|
||||
|
||||
type LimitRangerActions interface {
|
||||
// Limit is a pluggable function to enforce limits on the object.
|
||||
Limit(limitRange *api.LimitRange, kind string, obj runtime.Object) error
|
||||
// MutateLimit is a pluggable function to set limits on the object.
|
||||
MutateLimit(limitRange *api.LimitRange, kind string, obj runtime.Object) error
|
||||
// ValidateLimits is a pluggable function to enforce limits on the object.
|
||||
ValidateLimit(limitRange *api.LimitRange, kind string, obj runtime.Object) error
|
||||
// SupportsAttributes is a pluggable function to allow overridding what resources the limitranger
|
||||
// supports.
|
||||
SupportsAttributes(attr admission.Attributes) bool
|
||||
|
@ -46,6 +46,7 @@ type Provision struct {
|
||||
namespaceLister corelisters.NamespaceLister
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = &Provision{}
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&Provision{})
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&Provision{})
|
||||
|
||||
|
@ -52,6 +52,7 @@ type persistentVolumeLabel struct {
|
||||
gceCloudProvider *gce.GCECloud
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = &persistentVolumeLabel{}
|
||||
var _ kubeapiserveradmission.WantsCloudConfig = &persistentVolumeLabel{}
|
||||
|
||||
// NewPersistentVolumeLabel returns an admission.Interface implementation which adds labels to PersistentVolume CREATE requests,
|
||||
|
@ -59,6 +59,7 @@ type podPresetPlugin struct {
|
||||
lister settingslisters.PodPresetLister
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = &podPresetPlugin{}
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&podPresetPlugin{})
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&podPresetPlugin{})
|
||||
|
||||
|
@ -57,6 +57,8 @@ const (
|
||||
NSWLTolerations string = "scheduler.alpha.kubernetes.io/tolerationsWhitelist"
|
||||
)
|
||||
|
||||
var _ admission.MutationInterface = &podTolerationsPlugin{}
|
||||
var _ admission.ValidationInterface = &podTolerationsPlugin{}
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&podTolerationsPlugin{})
|
||||
|
||||
type podTolerationsPlugin struct {
|
||||
|
@ -54,6 +54,7 @@ type claimDefaulterPlugin struct {
|
||||
}
|
||||
|
||||
var _ admission.Interface = &claimDefaulterPlugin{}
|
||||
var _ admission.MutationInterface = &claimDefaulterPlugin{}
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&claimDefaulterPlugin{})
|
||||
|
||||
// newPlugin creates a new admission plugin.
|
||||
|
Loading…
Reference in New Issue
Block a user