2015-02-24 16:17:41 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2015 The Kubernetes Authors .
2015-02-24 16:17:41 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2015-10-10 03:58:57 +00:00
package resourcequota
2015-02-24 16:17:41 +00:00
import (
2017-10-27 15:07:53 +00:00
"fmt"
2016-02-22 16:15:09 +00:00
"strings"
2015-02-24 16:17:41 +00:00
"testing"
2017-06-22 18:24:23 +00:00
"k8s.io/api/core/v1"
2017-01-25 13:13:07 +00:00
"k8s.io/apimachinery/pkg/api/resource"
2017-01-17 03:38:19 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-10-27 15:07:53 +00:00
"k8s.io/apimachinery/pkg/runtime"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
2017-06-23 20:56:37 +00:00
"k8s.io/client-go/informers"
2017-10-27 15:07:53 +00:00
"k8s.io/client-go/kubernetes"
2017-06-23 20:56:37 +00:00
"k8s.io/client-go/kubernetes/fake"
2017-01-25 20:07:10 +00:00
core "k8s.io/client-go/testing"
2017-10-27 15:07:53 +00:00
"k8s.io/client-go/tools/cache"
2015-11-11 21:19:39 +00:00
"k8s.io/kubernetes/pkg/controller"
2018-08-27 13:49:26 +00:00
quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/v1/generic"
"k8s.io/kubernetes/pkg/quota/v1/install"
2015-02-24 16:17:41 +00:00
)
2016-11-18 20:50:17 +00:00
func getResourceList ( cpu , memory string ) v1 . ResourceList {
res := v1 . ResourceList { }
2015-02-24 16:17:41 +00:00
if cpu != "" {
2016-11-18 20:50:17 +00:00
res [ v1 . ResourceCPU ] = resource . MustParse ( cpu )
2015-02-24 16:17:41 +00:00
}
if memory != "" {
2016-11-18 20:50:17 +00:00
res [ v1 . ResourceMemory ] = resource . MustParse ( memory )
2015-02-24 16:17:41 +00:00
}
2015-08-13 14:19:27 +00:00
return res
}
2015-02-24 16:17:41 +00:00
2016-11-18 20:50:17 +00:00
func getResourceRequirements ( requests , limits v1 . ResourceList ) v1 . ResourceRequirements {
res := v1 . ResourceRequirements { }
2015-08-13 14:19:27 +00:00
res . Requests = requests
res . Limits = limits
2015-02-24 16:17:41 +00:00
return res
}
2017-10-27 15:07:53 +00:00
func mockDiscoveryFunc ( ) ( [ ] * metav1 . APIResourceList , error ) {
return [ ] * metav1 . APIResourceList { } , nil
}
func mockListerForResourceFunc ( listersForResource map [ schema . GroupVersionResource ] cache . GenericLister ) quota . ListerForResourceFunc {
return func ( gvr schema . GroupVersionResource ) ( cache . GenericLister , error ) {
lister , found := listersForResource [ gvr ]
if ! found {
return nil , fmt . Errorf ( "no lister found for resource" )
}
return lister , nil
2015-02-24 16:17:41 +00:00
}
2017-10-27 15:07:53 +00:00
}
func newGenericLister ( groupResource schema . GroupResource , items [ ] runtime . Object ) cache . GenericLister {
store := cache . NewIndexer ( cache . MetaNamespaceKeyFunc , cache . Indexers { "namespace" : cache . MetaNamespaceIndexFunc } )
for _ , item := range items {
store . Add ( item )
2015-02-24 16:17:41 +00:00
}
2017-10-27 15:07:53 +00:00
return cache . NewGenericLister ( store , groupResource )
}
type quotaController struct {
* ResourceQuotaController
stop chan struct { }
}
2015-02-24 16:17:41 +00:00
2017-10-27 15:07:53 +00:00
func setupQuotaController ( t * testing . T , kubeClient kubernetes . Interface , lister quota . ListerForResourceFunc ) quotaController {
2017-02-10 16:25:54 +00:00
informerFactory := informers . NewSharedInformerFactory ( kubeClient , controller . NoResyncPeriodFunc ( ) )
2017-10-27 15:07:53 +00:00
quotaConfiguration := install . NewQuotaConfigurationForControllers ( lister )
alwaysStarted := make ( chan struct { } )
close ( alwaysStarted )
2016-02-22 16:15:09 +00:00
resourceQuotaControllerOptions := & ResourceQuotaControllerOptions {
2017-11-12 11:00:21 +00:00
QuotaClient : kubeClient . CoreV1 ( ) ,
2017-10-27 15:07:53 +00:00
ResourceQuotaInformer : informerFactory . Core ( ) . V1 ( ) . ResourceQuotas ( ) ,
ResyncPeriod : controller . NoResyncPeriodFunc ,
2016-03-07 07:20:32 +00:00
ReplenishmentResyncPeriod : controller . NoResyncPeriodFunc ,
2017-10-27 15:07:53 +00:00
IgnoredResourcesFunc : quotaConfiguration . IgnoredResources ,
DiscoveryFunc : mockDiscoveryFunc ,
Registry : generic . NewRegistry ( quotaConfiguration . Evaluators ( ) ) ,
InformersStarted : alwaysStarted ,
2016-02-22 16:15:09 +00:00
}
2017-10-27 15:07:53 +00:00
qc , err := NewResourceQuotaController ( resourceQuotaControllerOptions )
2015-02-24 16:17:41 +00:00
if err != nil {
2017-10-27 15:07:53 +00:00
t . Fatal ( err )
2015-02-24 16:17:41 +00:00
}
2017-10-27 15:07:53 +00:00
stop := make ( chan struct { } )
go informerFactory . Start ( stop )
return quotaController { qc , stop }
2015-02-24 16:17:41 +00:00
}
2015-04-16 21:46:27 +00:00
2017-10-27 15:07:53 +00:00
func newTestPods ( ) [ ] runtime . Object {
return [ ] runtime . Object {
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-running" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodRunning } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "100m" , "1Gi" ) , getResourceList ( "" , "" ) ) } } ,
2015-04-16 21:46:27 +00:00
} ,
} ,
2017-10-27 15:07:53 +00:00
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-running-2" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodRunning } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "100m" , "1Gi" ) , getResourceList ( "" , "" ) ) } } ,
2015-04-16 21:46:27 +00:00
} ,
} ,
2017-10-27 15:07:53 +00:00
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-failed" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodFailed } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "100m" , "1Gi" ) , getResourceList ( "" , "" ) ) } } ,
2015-04-16 21:46:27 +00:00
} ,
2016-02-22 16:15:09 +00:00
} ,
}
2015-04-16 21:46:27 +00:00
}
2017-10-27 15:07:53 +00:00
2018-07-18 19:26:34 +00:00
func newBestEffortTestPods ( ) [ ] runtime . Object {
return [ ] runtime . Object {
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-running" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodRunning } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "" , "" ) , getResourceList ( "" , "" ) ) } } ,
} ,
} ,
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-running-2" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodRunning } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "" , "" ) , getResourceList ( "" , "" ) ) } } ,
} ,
} ,
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-failed" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodFailed } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "100m" , "1Gi" ) , getResourceList ( "" , "" ) ) } } ,
} ,
} ,
}
}
func newTestPodsWithPriorityClasses ( ) [ ] runtime . Object {
return [ ] runtime . Object {
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-running" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodRunning } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "500m" , "50Gi" ) , getResourceList ( "" , "" ) ) } } ,
PriorityClassName : "high" ,
} ,
} ,
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-running-2" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodRunning } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "100m" , "1Gi" ) , getResourceList ( "" , "" ) ) } } ,
PriorityClassName : "low" ,
} ,
} ,
& v1 . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "pod-failed" , Namespace : "testing" } ,
Status : v1 . PodStatus { Phase : v1 . PodFailed } ,
Spec : v1 . PodSpec {
Volumes : [ ] v1 . Volume { { Name : "vol" } } ,
Containers : [ ] v1 . Container { { Name : "ctr" , Image : "image" , Resources : getResourceRequirements ( getResourceList ( "100m" , "1Gi" ) , getResourceList ( "" , "" ) ) } } ,
} ,
} ,
}
}
2017-10-27 15:07:53 +00:00
func TestSyncResourceQuota ( t * testing . T ) {
testCases := map [ string ] struct {
gvr schema . GroupVersionResource
items [ ] runtime . Object
quota v1 . ResourceQuota
status v1 . ResourceQuotaStatus
expectedActionSet sets . String
} {
2018-07-18 19:26:34 +00:00
"non-matching-best-effort-scoped-quota" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Scopes : [ ] v1 . ResourceQuotaScope { v1 . ResourceQuotaScopeBestEffort } ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
v1 . ResourceMemory : resource . MustParse ( "0" ) ,
v1 . ResourcePods : resource . MustParse ( "0" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPods ( ) ,
} ,
"matching-best-effort-scoped-quota" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Scopes : [ ] v1 . ResourceQuotaScope { v1 . ResourceQuotaScopeBestEffort } ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
v1 . ResourceMemory : resource . MustParse ( "0" ) ,
v1 . ResourcePods : resource . MustParse ( "2" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newBestEffortTestPods ( ) ,
} ,
"non-matching-priorityclass-scoped-quota-OpExists" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpExists } ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
v1 . ResourceMemory : resource . MustParse ( "0" ) ,
v1 . ResourcePods : resource . MustParse ( "0" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPods ( ) ,
} ,
"matching-priorityclass-scoped-quota-OpExists" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpExists } ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "600m" ) ,
v1 . ResourceMemory : resource . MustParse ( "51Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "2" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPodsWithPriorityClasses ( ) ,
} ,
"matching-priorityclass-scoped-quota-OpIn" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpIn ,
Values : [ ] string { "high" , "low" } ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "600m" ) ,
v1 . ResourceMemory : resource . MustParse ( "51Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "2" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPodsWithPriorityClasses ( ) ,
} ,
"matching-priorityclass-scoped-quota-OpIn-high" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpIn ,
Values : [ ] string { "high" } ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "500m" ) ,
v1 . ResourceMemory : resource . MustParse ( "50Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "1" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPodsWithPriorityClasses ( ) ,
} ,
"matching-priorityclass-scoped-quota-OpIn-low" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpIn ,
Values : [ ] string { "low" } ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "100m" ) ,
v1 . ResourceMemory : resource . MustParse ( "1Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "1" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPodsWithPriorityClasses ( ) ,
} ,
"matching-priorityclass-scoped-quota-OpNotIn-low" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpNotIn ,
Values : [ ] string { "high" } ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "100m" ) ,
v1 . ResourceMemory : resource . MustParse ( "1Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "1" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPodsWithPriorityClasses ( ) ,
} ,
"non-matching-priorityclass-scoped-quota-OpIn" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpIn ,
Values : [ ] string { "random" } ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
v1 . ResourceMemory : resource . MustParse ( "0" ) ,
v1 . ResourcePods : resource . MustParse ( "0" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPodsWithPriorityClasses ( ) ,
} ,
"non-matching-priorityclass-scoped-quota-OpNotIn" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpNotIn ,
Values : [ ] string { "random" } ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "200m" ) ,
v1 . ResourceMemory : resource . MustParse ( "2Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "2" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPods ( ) ,
} ,
"matching-priorityclass-scoped-quota-OpDoesNotExist" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
ScopeSelector : & v1 . ScopeSelector {
MatchExpressions : [ ] v1 . ScopedResourceSelectorRequirement {
{
ScopeName : v1 . ResourceQuotaScopePriorityClass ,
Operator : v1 . ScopeSelectorOpDoesNotExist ,
} ,
} ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "200m" ) ,
v1 . ResourceMemory : resource . MustParse ( "2Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "2" ) ,
} ,
} ,
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPods ( ) ,
} ,
2017-10-27 15:07:53 +00:00
"pods" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta { Name : "quota" , Namespace : "testing" } ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
} ,
} ,
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
v1 . ResourceMemory : resource . MustParse ( "100Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "5" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "200m" ) ,
v1 . ResourceMemory : resource . MustParse ( "2Gi" ) ,
v1 . ResourcePods : resource . MustParse ( "2" ) ,
} ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : newTestPods ( ) ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
"quota-spec-hard-updated" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta {
Namespace : "default" ,
Name : "rq" ,
} ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
} ,
} ,
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "3" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
} ,
} ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
} ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
expectedActionSet : sets . NewString (
strings . Join ( [ ] string { "update" , "resourcequotas" , "status" } , "-" ) ,
) ,
items : [ ] runtime . Object { } ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
"quota-unchanged" : {
gvr : v1 . SchemeGroupVersion . WithResource ( "pods" ) ,
quota : v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta {
Namespace : "default" ,
Name : "rq" ,
} ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
} ,
} ,
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
} ,
} ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
} ,
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
} ,
2016-08-05 22:10:09 +00:00
} ,
2017-10-27 15:07:53 +00:00
expectedActionSet : sets . NewString ( ) ,
items : [ ] runtime . Object { } ,
2016-08-05 22:10:09 +00:00
} ,
}
2017-10-27 15:07:53 +00:00
for testName , testCase := range testCases {
kubeClient := fake . NewSimpleClientset ( & testCase . quota )
listersForResourceConfig := map [ schema . GroupVersionResource ] cache . GenericLister {
testCase . gvr : newGenericLister ( testCase . gvr . GroupResource ( ) , testCase . items ) ,
2016-08-05 22:10:09 +00:00
}
2017-10-27 15:07:53 +00:00
qc := setupQuotaController ( t , kubeClient , mockListerForResourceFunc ( listersForResourceConfig ) )
defer close ( qc . stop )
2016-08-05 22:10:09 +00:00
2017-10-27 15:07:53 +00:00
if err := qc . syncResourceQuota ( & testCase . quota ) ; err != nil {
t . Fatalf ( "test: %s, unexpected error: %v" , testName , err )
2016-08-05 22:10:09 +00:00
}
2017-10-27 15:07:53 +00:00
actionSet := sets . NewString ( )
for _ , action := range kubeClient . Actions ( ) {
actionSet . Insert ( strings . Join ( [ ] string { action . GetVerb ( ) , action . GetResource ( ) . Resource , action . GetSubresource ( ) } , "-" ) )
}
if ! actionSet . HasAll ( testCase . expectedActionSet . List ( ) ... ) {
t . Errorf ( "test: %s,\nExpected actions:\n%v\n but got:\n%v\nDifference:\n%v" , testName , testCase . expectedActionSet , actionSet , testCase . expectedActionSet . Difference ( actionSet ) )
2016-08-05 22:10:09 +00:00
}
2015-04-16 21:46:27 +00:00
2017-10-27 15:07:53 +00:00
lastActionIndex := len ( kubeClient . Actions ( ) ) - 1
usage := kubeClient . Actions ( ) [ lastActionIndex ] . ( core . UpdateAction ) . GetObject ( ) . ( * v1 . ResourceQuota )
2015-04-16 21:46:27 +00:00
2017-10-27 15:07:53 +00:00
// ensure usage is as expected
if len ( usage . Status . Hard ) != len ( testCase . status . Hard ) {
t . Errorf ( "test: %s, status hard lengths do not match" , testName )
}
if len ( usage . Status . Used ) != len ( testCase . status . Used ) {
t . Errorf ( "test: %s, status used lengths do not match" , testName )
}
for k , v := range testCase . status . Hard {
actual := usage . Status . Hard [ k ]
actualValue := actual . String ( )
expectedValue := v . String ( )
if expectedValue != actualValue {
t . Errorf ( "test: %s, Usage Hard: Key: %v, Expected: %v, Actual: %v" , testName , k , expectedValue , actualValue )
}
}
for k , v := range testCase . status . Used {
actual := usage . Status . Used [ k ]
actualValue := actual . String ( )
expectedValue := v . String ( )
if expectedValue != actualValue {
t . Errorf ( "test: %s, Usage Used: Key: %v, Expected: %v, Actual: %v" , testName , k , expectedValue , actualValue )
}
}
2015-04-17 20:59:54 +00:00
}
}
2016-07-18 19:48:37 +00:00
func TestAddQuota ( t * testing . T ) {
kubeClient := fake . NewSimpleClientset ( )
2017-10-27 15:07:53 +00:00
gvr := v1 . SchemeGroupVersion . WithResource ( "pods" )
listersForResourceConfig := map [ schema . GroupVersionResource ] cache . GenericLister {
gvr : newGenericLister ( gvr . GroupResource ( ) , newTestPods ( ) ) ,
2016-07-18 19:48:37 +00:00
}
2017-10-27 15:07:53 +00:00
qc := setupQuotaController ( t , kubeClient , mockListerForResourceFunc ( listersForResourceConfig ) )
defer close ( qc . stop )
2016-07-18 19:48:37 +00:00
testCases := [ ] struct {
2017-10-27 15:07:53 +00:00
name string
2016-11-18 20:50:17 +00:00
quota * v1 . ResourceQuota
2016-07-18 19:48:37 +00:00
expectedPriority bool
} {
{
name : "no status" ,
expectedPriority : true ,
2016-11-18 20:50:17 +00:00
quota : & v1 . ResourceQuota {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-18 19:48:37 +00:00
Namespace : "default" ,
Name : "rq" ,
} ,
2016-11-18 20:50:17 +00:00
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
} ,
} ,
{
name : "status, no usage" ,
expectedPriority : true ,
2016-11-18 20:50:17 +00:00
quota : & v1 . ResourceQuota {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-18 19:48:37 +00:00
Namespace : "default" ,
Name : "rq" ,
} ,
2016-11-18 20:50:17 +00:00
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
} ,
} ,
2018-01-30 01:29:22 +00:00
{
name : "status, no usage(to validate it works for extended resources)" ,
expectedPriority : true ,
quota : & v1 . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta {
Namespace : "default" ,
Name : "rq" ,
} ,
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
"requests.example/foobars.example.com" : resource . MustParse ( "4" ) ,
} ,
} ,
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
"requests.example/foobars.example.com" : resource . MustParse ( "4" ) ,
} ,
} ,
} ,
} ,
2016-07-18 19:48:37 +00:00
{
name : "status, mismatch" ,
expectedPriority : true ,
2016-11-18 20:50:17 +00:00
quota : & v1 . ResourceQuota {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-18 19:48:37 +00:00
Namespace : "default" ,
Name : "rq" ,
} ,
2016-11-18 20:50:17 +00:00
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "6" ) ,
2016-07-18 19:48:37 +00:00
} ,
2016-11-18 20:50:17 +00:00
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
} ,
} ,
{
2017-10-27 15:07:53 +00:00
name : "status, missing usage, but don't care (no informer)" ,
2016-07-18 19:48:37 +00:00
expectedPriority : false ,
2016-11-18 20:50:17 +00:00
quota : & v1 . ResourceQuota {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-18 19:48:37 +00:00
Namespace : "default" ,
Name : "rq" ,
} ,
2016-11-18 20:50:17 +00:00
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
2018-01-30 01:29:22 +00:00
"foobars.example.com" : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
2018-01-30 01:29:22 +00:00
"foobars.example.com" : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
} ,
} ,
{
name : "ready" ,
expectedPriority : false ,
2016-11-18 20:50:17 +00:00
quota : & v1 . ResourceQuota {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-18 19:48:37 +00:00
Namespace : "default" ,
Name : "rq" ,
} ,
2016-11-18 20:50:17 +00:00
Spec : v1 . ResourceQuotaSpec {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
Status : v1 . ResourceQuotaStatus {
Hard : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "4" ) ,
2016-07-18 19:48:37 +00:00
} ,
2016-11-18 20:50:17 +00:00
Used : v1 . ResourceList {
v1 . ResourceCPU : resource . MustParse ( "0" ) ,
2016-07-18 19:48:37 +00:00
} ,
} ,
} ,
} ,
}
for _ , tc := range testCases {
2017-10-27 15:07:53 +00:00
qc . addQuota ( tc . quota )
2016-07-18 19:48:37 +00:00
if tc . expectedPriority {
2017-10-27 15:07:53 +00:00
if e , a := 1 , qc . missingUsageQueue . Len ( ) ; e != a {
2016-07-18 19:48:37 +00:00
t . Errorf ( "%s: expected %v, got %v" , tc . name , e , a )
}
2017-10-27 15:07:53 +00:00
if e , a := 0 , qc . queue . Len ( ) ; e != a {
2016-07-18 19:48:37 +00:00
t . Errorf ( "%s: expected %v, got %v" , tc . name , e , a )
}
} else {
2017-10-27 15:07:53 +00:00
if e , a := 0 , qc . missingUsageQueue . Len ( ) ; e != a {
2016-07-18 19:48:37 +00:00
t . Errorf ( "%s: expected %v, got %v" , tc . name , e , a )
}
2017-10-27 15:07:53 +00:00
if e , a := 1 , qc . queue . Len ( ) ; e != a {
2016-07-18 19:48:37 +00:00
t . Errorf ( "%s: expected %v, got %v" , tc . name , e , a )
}
}
2017-10-27 15:07:53 +00:00
for qc . missingUsageQueue . Len ( ) > 0 {
key , _ := qc . missingUsageQueue . Get ( )
qc . missingUsageQueue . Done ( key )
2016-07-18 19:48:37 +00:00
}
2017-10-27 15:07:53 +00:00
for qc . queue . Len ( ) > 0 {
key , _ := qc . queue . Get ( )
qc . queue . Done ( key )
2016-07-18 19:48:37 +00:00
}
}
}