TRICKY: dependencies: pkg/quota

pull/6/head
Chao Xu 2016-11-18 13:23:02 -08:00
parent 98a82de6d1
commit f8b36bdd40
17 changed files with 142 additions and 76 deletions

View File

@ -30,11 +30,11 @@ go_library(
"//pkg/api/resource:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/release_1_5:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/kubelet/qos:go_default_library",
"//pkg/quota:go_default_library",
"//pkg/quota/generic:go_default_library",
"//pkg/quotainternal:go_default_library",
"//pkg/quotainternal/generic:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/validation/field:go_default_library",
@ -54,7 +54,7 @@ go_test(
"//pkg/api:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/quota:go_default_library",
"//pkg/client/clientset_generated/release_1_5/fake:go_default_library",
"//pkg/quotainternal:go_default_library",
],
)

View File

@ -19,7 +19,8 @@ package core
import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/runtime"
@ -38,7 +39,7 @@ func NewConfigMapEvaluator(kubeClient clientset.Interface) quota.Evaluator {
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: generic.ObjectCountConstraintsFunc(api.ResourceConfigMaps),
UsageFunc: generic.ObjectCountUsageFunc(api.ResourceConfigMaps),
ListFuncByNamespace: func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
ListFuncByNamespace: func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().ConfigMaps(namespace).List(options)
if err != nil {
return nil, err

View File

@ -24,7 +24,8 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
@ -37,7 +38,7 @@ func listPersistentVolumeClaimsByNamespaceFuncUsingClient(kubeClient clientset.I
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
// structured objects.
return func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
return func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().PersistentVolumeClaims(namespace).List(options)
if err != nil {
return nil, err
@ -75,13 +76,21 @@ func NewPersistentVolumeClaimEvaluator(kubeClient clientset.Interface, f informe
// PersistentVolumeClaimUsageFunc knows how to measure usage associated with persistent volume claims
func PersistentVolumeClaimUsageFunc(object runtime.Object) api.ResourceList {
pvc, ok := object.(*api.PersistentVolumeClaim)
if !ok {
return api.ResourceList{}
}
result := api.ResourceList{}
var found bool
var request resource.Quantity
switch t := object.(type) {
case *v1.PersistentVolumeClaim:
request, found = t.Spec.Resources.Requests[v1.ResourceStorage]
case *api.PersistentVolumeClaim:
request, found = t.Spec.Resources.Requests[api.ResourceStorage]
default:
panic(fmt.Sprintf("expect *api.PersistenVolumeClaim or *v1.PersistentVolumeClaim, got %v", t))
}
result[api.ResourcePersistentVolumeClaims] = resource.MustParse("1")
if request, found := pvc.Spec.Resources.Requests[api.ResourceStorage]; found {
if found {
result[api.ResourceRequestsStorage] = request
}
return result

View File

@ -22,7 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/fake"
"k8s.io/kubernetes/pkg/quota"
)

View File

@ -24,8 +24,9 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/validation"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/quota"
@ -40,7 +41,7 @@ func listPodsByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.
// TODO: ideally, we could pass dynamic client pool down into this code, and have one way of doing this.
// unfortunately, dynamic client works with Unstructured objects, and when we calculate Usage, we require
// structured objects.
return func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
return func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().Pods(namespace).List(options)
if err != nil {
return nil, err
@ -163,23 +164,32 @@ func podUsageHelper(requests api.ResourceList, limits api.ResourceList) api.Reso
return result
}
// PodUsageFunc knows how to measure usage associated with pods
func PodUsageFunc(object runtime.Object) api.ResourceList {
pod, ok := object.(*api.Pod)
if !ok {
return api.ResourceList{}
func toInternalPodOrDie(obj runtime.Object) *api.Pod {
pod := &api.Pod{}
switch t := obj.(type) {
case *v1.Pod:
if err := v1.Convert_v1_Pod_To_api_Pod(t, pod, nil); err != nil {
panic(err)
}
case *api.Pod:
pod = t
default:
panic(fmt.Sprintf("expect *api.Pod or *v1.Pod, got %v", t))
}
return pod
}
// PodUsageFunc knows how to measure usage associated with pods
func PodUsageFunc(obj runtime.Object) api.ResourceList {
pod := toInternalPodOrDie(obj)
// by convention, we do not quota pods that have reached an end-of-life state
if !QuotaPod(pod) {
return api.ResourceList{}
}
// TODO: fix this when we have pod level cgroups
// when we have pod level cgroups, we can just read pod level requests/limits
requests := api.ResourceList{}
limits := api.ResourceList{}
// TODO: fix this when we have pod level cgroups
// when we have pod level cgroups, we can just read pod level requests/limits
for i := range pod.Spec.Containers {
requests = quota.Add(requests, pod.Spec.Containers[i].Resources.Requests)
limits = quota.Add(limits, pod.Spec.Containers[i].Resources.Limits)
@ -197,10 +207,7 @@ func PodUsageFunc(object runtime.Object) api.ResourceList {
// PodMatchesScopeFunc is a function that knows how to evaluate if a pod matches a scope
func PodMatchesScopeFunc(scope api.ResourceQuotaScope, object runtime.Object) bool {
pod, ok := object.(*api.Pod)
if !ok {
return false
}
pod := toInternalPodOrDie(object)
switch scope {
case api.ResourceQuotaScopeTerminating:
return isTerminating(pod)
@ -215,7 +222,7 @@ func PodMatchesScopeFunc(scope api.ResourceQuotaScope, object runtime.Object) bo
}
func isBestEffort(pod *api.Pod) bool {
return qos.GetPodQOS(pod) == qos.BestEffort
return qos.InternalGetPodQOS(pod) == qos.BestEffort
}
func isTerminating(pod *api.Pod) bool {
@ -228,7 +235,11 @@ func isTerminating(pod *api.Pod) bool {
// QuotaPod returns true if the pod is eligible to track against a quota
// if it's not in a terminal state according to its phase.
func QuotaPod(pod *api.Pod) bool {
// see GetPhase in kubelet.go for details on how it covers all restart policy conditions
// https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/kubelet.go#L3001
return !(api.PodFailed == pod.Status.Phase || api.PodSucceeded == pod.Status.Phase)
}
// QuotaV1Pod returns true if the pod is eligible to track against a quota
// if it's not in a terminal state according to its phase.
func QuotaV1Pod(pod *v1.Pod) bool {
return !(v1.PodFailed == pod.Status.Phase || v1.PodSucceeded == pod.Status.Phase)
}

View File

@ -21,7 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/fake"
"k8s.io/kubernetes/pkg/quota"
)

View File

@ -18,7 +18,7 @@ package core
import (
"k8s.io/kubernetes/pkg/api/unversioned"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"

View File

@ -19,7 +19,8 @@ package core
import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/runtime"
@ -38,7 +39,7 @@ func NewReplicationControllerEvaluator(kubeClient clientset.Interface) quota.Eva
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: generic.ObjectCountConstraintsFunc(api.ResourceReplicationControllers),
UsageFunc: generic.ObjectCountUsageFunc(api.ResourceReplicationControllers),
ListFuncByNamespace: func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
ListFuncByNamespace: func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().ReplicationControllers(namespace).List(options)
if err != nil {
return nil, err

View File

@ -19,7 +19,8 @@ package core
import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/runtime"
@ -38,7 +39,7 @@ func NewResourceQuotaEvaluator(kubeClient clientset.Interface) quota.Evaluator {
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: generic.ObjectCountConstraintsFunc(api.ResourceQuotas),
UsageFunc: generic.ObjectCountUsageFunc(api.ResourceQuotas),
ListFuncByNamespace: func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
ListFuncByNamespace: func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().ResourceQuotas(namespace).List(options)
if err != nil {
return nil, err

View File

@ -19,7 +19,8 @@ package core
import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/runtime"
@ -38,7 +39,7 @@ func NewSecretEvaluator(kubeClient clientset.Interface) quota.Evaluator {
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: generic.ObjectCountConstraintsFunc(api.ResourceSecrets),
UsageFunc: generic.ObjectCountUsageFunc(api.ResourceSecrets),
ListFuncByNamespace: func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
ListFuncByNamespace: func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().Secrets(namespace).List(options)
if err != nil {
return nil, err

View File

@ -23,7 +23,8 @@ import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/runtime"
@ -48,7 +49,7 @@ func NewServiceEvaluator(kubeClient clientset.Interface) quota.Evaluator {
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: ServiceConstraintsFunc,
UsageFunc: ServiceUsageFunc,
ListFuncByNamespace: func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
ListFuncByNamespace: func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
itemList, err := kubeClient.Core().Services(namespace).List(options)
if err != nil {
return nil, err
@ -65,42 +66,54 @@ func NewServiceEvaluator(kubeClient clientset.Interface) quota.Evaluator {
// ServiceUsageFunc knows how to measure usage associated with services
func ServiceUsageFunc(object runtime.Object) api.ResourceList {
result := api.ResourceList{}
if service, ok := object.(*api.Service); ok {
// default service usage
result[api.ResourceServices] = resource.MustParse("1")
result[api.ResourceServicesLoadBalancers] = resource.MustParse("0")
result[api.ResourceServicesNodePorts] = resource.MustParse("0")
switch service.Spec.Type {
case api.ServiceTypeNodePort:
// node port services need to count node ports
value := resource.NewQuantity(int64(len(service.Spec.Ports)), resource.DecimalSI)
result[api.ResourceServicesNodePorts] = *value
case api.ServiceTypeLoadBalancer:
// load balancer services need to count load balancers
result[api.ResourceServicesLoadBalancers] = resource.MustParse("1")
}
var serviceType api.ServiceType
var ports int
switch t := object.(type) {
case *v1.Service:
serviceType = api.ServiceType(t.Spec.Type)
ports = len(t.Spec.Ports)
case *api.Service:
serviceType = t.Spec.Type
ports = len(t.Spec.Ports)
default:
panic(fmt.Sprintf("expect *api.Service or *v1.Service, got %v", t))
}
// default service usage
result[api.ResourceServices] = resource.MustParse("1")
result[api.ResourceServicesLoadBalancers] = resource.MustParse("0")
result[api.ResourceServicesNodePorts] = resource.MustParse("0")
switch serviceType {
case api.ServiceTypeNodePort:
// node port services need to count node ports
value := resource.NewQuantity(int64(ports), resource.DecimalSI)
result[api.ResourceServicesNodePorts] = *value
case api.ServiceTypeLoadBalancer:
// load balancer services need to count load balancers
result[api.ResourceServicesLoadBalancers] = resource.MustParse("1")
}
return result
}
// QuotaServiceType returns true if the service type is eligible to track against a quota
func QuotaServiceType(service *api.Service) bool {
func QuotaServiceType(service *v1.Service) bool {
switch service.Spec.Type {
case api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer:
case v1.ServiceTypeNodePort, v1.ServiceTypeLoadBalancer:
return true
}
return false
}
//GetQuotaServiceType returns ServiceType if the service type is eligible to track against a quota, nor return ""
func GetQuotaServiceType(service *api.Service) api.ServiceType {
func GetQuotaServiceType(service *v1.Service) v1.ServiceType {
switch service.Spec.Type {
case api.ServiceTypeNodePort:
return api.ServiceTypeNodePort
case api.ServiceTypeLoadBalancer:
return api.ServiceTypeLoadBalancer
case v1.ServiceTypeNodePort:
return v1.ServiceTypeNodePort
case v1.ServiceTypeLoadBalancer:
return v1.ServiceTypeLoadBalancer
}
return api.ServiceType("")
return v1.ServiceType("")
}
// ServiceConstraintsFunc verifies that all required resources are captured in service usage.

View File

@ -21,7 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/fake"
"k8s.io/kubernetes/pkg/quota"
)

View File

@ -24,7 +24,7 @@ go_library(
"//pkg/api/unversioned:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/quota:go_default_library",
"//pkg/quotainternal:go_default_library",
"//pkg/runtime:go_default_library",
],
)

View File

@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/quota"
@ -31,12 +32,16 @@ import (
// ListResourceUsingInformerFunc returns a listing function based on the shared informer factory for the specified resource.
func ListResourceUsingInformerFunc(f informers.SharedInformerFactory, groupResource unversioned.GroupResource) ListFuncByNamespace {
return func(namespace string, options api.ListOptions) ([]runtime.Object, error) {
return func(namespace string, options v1.ListOptions) ([]runtime.Object, error) {
labelSelector, err := labels.Parse(options.LabelSelector)
if err != nil {
return nil, err
}
informer, err := f.ForResource(groupResource)
if err != nil {
return nil, err
}
return informer.Lister().ByNamespace(namespace).List(options.LabelSelector)
return informer.Lister().ByNamespace(namespace).List(labelSelector)
}
}
@ -47,7 +52,7 @@ type ConstraintsFunc func(required []api.ResourceName, item runtime.Object) erro
type GetFuncByNamespace func(namespace, name string) (runtime.Object, error)
// ListFuncByNamespace knows how to list resources in a namespace
type ListFuncByNamespace func(namespace string, options api.ListOptions) ([]runtime.Object, error)
type ListFuncByNamespace func(namespace string, options v1.ListOptions) ([]runtime.Object, error)
// MatchesScopeFunc knows how to evaluate if an object matches a scope
type MatchesScopeFunc func(scope api.ResourceQuotaScope, object runtime.Object) bool
@ -183,8 +188,8 @@ func (g *GenericEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.Us
for _, resourceName := range g.MatchedResourceNames {
result.Used[resourceName] = resource.MustParse("0")
}
items, err := g.ListFuncByNamespace(options.Namespace, api.ListOptions{
LabelSelector: labels.Everything(),
items, err := g.ListFuncByNamespace(options.Namespace, v1.ListOptions{
LabelSelector: labels.Everything().String(),
})
if err != nil {
return result, fmt.Errorf("%s: Failed to list %v: %v", g.Name, g.GroupKind(), err)

View File

@ -15,9 +15,9 @@ go_library(
srcs = ["registry.go"],
tags = ["automanaged"],
deps = [
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/release_1_5:go_default_library",
"//pkg/controller/informers:go_default_library",
"//pkg/quota:go_default_library",
"//pkg/quota/evaluator/core:go_default_library",
"//pkg/quotainternal:go_default_library",
"//pkg/quotainternal/evaluator/core:go_default_library",
],
)

View File

@ -17,7 +17,7 @@ limitations under the License.
package install
import (
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/evaluator/core"

View File

@ -19,6 +19,7 @@ package quota
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/util/sets"
)
@ -45,6 +46,29 @@ func Equals(a api.ResourceList, b api.ResourceList) bool {
return true
}
// V1Equals returns true if the two lists are equivalent
func V1Equals(a v1.ResourceList, b v1.ResourceList) bool {
for key, value1 := range a {
value2, found := b[key]
if !found {
return false
}
if value1.Cmp(value2) != 0 {
return false
}
}
for key, value1 := range b {
value2, found := a[key]
if !found {
return false
}
if value1.Cmp(value2) != 0 {
return false
}
}
return true
}
// LessThanOrEqual returns true if a < b for each key in b
// If false, it returns the keys in a that exceeded b
func LessThanOrEqual(a api.ResourceList, b api.ResourceList) (bool, []api.ResourceName) {