|
|
|
@@ -44,7 +44,7 @@ func NewResourceQuota(client client.Interface) admission.Interface {
|
|
|
|
|
return "a{client: client}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var kindToResourceName = map[string]api.ResourceName{
|
|
|
|
|
var resourceToResourceName = map[string]api.ResourceName{
|
|
|
|
|
"pods": api.ResourcePods,
|
|
|
|
|
"services": api.ResourceServices,
|
|
|
|
|
"replicationControllers": api.ResourceReplicationControllers,
|
|
|
|
@@ -57,7 +57,7 @@ func (q *quota) Admit(a admission.Attributes) (err error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obj := a.GetObject()
|
|
|
|
|
kind := a.GetKind()
|
|
|
|
|
resource := a.GetResource()
|
|
|
|
|
name := "Unknown"
|
|
|
|
|
if obj != nil {
|
|
|
|
|
name, _ = meta.NewAccessor().Name(obj)
|
|
|
|
@@ -65,7 +65,7 @@ func (q *quota) Admit(a admission.Attributes) (err error) {
|
|
|
|
|
|
|
|
|
|
list, err := q.client.ResourceQuotas(a.GetNamespace()).List(labels.Everything())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return apierrors.NewForbidden(a.GetKind(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), kind))
|
|
|
|
|
return apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), resource))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(list.Items) == 0 {
|
|
|
|
@@ -90,7 +90,7 @@ func (q *quota) Admit(a admission.Attributes) (err error) {
|
|
|
|
|
usage.Status = quota.Status
|
|
|
|
|
err = q.client.ResourceQuotaUsages(usage.Namespace).Create(&usage)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return apierrors.NewForbidden(a.GetKind(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), a.GetKind()))
|
|
|
|
|
return apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), a.GetResource()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -102,7 +102,7 @@ func (q *quota) Admit(a admission.Attributes) (err error) {
|
|
|
|
|
// Return an error if the operation should not pass admission control
|
|
|
|
|
func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, client client.Interface) (bool, error) {
|
|
|
|
|
obj := a.GetObject()
|
|
|
|
|
kind := a.GetKind()
|
|
|
|
|
resourceName := a.GetResource()
|
|
|
|
|
name := "Unknown"
|
|
|
|
|
if obj != nil {
|
|
|
|
|
name, _ = meta.NewAccessor().Name(obj)
|
|
|
|
@@ -114,15 +114,15 @@ func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, cli
|
|
|
|
|
}
|
|
|
|
|
// handle max counts for each kind of resource (pods, services, replicationControllers, etc.)
|
|
|
|
|
if a.GetOperation() == "CREATE" {
|
|
|
|
|
resourceName := kindToResourceName[a.GetKind()]
|
|
|
|
|
resourceName := resourceToResourceName[a.GetResource()]
|
|
|
|
|
hard, hardFound := status.Hard[resourceName]
|
|
|
|
|
if hardFound {
|
|
|
|
|
used, usedFound := status.Used[resourceName]
|
|
|
|
|
if !usedFound {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, fmt.Errorf("Quota usage stats are not yet known, unable to admit resource until an accurate count is completed."))
|
|
|
|
|
return false, apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Quota usage stats are not yet known, unable to admit resource until an accurate count is completed."))
|
|
|
|
|
}
|
|
|
|
|
if used.Value() >= hard.Value() {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, fmt.Errorf("Limited to %s %s", hard.String(), kind))
|
|
|
|
|
return false, apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Limited to %s %s", hard.String(), a.GetResource()))
|
|
|
|
|
} else {
|
|
|
|
|
status.Used[resourceName] = *resource.NewQuantity(used.Value()+int64(1), resource.DecimalSI)
|
|
|
|
|
dirty = true
|
|
|
|
@@ -130,7 +130,7 @@ func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, cli
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// handle memory/cpu constraints, and any diff of usage based on memory/cpu on updates
|
|
|
|
|
if a.GetKind() == "pods" && (set[api.ResourceMemory] || set[api.ResourceCPU]) {
|
|
|
|
|
if a.GetResource() == "pods" && (set[api.ResourceMemory] || set[api.ResourceCPU]) {
|
|
|
|
|
pod := obj.(*api.Pod)
|
|
|
|
|
deltaCPU := resourcequota.PodCPU(pod)
|
|
|
|
|
deltaMemory := resourcequota.PodMemory(pod)
|
|
|
|
@@ -138,7 +138,7 @@ func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, cli
|
|
|
|
|
if a.GetOperation() == "UPDATE" {
|
|
|
|
|
oldPod, err := client.Pods(a.GetNamespace()).Get(pod.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, err)
|
|
|
|
|
return false, apierrors.NewForbidden(resourceName, name, err)
|
|
|
|
|
}
|
|
|
|
|
oldCPU := resourcequota.PodCPU(oldPod)
|
|
|
|
|
oldMemory := resourcequota.PodMemory(oldPod)
|
|
|
|
@@ -150,10 +150,10 @@ func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, cli
|
|
|
|
|
if hardMemFound {
|
|
|
|
|
used, usedFound := status.Used[api.ResourceMemory]
|
|
|
|
|
if !usedFound {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, fmt.Errorf("Quota usage stats are not yet known, unable to admit resource until an accurate count is completed."))
|
|
|
|
|
return false, apierrors.NewForbidden(resourceName, name, fmt.Errorf("Quota usage stats are not yet known, unable to admit resource until an accurate count is completed."))
|
|
|
|
|
}
|
|
|
|
|
if used.Value()+deltaMemory.Value() > hardMem.Value() {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, fmt.Errorf("Limited to %s memory", hardMem.String()))
|
|
|
|
|
return false, apierrors.NewForbidden(resourceName, name, fmt.Errorf("Limited to %s memory", hardMem.String()))
|
|
|
|
|
} else {
|
|
|
|
|
status.Used[api.ResourceMemory] = *resource.NewQuantity(used.Value()+deltaMemory.Value(), resource.DecimalSI)
|
|
|
|
|
dirty = true
|
|
|
|
@@ -163,10 +163,10 @@ func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, cli
|
|
|
|
|
if hardCPUFound {
|
|
|
|
|
used, usedFound := status.Used[api.ResourceCPU]
|
|
|
|
|
if !usedFound {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, fmt.Errorf("Quota usage stats are not yet known, unable to admit resource until an accurate count is completed."))
|
|
|
|
|
return false, apierrors.NewForbidden(resourceName, name, fmt.Errorf("Quota usage stats are not yet known, unable to admit resource until an accurate count is completed."))
|
|
|
|
|
}
|
|
|
|
|
if used.MilliValue()+deltaCPU.MilliValue() > hardCPU.MilliValue() {
|
|
|
|
|
return false, apierrors.NewForbidden(kind, name, fmt.Errorf("Limited to %s CPU", hardCPU.String()))
|
|
|
|
|
return false, apierrors.NewForbidden(resourceName, name, fmt.Errorf("Limited to %s CPU", hardCPU.String()))
|
|
|
|
|
} else {
|
|
|
|
|
status.Used[api.ResourceCPU] = *resource.NewMilliQuantity(used.MilliValue()+deltaCPU.MilliValue(), resource.DecimalSI)
|
|
|
|
|
dirty = true
|
|
|
|
|