Merge pull request #22183 from pmorie/config-quota

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-03-04 03:12:51 -08:00
commit 0e3469dce3
9 changed files with 123 additions and 0 deletions

View File

@ -237,6 +237,7 @@ func StartControllers(s *options.CMServer, kubeClient *client.Client, kubeconfig
api.Kind("ReplicationController"), api.Kind("ReplicationController"),
api.Kind("PersistentVolumeClaim"), api.Kind("PersistentVolumeClaim"),
api.Kind("Secret"), api.Kind("Secret"),
api.Kind("ConfigMap"),
} }
resourceQuotaControllerOptions := &resourcequotacontroller.ResourceQuotaControllerOptions{ resourceQuotaControllerOptions := &resourcequotacontroller.ResourceQuotaControllerOptions{
KubeClient: resourceQuotaControllerClient, KubeClient: resourceQuotaControllerClient,

View File

@ -140,6 +140,7 @@ var standardQuotaResources = sets.NewString(
string(ResourceReplicationControllers), string(ResourceReplicationControllers),
string(ResourceSecrets), string(ResourceSecrets),
string(ResourcePersistentVolumeClaims), string(ResourcePersistentVolumeClaims),
string(ResourceConfigMaps),
) )
// IsStandardQuotaResourceName returns true if the resource is known to // IsStandardQuotaResourceName returns true if the resource is known to
@ -160,6 +161,7 @@ var standardResources = sets.NewString(
string(ResourceServices), string(ResourceServices),
string(ResourceReplicationControllers), string(ResourceReplicationControllers),
string(ResourceSecrets), string(ResourceSecrets),
string(ResourceConfigMaps),
string(ResourcePersistentVolumeClaims), string(ResourcePersistentVolumeClaims),
string(ResourceStorage), string(ResourceStorage),
) )
@ -175,6 +177,7 @@ var integerResources = sets.NewString(
string(ResourceServices), string(ResourceServices),
string(ResourceReplicationControllers), string(ResourceReplicationControllers),
string(ResourceSecrets), string(ResourceSecrets),
string(ResourceConfigMaps),
string(ResourcePersistentVolumeClaims), string(ResourcePersistentVolumeClaims),
) )

View File

@ -2187,6 +2187,8 @@ const (
ResourceQuotas ResourceName = "resourcequotas" ResourceQuotas ResourceName = "resourcequotas"
// ResourceSecrets, number // ResourceSecrets, number
ResourceSecrets ResourceName = "secrets" ResourceSecrets ResourceName = "secrets"
// ResourceConfigMaps, number
ResourceConfigMaps ResourceName = "configmaps"
// ResourcePersistentVolumeClaims, number // ResourcePersistentVolumeClaims, number
ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims" ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims"
// CPU request, in cores. (500m = .5 cores) // CPU request, in cores. (500m = .5 cores)

View File

@ -2644,6 +2644,8 @@ const (
ResourceQuotas ResourceName = "resourcequotas" ResourceQuotas ResourceName = "resourcequotas"
// ResourceSecrets, number // ResourceSecrets, number
ResourceSecrets ResourceName = "secrets" ResourceSecrets ResourceName = "secrets"
// ResourceConfigMaps, number
ResourceConfigMaps ResourceName = "configmaps"
// ResourcePersistentVolumeClaims, number // ResourcePersistentVolumeClaims, number
ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims" ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims"
// CPU request, in cores. (500m = .5 cores) // CPU request, in cores. (500m = .5 cores)

View File

@ -4082,6 +4082,8 @@ func TestValidateResourceQuota(t *testing.T) {
api.ResourceServices: resource.MustParse("0"), api.ResourceServices: resource.MustParse("0"),
api.ResourceReplicationControllers: resource.MustParse("10"), api.ResourceReplicationControllers: resource.MustParse("10"),
api.ResourceQuotas: resource.MustParse("10"), api.ResourceQuotas: resource.MustParse("10"),
api.ResourceConfigMaps: resource.MustParse("10"),
api.ResourceSecrets: resource.MustParse("10"),
}, },
} }
@ -4129,6 +4131,8 @@ func TestValidateResourceQuota(t *testing.T) {
api.ResourceServices: resource.MustParse("-10"), api.ResourceServices: resource.MustParse("-10"),
api.ResourceReplicationControllers: resource.MustParse("-10"), api.ResourceReplicationControllers: resource.MustParse("-10"),
api.ResourceQuotas: resource.MustParse("-10"), api.ResourceQuotas: resource.MustParse("-10"),
api.ResourceConfigMaps: resource.MustParse("-10"),
api.ResourceSecrets: resource.MustParse("-10"),
}, },
} }

View File

@ -187,6 +187,22 @@ func (r *replenishmentControllerFactory) NewController(options *ReplenishmentCon
DeleteFunc: ObjectReplenishmentDeleteFunc(options), DeleteFunc: ObjectReplenishmentDeleteFunc(options),
}, },
) )
case api.Kind("ConfigMap"):
_, result = framework.NewInformer(
&cache.ListWatch{
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
return r.kubeClient.Core().ConfigMaps(api.NamespaceAll).List(options)
},
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
return r.kubeClient.Core().ConfigMaps(api.NamespaceAll).Watch(options)
},
},
&api.ConfigMap{},
options.ResyncPeriod(),
framework.ResourceEventHandlerFuncs{
DeleteFunc: ObjectReplenishmentDeleteFunc(options),
},
)
default: default:
return nil, fmt.Errorf("no replenishment controller available for %s", options.GroupKind) return nil, fmt.Errorf("no replenishment controller available for %s", options.GroupKind)
} }

View File

@ -0,0 +1,45 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
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/quota"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/runtime"
)
// NewConfigMapEvaluator returns an evaluator that can evaluate configMaps
func NewConfigMapEvaluator(kubeClient clientset.Interface) quota.Evaluator {
allResources := []api.ResourceName{api.ResourceConfigMaps}
return &generic.GenericEvaluator{
Name: "Evaluator.ConfigMap",
InternalGroupKind: api.Kind("ConfigMap"),
InternalOperationResources: map[admission.Operation][]api.ResourceName{
admission.Create: allResources,
},
MatchedResourceNames: allResources,
MatchesScopeFunc: generic.MatchesNoScopeFunc,
ConstraintsFunc: generic.ObjectCountConstraintsFunc(api.ResourceConfigMaps),
UsageFunc: generic.ObjectCountUsageFunc(api.ResourceConfigMaps),
ListFuncByNamespace: func(namespace string, options api.ListOptions) (runtime.Object, error) {
return kubeClient.Core().ConfigMaps(namespace).List(options)
},
}
}

View File

@ -30,6 +30,7 @@ func NewRegistry(kubeClient clientset.Interface) quota.Registry {
replicationController := NewReplicationControllerEvaluator(kubeClient) replicationController := NewReplicationControllerEvaluator(kubeClient)
resourceQuota := NewResourceQuotaEvaluator(kubeClient) resourceQuota := NewResourceQuotaEvaluator(kubeClient)
secret := NewSecretEvaluator(kubeClient) secret := NewSecretEvaluator(kubeClient)
configMap := NewConfigMapEvaluator(kubeClient)
persistentVolumeClaim := NewPersistentVolumeClaimEvaluator(kubeClient) persistentVolumeClaim := NewPersistentVolumeClaimEvaluator(kubeClient)
return &generic.GenericRegistry{ return &generic.GenericRegistry{
InternalEvaluators: map[unversioned.GroupKind]quota.Evaluator{ InternalEvaluators: map[unversioned.GroupKind]quota.Evaluator{
@ -37,6 +38,7 @@ func NewRegistry(kubeClient clientset.Interface) quota.Registry {
service.GroupKind(): service, service.GroupKind(): service,
replicationController.GroupKind(): replicationController, replicationController.GroupKind(): replicationController,
secret.GroupKind(): secret, secret.GroupKind(): secret,
configMap.GroupKind(): configMap,
resourceQuota.GroupKind(): resourceQuota, resourceQuota.GroupKind(): resourceQuota,
persistentVolumeClaim.GroupKind(): persistentVolumeClaim, persistentVolumeClaim.GroupKind(): persistentVolumeClaim,
}, },

View File

@ -182,6 +182,41 @@ var _ = Describe("ResourceQuota", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
}) })
It("should create a ResourceQuota and capture the life of a configMap.", func() {
By("Creating a ResourceQuota")
quotaName := "test-quota"
resourceQuota := newTestResourceQuota(quotaName)
resourceQuota, err := createResourceQuota(f.Client, f.Namespace.Name, resourceQuota)
Expect(err).NotTo(HaveOccurred())
By("Ensuring resource quota status is calculated")
usedResources := api.ResourceList{}
usedResources[api.ResourceQuotas] = resource.MustParse("1")
err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources)
Expect(err).NotTo(HaveOccurred())
By("Creating a ConfigMap")
configMap := newTestConfigMapForQuota("test-configmap")
configMap, err = f.Client.ConfigMaps(f.Namespace.Name).Create(configMap)
Expect(err).NotTo(HaveOccurred())
By("Ensuring resource quota status captures configMap creation")
usedResources = api.ResourceList{}
usedResources[api.ResourceQuotas] = resource.MustParse("1")
usedResources[api.ResourceConfigMaps] = resource.MustParse("1")
err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources)
Expect(err).NotTo(HaveOccurred())
By("Deleting a ConfigMap")
err = f.Client.ConfigMaps(f.Namespace.Name).Delete(configMap.Name)
Expect(err).NotTo(HaveOccurred())
By("Ensuring resource quota status released usage")
usedResources[api.ResourceConfigMaps] = resource.MustParse("0")
err = waitForResourceQuota(f.Client, f.Namespace.Name, quotaName, usedResources)
Expect(err).NotTo(HaveOccurred())
})
It("should verify ResourceQuota with terminating scopes.", func() { It("should verify ResourceQuota with terminating scopes.", func() {
By("Creating a ResourceQuota with terminating scope") By("Creating a ResourceQuota with terminating scope")
quotaTerminatingName := "quota-terminating" quotaTerminatingName := "quota-terminating"
@ -387,6 +422,8 @@ func newTestResourceQuota(name string) *api.ResourceQuota {
hard[api.ResourceQuotas] = resource.MustParse("1") hard[api.ResourceQuotas] = resource.MustParse("1")
hard[api.ResourceCPU] = resource.MustParse("1") hard[api.ResourceCPU] = resource.MustParse("1")
hard[api.ResourceMemory] = resource.MustParse("500Mi") hard[api.ResourceMemory] = resource.MustParse("500Mi")
hard[api.ResourceConfigMaps] = resource.MustParse("2")
hard[api.ResourceSecrets] = resource.MustParse("2")
return &api.ResourceQuota{ return &api.ResourceQuota{
ObjectMeta: api.ObjectMeta{Name: name}, ObjectMeta: api.ObjectMeta{Name: name},
Spec: api.ResourceQuotaSpec{Hard: hard}, Spec: api.ResourceQuotaSpec{Hard: hard},
@ -429,6 +466,17 @@ func newTestServiceForQuota(name string) *api.Service {
} }
} }
func newTestConfigMapForQuota(name string) *api.ConfigMap {
return &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: name,
},
Data: map[string]string{
"a": "b",
},
}
}
func newTestSecretForQuota(name string) *api.Secret { func newTestSecretForQuota(name string) *api.Secret {
return &api.Secret{ return &api.Secret{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{