Skip to content

Commit 99af708

Browse files
authored
Merge pull request kubernetes#76299 from mansi-a/resource-quota
Accept admission request if resource is being deleted
2 parents ca17f9d + 4466f97 commit 99af708

File tree

2 files changed

+113
-23
lines changed

2 files changed

+113
-23
lines changed

plugin/pkg/admission/resourcequota/admission_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,3 +2182,93 @@ func TestAdmitLimitedScopeWithCoverQuota(t *testing.T) {
21822182

21832183
}
21842184
}
2185+
2186+
// TestAdmitZeroDeltaUsageWithoutCoveringQuota verifies that resource quota is not required for zero delta requests.
2187+
func TestAdmitZeroDeltaUsageWithoutCoveringQuota(t *testing.T) {
2188+
2189+
kubeClient := fake.NewSimpleClientset()
2190+
stopCh := make(chan struct{})
2191+
defer close(stopCh)
2192+
2193+
informerFactory := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc())
2194+
quotaAccessor, _ := newQuotaAccessor()
2195+
quotaAccessor.client = kubeClient
2196+
quotaAccessor.lister = informerFactory.Core().V1().ResourceQuotas().Lister()
2197+
2198+
// disable services unless there is a covering quota.
2199+
config := &resourcequotaapi.Configuration{
2200+
LimitedResources: []resourcequotaapi.LimitedResource{
2201+
{
2202+
Resource: "services",
2203+
MatchContains: []string{"services"},
2204+
},
2205+
},
2206+
}
2207+
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
2208+
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
2209+
2210+
handler := &QuotaAdmission{
2211+
Handler: admission.NewHandler(admission.Create, admission.Update),
2212+
evaluator: evaluator,
2213+
}
2214+
2215+
existingService := &api.Service{
2216+
ObjectMeta: metav1.ObjectMeta{Name: "service", Namespace: "test", ResourceVersion: "1"},
2217+
Spec: api.ServiceSpec{Type: api.ServiceTypeLoadBalancer},
2218+
}
2219+
newService := &api.Service{
2220+
ObjectMeta: metav1.ObjectMeta{Name: "service", Namespace: "test"},
2221+
Spec: api.ServiceSpec{Type: api.ServiceTypeLoadBalancer},
2222+
}
2223+
2224+
err := handler.Validate(admission.NewAttributesRecord(newService, existingService, api.Kind("Service").WithVersion("version"), newService.Namespace, newService.Name, corev1.Resource("services").WithVersion("version"), "", admission.Update, false, nil), nil)
2225+
if err != nil {
2226+
t.Errorf("unexpected error: %v", err)
2227+
}
2228+
}
2229+
2230+
// TestAdmitRejectDeltaUsageWithoutCoveringQuota verifies that resource quota is required for non zero delta requests.
2231+
func TestAdmitRejectDeltaUsageWithoutCoveringQuota(t *testing.T) {
2232+
kubeClient := fake.NewSimpleClientset()
2233+
stopCh := make(chan struct{})
2234+
defer close(stopCh)
2235+
2236+
informerFactory := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc())
2237+
quotaAccessor, _ := newQuotaAccessor()
2238+
quotaAccessor.client = kubeClient
2239+
quotaAccessor.lister = informerFactory.Core().V1().ResourceQuotas().Lister()
2240+
2241+
// disable services unless there is a covering quota.
2242+
config := &resourcequotaapi.Configuration{
2243+
LimitedResources: []resourcequotaapi.LimitedResource{
2244+
{
2245+
Resource: "services",
2246+
MatchContains: []string{"services"},
2247+
},
2248+
},
2249+
}
2250+
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
2251+
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
2252+
2253+
handler := &QuotaAdmission{
2254+
Handler: admission.NewHandler(admission.Create, admission.Update),
2255+
evaluator: evaluator,
2256+
}
2257+
2258+
existingService := &api.Service{
2259+
ObjectMeta: metav1.ObjectMeta{Name: "service", Namespace: "test", ResourceVersion: "1"},
2260+
Spec: api.ServiceSpec{Type: api.ServiceTypeLoadBalancer},
2261+
}
2262+
newService := &api.Service{
2263+
ObjectMeta: metav1.ObjectMeta{Name: "service", Namespace: "test"},
2264+
Spec: api.ServiceSpec{
2265+
Type: api.ServiceTypeNodePort,
2266+
Ports: []api.ServicePort{{Port: 1234}},
2267+
},
2268+
}
2269+
2270+
err := handler.Validate(admission.NewAttributesRecord(newService, existingService, api.Kind("Service").WithVersion("version"), newService.Namespace, newService.Name, corev1.Resource("services").WithVersion("version"), "", admission.Update, false, nil), nil)
2271+
if err == nil {
2272+
t.Errorf("Expected an error for consuming a limited resource without quota.")
2273+
}
2274+
}

plugin/pkg/admission/resourcequota/controller.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -468,29 +468,6 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat
468468
restrictedResourcesSet.Insert(localRestrictedResourcesSet.List()...)
469469
}
470470

471-
// verify that for every resource that had limited by default consumption
472-
// enabled that there was a corresponding quota that covered its use.
473-
// if not, we reject the request.
474-
hasNoCoveringQuota := limitedResourceNamesSet.Difference(restrictedResourcesSet)
475-
if len(hasNoCoveringQuota) > 0 {
476-
return quotas, admission.NewForbidden(a, fmt.Errorf("insufficient quota to consume: %v", strings.Join(hasNoCoveringQuota.List(), ",")))
477-
}
478-
479-
// verify that for every scope that had limited access enabled
480-
// that there was a corresponding quota that covered it.
481-
// if not, we reject the request.
482-
scopesHasNoCoveringQuota, err := evaluator.UncoveredQuotaScopes(limitedScopes, restrictedScopes)
483-
if err != nil {
484-
return quotas, err
485-
}
486-
if len(scopesHasNoCoveringQuota) > 0 {
487-
return quotas, fmt.Errorf("insufficient quota to match these scopes: %v", scopesHasNoCoveringQuota)
488-
}
489-
490-
if len(interestingQuotaIndexes) == 0 {
491-
return quotas, nil
492-
}
493-
494471
// Usage of some resources cannot be counted in isolation. For example, when
495472
// the resource represents a number of unique references to external
496473
// resource. In such a case an evaluator needs to process other objects in
@@ -537,6 +514,29 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat
537514
return quotas, nil
538515
}
539516

517+
// verify that for every resource that had limited by default consumption
518+
// enabled that there was a corresponding quota that covered its use.
519+
// if not, we reject the request.
520+
hasNoCoveringQuota := limitedResourceNamesSet.Difference(restrictedResourcesSet)
521+
if len(hasNoCoveringQuota) > 0 {
522+
return quotas, admission.NewForbidden(a, fmt.Errorf("insufficient quota to consume: %v", strings.Join(hasNoCoveringQuota.List(), ",")))
523+
}
524+
525+
// verify that for every scope that had limited access enabled
526+
// that there was a corresponding quota that covered it.
527+
// if not, we reject the request.
528+
scopesHasNoCoveringQuota, err := evaluator.UncoveredQuotaScopes(limitedScopes, restrictedScopes)
529+
if err != nil {
530+
return quotas, err
531+
}
532+
if len(scopesHasNoCoveringQuota) > 0 {
533+
return quotas, fmt.Errorf("insufficient quota to match these scopes: %v", scopesHasNoCoveringQuota)
534+
}
535+
536+
if len(interestingQuotaIndexes) == 0 {
537+
return quotas, nil
538+
}
539+
540540
outQuotas, err := copyQuotas(quotas)
541541
if err != nil {
542542
return nil, err

0 commit comments

Comments
 (0)