pkg/collectors: Refactor resource quota collector

This commit is contained in:
Max Leonard Inden 2018-11-27 17:23:52 +01:00
parent 56b4211eb1
commit 13c990201f
No known key found for this signature in database
GPG Key ID: 5403C5464810BC26
4 changed files with 262 additions and 3 deletions

View File

@ -263,6 +263,8 @@ func TestFullScrapeCycle(t *testing.T) {
# HELP kube_replicationcontroller_status_observed_generation The generation observed by the ReplicationController controller.
# HELP kube_replicationcontroller_spec_replicas Number of desired pods for a ReplicationController.
# HELP kube_replicationcontroller_metadata_generation Sequence number representing a specific generation of the desired state.
# HELP kube_resourcequota_created Unix creation timestamp
# HELP kube_resourcequota Information about resource quota.
# HELP kube_secret_info Information about secret.
# HELP kube_secret_type Type about secret.
# HELP kube_secret_labels Kubernetes labels converted to Prometheus labels.

View File

@ -130,14 +130,12 @@ var availableCollectors = map[string]func(f *Builder) *Collector{
"pods": func(b *Builder) *Collector { return b.buildPodCollector() },
"replicasets": func(b *Builder) *Collector { return b.buildReplicaSetCollector() },
"replicationcontrollers": func(b *Builder) *Collector { return b.buildReplicationControllerCollector() },
"resourcequotas": func(b *Builder) *Collector { return b.buildResourceQuotaCollector() },
"secrets": func(b *Builder) *Collector { return b.buildSecretCollector() },
"services": func(b *Builder) *Collector { return b.buildServiceCollector() },
"statefulsets": func(b *Builder) *Collector { return b.buildStatefulSetCollector() },
// "configmaps": func(b *Builder) *Collector { return b.buildConfigMapCollector() },
// "cronjobs": func(b *Builder) *Collector { return b.buildCronJobCollector() },
// "endpoints": func(b *Builder) *Collector { return b.buildEndpointsCollector() },
// "horizontalpodautoscalers": func(b *Builder) *Collector { return b.buildHPACollector() },
// "resourcequotas": func(b *Builder) *Collector { return b.buildResourceQuotaCollector() },
}
func (b *Builder) buildConfigMapCollector() *Collector {
@ -335,6 +333,21 @@ func (b *Builder) buildReplicationControllerCollector() *Collector {
return NewCollector(store)
}
func (b *Builder) buildResourceQuotaCollector() *Collector {
filteredMetricFamilies := filterMetricFamilies(b.whiteBlackList, resourceQuotaMetricFamilies)
composedMetricGenFuncs := composeMetricGenFuncs(filteredMetricFamilies)
helpTexts := extractHelpText(filteredMetricFamilies)
store := metricsstore.NewMetricsStore(
helpTexts,
composedMetricGenFuncs,
)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.ResourceQuota{}, store, b.namespaces, createResourceQuotaListWatch)
return NewCollector(store)
}
func (b *Builder) buildSecretCollector() *Collector {
filteredMetricFamilies := filterMetricFamilies(b.whiteBlackList, secretMetricFamilies)
composedMetricGenFuncs := composeMetricGenFuncs(filteredMetricFamilies)

View File

@ -0,0 +1,104 @@
/*
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 collectors
import (
"k8s.io/kube-state-metrics/pkg/metrics"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
)
var (
descResourceQuotaLabelsDefaultLabels = []string{"namespace", "resourcequota"}
resourceQuotaMetricFamilies = []metrics.FamilyGenerator{
metrics.FamilyGenerator{
Name: "kube_resourcequota_created",
Help: "Unix creation timestamp",
GenerateFunc: wrapResourceQuotaFunc(func(r *v1.ResourceQuota) metrics.Family {
f := metrics.Family{}
if !r.CreationTimestamp.IsZero() {
f = append(f, &metrics.Metric{
Name: "kube_resourcequota_created",
Value: float64(r.CreationTimestamp.Unix()),
})
}
return f
}),
},
metrics.FamilyGenerator{
Name: "kube_resourcequota",
Help: "Information about resource quota.",
GenerateFunc: wrapResourceQuotaFunc(func(r *v1.ResourceQuota) metrics.Family {
f := metrics.Family{}
for res, qty := range r.Status.Hard {
f = append(f, &metrics.Metric{
LabelValues: []string{string(res), "hard"},
Value: float64(qty.MilliValue()) / 1000,
})
}
for res, qty := range r.Status.Used {
f = append(f, &metrics.Metric{
LabelValues: []string{string(res), "used"},
Value: float64(qty.MilliValue()) / 1000,
})
}
for _, m := range f {
m.Name = "kube_resourcequota"
m.LabelKeys = []string{"resource", "type"}
}
return f
}),
},
}
)
func wrapResourceQuotaFunc(f func(*v1.ResourceQuota) metrics.Family) func(interface{}) metrics.Family {
return func(obj interface{}) metrics.Family {
resourceQuota := obj.(*v1.ResourceQuota)
metricFamily := f(resourceQuota)
for _, m := range metricFamily {
m.LabelKeys = append(descResourceQuotaLabelsDefaultLabels, m.LabelKeys...)
m.LabelValues = append([]string{resourceQuota.Namespace, resourceQuota.Name}, m.LabelValues...)
}
return metricFamily
}
}
func createResourceQuotaListWatch(kubeClient clientset.Interface, ns string) cache.ListWatch {
return cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
return kubeClient.CoreV1().ResourceQuotas(ns).List(opts)
},
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
return kubeClient.CoreV1().ResourceQuotas(ns).Watch(opts)
},
}
}

View File

@ -0,0 +1,140 @@
/*
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 collectors
import (
"testing"
"time"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestResourceQuotaCollector(t *testing.T) {
// Fixed metadata on type and help text. We prepend this to every expected
// output so we only have to modify a single place when doing adjustments.
const metadata = `
# HELP kube_resourcequota Information about resource quota.
# TYPE kube_resourcequota gauge
# HELP kube_resourcequota_created Unix creation timestamp
# TYPE kube_resourcequota_created gauge
`
cases := []generateMetricsTestCase{
// Verify populating base metrics and that metrics for unset fields are skipped.
{
Obj: &v1.ResourceQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "quotaTest",
CreationTimestamp: metav1.Time{Time: time.Unix(1500000000, 0)},
Namespace: "testNS",
},
Status: v1.ResourceQuotaStatus{},
},
Want: `
kube_resourcequota_created{namespace="testNS",resourcequota="quotaTest"} 1.5e+09
`,
},
// Verify resource metrics.
{
Obj: &v1.ResourceQuota{
ObjectMeta: metav1.ObjectMeta{
Name: "quotaTest",
Namespace: "testNS",
},
Spec: v1.ResourceQuotaSpec{
Hard: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("4.3"),
v1.ResourceMemory: resource.MustParse("2.1G"),
v1.ResourceStorage: resource.MustParse("10G"),
v1.ResourcePods: resource.MustParse("9"),
v1.ResourceServices: resource.MustParse("8"),
v1.ResourceReplicationControllers: resource.MustParse("7"),
v1.ResourceQuotas: resource.MustParse("6"),
v1.ResourceSecrets: resource.MustParse("5"),
v1.ResourceConfigMaps: resource.MustParse("4"),
v1.ResourcePersistentVolumeClaims: resource.MustParse("3"),
v1.ResourceServicesNodePorts: resource.MustParse("2"),
v1.ResourceServicesLoadBalancers: resource.MustParse("1"),
},
},
Status: v1.ResourceQuotaStatus{
Hard: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("4.3"),
v1.ResourceMemory: resource.MustParse("2.1G"),
v1.ResourceStorage: resource.MustParse("10G"),
v1.ResourcePods: resource.MustParse("9"),
v1.ResourceServices: resource.MustParse("8"),
v1.ResourceReplicationControllers: resource.MustParse("7"),
v1.ResourceQuotas: resource.MustParse("6"),
v1.ResourceSecrets: resource.MustParse("5"),
v1.ResourceConfigMaps: resource.MustParse("4"),
v1.ResourcePersistentVolumeClaims: resource.MustParse("3"),
v1.ResourceServicesNodePorts: resource.MustParse("2"),
v1.ResourceServicesLoadBalancers: resource.MustParse("1"),
},
Used: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("2.1"),
v1.ResourceMemory: resource.MustParse("500M"),
v1.ResourceStorage: resource.MustParse("9G"),
v1.ResourcePods: resource.MustParse("8"),
v1.ResourceServices: resource.MustParse("7"),
v1.ResourceReplicationControllers: resource.MustParse("6"),
v1.ResourceQuotas: resource.MustParse("5"),
v1.ResourceSecrets: resource.MustParse("4"),
v1.ResourceConfigMaps: resource.MustParse("3"),
v1.ResourcePersistentVolumeClaims: resource.MustParse("2"),
v1.ResourceServicesNodePorts: resource.MustParse("1"),
v1.ResourceServicesLoadBalancers: resource.MustParse("0"),
},
},
},
Want: `
kube_resourcequota{namespace="testNS",resource="configmaps",resourcequota="quotaTest",type="hard"} 4
kube_resourcequota{namespace="testNS",resource="configmaps",resourcequota="quotaTest",type="used"} 3
kube_resourcequota{namespace="testNS",resource="cpu",resourcequota="quotaTest",type="hard"} 4.3
kube_resourcequota{namespace="testNS",resource="cpu",resourcequota="quotaTest",type="used"} 2.1
kube_resourcequota{namespace="testNS",resource="memory",resourcequota="quotaTest",type="hard"} 2.1e+09
kube_resourcequota{namespace="testNS",resource="memory",resourcequota="quotaTest",type="used"} 5e+08
kube_resourcequota{namespace="testNS",resource="persistentvolumeclaims",resourcequota="quotaTest",type="hard"} 3
kube_resourcequota{namespace="testNS",resource="persistentvolumeclaims",resourcequota="quotaTest",type="used"} 2
kube_resourcequota{namespace="testNS",resource="pods",resourcequota="quotaTest",type="hard"} 9
kube_resourcequota{namespace="testNS",resource="pods",resourcequota="quotaTest",type="used"} 8
kube_resourcequota{namespace="testNS",resource="replicationcontrollers",resourcequota="quotaTest",type="hard"} 7
kube_resourcequota{namespace="testNS",resource="replicationcontrollers",resourcequota="quotaTest",type="used"} 6
kube_resourcequota{namespace="testNS",resource="resourcequotas",resourcequota="quotaTest",type="hard"} 6
kube_resourcequota{namespace="testNS",resource="resourcequotas",resourcequota="quotaTest",type="used"} 5
kube_resourcequota{namespace="testNS",resource="secrets",resourcequota="quotaTest",type="hard"} 5
kube_resourcequota{namespace="testNS",resource="secrets",resourcequota="quotaTest",type="used"} 4
kube_resourcequota{namespace="testNS",resource="services",resourcequota="quotaTest",type="hard"} 8
kube_resourcequota{namespace="testNS",resource="services",resourcequota="quotaTest",type="used"} 7
kube_resourcequota{namespace="testNS",resource="services.loadbalancers",resourcequota="quotaTest",type="hard"} 1
kube_resourcequota{namespace="testNS",resource="services.loadbalancers",resourcequota="quotaTest",type="used"} 0
kube_resourcequota{namespace="testNS",resource="services.nodeports",resourcequota="quotaTest",type="hard"} 2
kube_resourcequota{namespace="testNS",resource="services.nodeports",resourcequota="quotaTest",type="used"} 1
kube_resourcequota{namespace="testNS",resource="storage",resourcequota="quotaTest",type="hard"} 1e+10
kube_resourcequota{namespace="testNS",resource="storage",resourcequota="quotaTest",type="used"} 9e+09
`,
},
}
for i, c := range cases {
c.Func = composeMetricGenFuncs(resourceQuotaMetricFamilies)
if err := c.run(); err != nil {
t.Errorf("unexpected collecting result in %vth run:\n%s", i, err)
}
}
}