mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 10:43:56 +00:00
let resourcequota evaluator handle uninitialid pod and pvc
This commit is contained in:
parent
e2de110e26
commit
4928c8d1bf
@ -25,6 +25,7 @@ go_library(
|
|||||||
"//pkg/api/helper/qos:go_default_library",
|
"//pkg/api/helper/qos:go_default_library",
|
||||||
"//pkg/api/v1:go_default_library",
|
"//pkg/api/v1:go_default_library",
|
||||||
"//pkg/api/validation:go_default_library",
|
"//pkg/api/validation:go_default_library",
|
||||||
|
"//pkg/kubeapiserver/admission/util:go_default_library",
|
||||||
"//pkg/quota:go_default_library",
|
"//pkg/quota:go_default_library",
|
||||||
"//pkg/quota/generic:go_default_library",
|
"//pkg/quota/generic:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/helper"
|
"k8s.io/kubernetes/pkg/api/helper"
|
||||||
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
|
||||||
"k8s.io/kubernetes/pkg/quota"
|
"k8s.io/kubernetes/pkg/quota"
|
||||||
"k8s.io/kubernetes/pkg/quota/generic"
|
"k8s.io/kubernetes/pkg/quota/generic"
|
||||||
)
|
)
|
||||||
@ -141,8 +142,18 @@ func (p *pvcEvaluator) GroupKind() schema.GroupKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles returns true if the evaluator should handle the specified operation.
|
// Handles returns true if the evaluator should handle the specified operation.
|
||||||
func (p *pvcEvaluator) Handles(operation admission.Operation) bool {
|
func (p *pvcEvaluator) Handles(a admission.Attributes) bool {
|
||||||
return admission.Create == operation
|
op := a.GetOperation()
|
||||||
|
if op == admission.Create {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
updateUninitialized, err := util.IsUpdatingUninitializedObject(a)
|
||||||
|
if err != nil {
|
||||||
|
// fail closed, will try to give an evaluation.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// only uninitialized pvc might be updated.
|
||||||
|
return updateUninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches returns true if the evaluator matches the specified quota with the provided input item
|
// Matches returns true if the evaluator matches the specified quota with the provided input item
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/helper/qos"
|
"k8s.io/kubernetes/pkg/api/helper/qos"
|
||||||
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/api/validation"
|
"k8s.io/kubernetes/pkg/api/validation"
|
||||||
|
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
|
||||||
"k8s.io/kubernetes/pkg/quota"
|
"k8s.io/kubernetes/pkg/quota"
|
||||||
"k8s.io/kubernetes/pkg/quota/generic"
|
"k8s.io/kubernetes/pkg/quota/generic"
|
||||||
)
|
)
|
||||||
@ -131,10 +132,19 @@ func (p *podEvaluator) GroupKind() schema.GroupKind {
|
|||||||
return api.Kind("Pod")
|
return api.Kind("Pod")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handles returns true of the evaluator should handle the specified operation.
|
// Handles returns true of the evaluator should handle the specified attributes.
|
||||||
func (p *podEvaluator) Handles(operation admission.Operation) bool {
|
func (p *podEvaluator) Handles(a admission.Attributes) bool {
|
||||||
// TODO: update this if/when pods support resizing resource requirements.
|
op := a.GetOperation()
|
||||||
return admission.Create == operation
|
if op == admission.Create {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
updateUninitialized, err := util.IsUpdatingUninitializedObject(a)
|
||||||
|
if err != nil {
|
||||||
|
// fail closed, will try to give an evaluation.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// only uninitialized pods might be updated.
|
||||||
|
return updateUninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches returns true if the evaluator matches the specified quota with the provided input item
|
// Matches returns true if the evaluator matches the specified quota with the provided input item
|
||||||
|
@ -108,7 +108,8 @@ func (p *serviceEvaluator) GroupKind() schema.GroupKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles returns true of the evaluator should handle the specified operation.
|
// Handles returns true of the evaluator should handle the specified operation.
|
||||||
func (p *serviceEvaluator) Handles(operation admission.Operation) bool {
|
func (p *serviceEvaluator) Handles(a admission.Attributes) bool {
|
||||||
|
operation := a.GetOperation()
|
||||||
// We handle create and update because a service type can change.
|
// We handle create and update because a service type can change.
|
||||||
return admission.Create == operation || admission.Update == operation
|
return admission.Create == operation || admission.Update == operation
|
||||||
}
|
}
|
||||||
|
@ -150,8 +150,9 @@ func (o *ObjectCountEvaluator) GroupKind() schema.GroupKind {
|
|||||||
return o.InternalGroupKind
|
return o.InternalGroupKind
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handles returns true if the object count evaluator needs to track this operation.
|
// Handles returns true if the object count evaluator needs to track this attributes.
|
||||||
func (o *ObjectCountEvaluator) Handles(operation admission.Operation) bool {
|
func (o *ObjectCountEvaluator) Handles(a admission.Attributes) bool {
|
||||||
|
operation := a.GetOperation()
|
||||||
return operation == admission.Create || (o.AllowCreateOnUpdate && operation == admission.Update)
|
return operation == admission.Create || (o.AllowCreateOnUpdate && operation == admission.Update)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ type Evaluator interface {
|
|||||||
Constraints(required []api.ResourceName, item runtime.Object) error
|
Constraints(required []api.ResourceName, item runtime.Object) error
|
||||||
// GroupKind returns the groupKind that this object knows how to evaluate
|
// GroupKind returns the groupKind that this object knows how to evaluate
|
||||||
GroupKind() schema.GroupKind
|
GroupKind() schema.GroupKind
|
||||||
// Handles determines if quota could be impacted by the specified operation.
|
// Handles determines if quota could be impacted by the specified attribute.
|
||||||
// If true, admission control must perform quota processing for the operation, otherwise it is safe to ignore quota.
|
// If true, admission control must perform quota processing for the operation, otherwise it is safe to ignore quota.
|
||||||
Handles(operation admission.Operation) bool
|
Handles(operation admission.Attributes) bool
|
||||||
// Matches returns true if the specified quota matches the input item
|
// Matches returns true if the specified quota matches the input item
|
||||||
Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error)
|
Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error)
|
||||||
// MatchingResources takes the input specified list of resources and returns the set of resources evaluator matches.
|
// MatchingResources takes the input specified list of resources and returns the set of resources evaluator matches.
|
||||||
|
@ -375,8 +375,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
|
|||||||
return quotas, nil
|
return quotas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
op := a.GetOperation()
|
if !evaluator.Handles(a) {
|
||||||
if !evaluator.Handles(op) {
|
|
||||||
return quotas, nil
|
return quotas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +462,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
|
|||||||
return nil, admission.NewForbidden(a, fmt.Errorf("quota usage is negative for resource(s): %s", prettyPrintResourceNames(negativeUsage)))
|
return nil, admission.NewForbidden(a, fmt.Errorf("quota usage is negative for resource(s): %s", prettyPrintResourceNames(negativeUsage)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if admission.Update == op {
|
if admission.Update == a.GetOperation() {
|
||||||
prevItem := a.GetOldObject()
|
prevItem := a.GetOldObject()
|
||||||
if prevItem == nil {
|
if prevItem == nil {
|
||||||
return nil, admission.NewForbidden(a, fmt.Errorf("unable to get previous usage since prior version of object was not found"))
|
return nil, admission.NewForbidden(a, fmt.Errorf("unable to get previous usage since prior version of object was not found"))
|
||||||
@ -529,8 +528,7 @@ func (e *quotaEvaluator) Evaluate(a admission.Attributes) error {
|
|||||||
}
|
}
|
||||||
// for this kind, check if the operation could mutate any quota resources
|
// for this kind, check if the operation could mutate any quota resources
|
||||||
// if no resources tracked by quota are impacted, then just return
|
// if no resources tracked by quota are impacted, then just return
|
||||||
op := a.GetOperation()
|
if !evaluator.Handles(a) {
|
||||||
if !evaluator.Handles(op) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +147,85 @@ var _ = framework.KubeDescribe("ResourceQuota", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should create a ResourceQuota and capture the life of an uninitialized pod.", func() {
|
||||||
|
// TODO: uncomment the test when #50344 is merged.
|
||||||
|
// By("Creating a ResourceQuota")
|
||||||
|
// quotaName := "test-quota"
|
||||||
|
// resourceQuota := newTestResourceQuota(quotaName)
|
||||||
|
// resourceQuota, err := createResourceQuota(f.ClientSet, f.Namespace.Name, resourceQuota)
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// By("Ensuring resource quota status is calculated")
|
||||||
|
// usedResources := v1.ResourceList{}
|
||||||
|
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
|
||||||
|
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// By("Creating an uninitialized Pod that fits quota")
|
||||||
|
// podName := "test-pod"
|
||||||
|
// requests := v1.ResourceList{}
|
||||||
|
// requests[v1.ResourceCPU] = resource.MustParse("500m")
|
||||||
|
// requests[v1.ResourceMemory] = resource.MustParse("252Mi")
|
||||||
|
// pod := newTestPodForQuota(f, podName, requests, v1.ResourceList{})
|
||||||
|
// pod.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "unhandled"}}}
|
||||||
|
// _, err = f.ClientSet.Core().Pods(f.Namespace.Name).Create(pod)
|
||||||
|
// // because no one is handling the initializer, server will return a 504 timeout
|
||||||
|
// if err != nil && !errors.IsTimeout(err) {
|
||||||
|
// framework.Failf("expect err to be timeout error, got %v", err)
|
||||||
|
// }
|
||||||
|
// podToUpdate, err := f.ClientSet.Core().Pods(f.Namespace.Name).Get(podName, metav1.GetOptions{})
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// By("Ensuring ResourceQuota status captures the pod usage")
|
||||||
|
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
|
||||||
|
// usedResources[v1.ResourcePods] = resource.MustParse("1")
|
||||||
|
// usedResources[v1.ResourceCPU] = requests[v1.ResourceCPU]
|
||||||
|
// usedResources[v1.ResourceMemory] = requests[v1.ResourceMemory]
|
||||||
|
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// By("Not allowing an uninitialized pod to be created that exceeds remaining quota")
|
||||||
|
// requests = v1.ResourceList{}
|
||||||
|
// requests[v1.ResourceCPU] = resource.MustParse("600m")
|
||||||
|
// requests[v1.ResourceMemory] = resource.MustParse("100Mi")
|
||||||
|
// pod = newTestPodForQuota(f, "fail-pod", requests, v1.ResourceList{})
|
||||||
|
// pod.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "unhandled"}}}
|
||||||
|
// pod, err = f.ClientSet.Core().Pods(f.Namespace.Name).Create(pod)
|
||||||
|
// Expect(err).To(HaveOccurred())
|
||||||
|
// fmt.Println("CHAO: err=", err)
|
||||||
|
|
||||||
|
// By("Ensuring an uninitialized pod can update its resource requirements")
|
||||||
|
// // a pod cannot dynamically update its resource requirements.
|
||||||
|
// requests = v1.ResourceList{}
|
||||||
|
// requests[v1.ResourceCPU] = resource.MustParse("100m")
|
||||||
|
// requests[v1.ResourceMemory] = resource.MustParse("100Mi")
|
||||||
|
// podToUpdate.Spec.Containers[0].Resources.Requests = requests
|
||||||
|
// _, err = f.ClientSet.Core().Pods(f.Namespace.Name).Update(podToUpdate)
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// By("Ensuring attempts to update pod resource requirements did change quota usage")
|
||||||
|
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
|
||||||
|
// usedResources[v1.ResourcePods] = resource.MustParse("1")
|
||||||
|
// usedResources[v1.ResourceCPU] = requests[v1.ResourceCPU]
|
||||||
|
// usedResources[v1.ResourceMemory] = requests[v1.ResourceMemory]
|
||||||
|
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// TODO: uncomment the test when the replenishment_controller uses the
|
||||||
|
// sharedInformer that list/watches uninitialized objects.
|
||||||
|
// By("Deleting the pod")
|
||||||
|
// err = f.ClientSet.Core().Pods(f.Namespace.Name).Delete(podName, metav1.NewDeleteOptions(0))
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
//
|
||||||
|
// By("Ensuring resource quota status released the pod usage")
|
||||||
|
// usedResources[v1.ResourceQuotas] = resource.MustParse("1")
|
||||||
|
// usedResources[v1.ResourcePods] = resource.MustParse("0")
|
||||||
|
// usedResources[v1.ResourceCPU] = resource.MustParse("0")
|
||||||
|
// usedResources[v1.ResourceMemory] = resource.MustParse("0")
|
||||||
|
// err = waitForResourceQuota(f.ClientSet, f.Namespace.Name, quotaName, usedResources)
|
||||||
|
// Expect(err).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
It("should create a ResourceQuota and capture the life of a pod.", func() {
|
It("should create a ResourceQuota and capture the life of a pod.", func() {
|
||||||
By("Creating a ResourceQuota")
|
By("Creating a ResourceQuota")
|
||||||
quotaName := "test-quota"
|
quotaName := "test-quota"
|
||||||
|
Loading…
Reference in New Issue
Block a user