pkg/collectors: Renable white-/blacklisting

Reenable feature to filter the exposition of metric families by their
name. This is now done at startup time, thereby not slowing down the
critical path.
This commit is contained in:
Max Leonard Inden 2018-11-05 16:45:12 +00:00
parent af34d12ac4
commit d44a8bc991
No known key found for this signature in database
GPG Key ID: 5403C5464810BC26
8 changed files with 347 additions and 464 deletions

View File

@ -103,9 +103,11 @@ func main() {
}
if !opts.MetricWhitelist.IsEmpty() {
glog.Infof("A metric whitelist has been configured. Only the following metrics will be exposed: %s.", opts.MetricWhitelist.String())
collectorBuilder.WithMetricWhitelist(opts.MetricWhitelist)
}
if !opts.MetricBlacklist.IsEmpty() {
glog.Infof("A metric blacklist has been configured. The following metrics will not be exposed: %s.", opts.MetricBlacklist.String())
collectorBuilder.WithMetricBlacklist(opts.MetricBlacklist)
}
proc.StartReaper()

View File

@ -17,9 +17,9 @@ limitations under the License.
package main
import (
"context"
// "fmt"
// "io/ioutil"
"context"
"net/http/httptest"
"strconv"
"testing"
@ -82,6 +82,7 @@ func BenchmarkKubeStateMetrics(t *testing.B) {
func injectFixtures(client *fake.Clientset, multiplier int) error {
creators := []func(*fake.Clientset, int) error{
configMap,
service,
pod,
}
@ -111,6 +112,19 @@ func configMap(client *fake.Clientset, index int) error {
return err
}
func service(client *fake.Clientset, index int) error {
i := strconv.Itoa(index)
service := v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "service" + i,
ResourceVersion: "123456",
},
}
_, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(&service)
return err
}
func pod(client *fake.Clientset, index int) error {
i := strconv.Itoa(index)

View File

@ -20,16 +20,16 @@ package collectors
import (
"strings"
apps "k8s.io/api/apps/v1beta1"
autoscaling "k8s.io/api/autoscaling/v2beta1"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
extensions "k8s.io/api/extensions/v1beta1"
// apps "k8s.io/api/apps/v1beta1"
// autoscaling "k8s.io/api/autoscaling/v2beta1"
// batchv1 "k8s.io/api/batch/v1"
// batchv1beta1 "k8s.io/api/batch/v1beta1"
// extensions "k8s.io/api/extensions/v1beta1"
"github.com/golang/glog"
"golang.org/x/net/context"
"k8s.io/api/core/v1"
"k8s.io/api/policy/v1beta1"
// "k8s.io/api/policy/v1beta1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/kube-state-metrics/pkg/metrics"
@ -45,6 +45,8 @@ type Builder struct {
opts *options.Options
ctx context.Context
enabledCollectors options.CollectorSet
metricWhitelist map[string]struct{}
metricBlacklist map[string]struct{}
}
// NewBuilder returns a new builder.
@ -73,6 +75,18 @@ func (b *Builder) WithKubeClient(c clientset.Interface) {
b.kubeClient = c
}
// WithMetricWhitelist configures the whitelisted metrics to be exposed by the
// collectors build by the Builder
func (b *Builder) WithMetricWhitelist(l map[string]struct{}) {
b.metricWhitelist = l
}
// WithMetricBlacklist configures the blacklisted metrics to be exposed by the
// collectors build by the Builder
func (b *Builder) WithMetricBlacklist(l map[string]struct{}) {
b.metricBlacklist = l
}
// Build initializes and registers all enabled collectors.
func (b *Builder) Build() []*Collector {
@ -95,174 +109,170 @@ func (b *Builder) Build() []*Collector {
}
var availableCollectors = map[string]func(f *Builder) *Collector{
"configmaps": func(b *Builder) *Collector { return b.buildConfigMapCollector() },
"cronjobs": func(b *Builder) *Collector { return b.buildCronJobCollector() },
"daemonsets": func(b *Builder) *Collector { return b.buildDaemonSetCollector() },
"deployments": func(b *Builder) *Collector { return b.buildDeploymentCollector() },
"endpoints": func(b *Builder) *Collector { return b.buildEndpointsCollector() },
"horizontalpodautoscalers": func(b *Builder) *Collector { return b.buildHPACollector() },
"jobs": func(b *Builder) *Collector { return b.buildJobCollector() },
"limitranges": func(b *Builder) *Collector { return b.buildLimitRangeCollector() },
"namespaces": func(b *Builder) *Collector { return b.buildNamespaceCollector() },
"nodes": func(b *Builder) *Collector { return b.buildNodeCollector() },
"persistentvolumeclaims": func(b *Builder) *Collector { return b.buildPersistentVolumeClaimCollector() },
"persistentvolumes": func(b *Builder) *Collector { return b.buildPersistentVolumeCollector() },
"poddisruptionbudgets": func(b *Builder) *Collector { return b.buildPodDisruptionBudgetCollector() },
"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() },
}
func (b *Builder) buildPodCollector() *Collector {
genFunc := func(obj interface{}) []*metrics.Metric {
return generatePodMetrics(b.opts.DisablePodNonGenericResourceMetrics, obj)
}
store := metricsstore.NewMetricsStore(genFunc)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Pod{}, store, b.namespaces, createPodListWatch)
return NewCollector(store)
}
func (b *Builder) buildCronJobCollector() *Collector {
store := metricsstore.NewMetricsStore(generateCronJobMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1beta1.CronJob{}, store, b.namespaces, createCronJobListWatch)
return NewCollector(store)
}
func (b *Builder) buildConfigMapCollector() *Collector {
store := metricsstore.NewMetricsStore(generateConfigMapMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.ConfigMap{}, store, b.namespaces, createConfigMapListWatch)
return NewCollector(store)
}
func (b *Builder) buildDaemonSetCollector() *Collector {
store := metricsstore.NewMetricsStore(generateDaemonSetMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &extensions.DaemonSet{}, store, b.namespaces, createDaemonSetListWatch)
return NewCollector(store)
}
func (b *Builder) buildDeploymentCollector() *Collector {
store := metricsstore.NewMetricsStore(generateDeploymentMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &extensions.Deployment{}, store, b.namespaces, createDeploymentListWatch)
return NewCollector(store)
}
func (b *Builder) buildEndpointsCollector() *Collector {
store := metricsstore.NewMetricsStore(generateEndpointsMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Endpoints{}, store, b.namespaces, createEndpointsListWatch)
return NewCollector(store)
}
func (b *Builder) buildHPACollector() *Collector {
store := metricsstore.NewMetricsStore(generateHPAMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &autoscaling.HorizontalPodAutoscaler{}, store, b.namespaces, createHPAListWatch)
return NewCollector(store)
}
func (b *Builder) buildJobCollector() *Collector {
store := metricsstore.NewMetricsStore(generateJobMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1.Job{}, store, b.namespaces, createJobListWatch)
return NewCollector(store)
}
func (b *Builder) buildLimitRangeCollector() *Collector {
store := metricsstore.NewMetricsStore(generateLimitRangeMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.LimitRange{}, store, b.namespaces, createLimitRangeListWatch)
return NewCollector(store)
}
func (b *Builder) buildNamespaceCollector() *Collector {
store := metricsstore.NewMetricsStore(generateNamespaceMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Namespace{}, store, b.namespaces, createNamespaceListWatch)
return NewCollector(store)
}
func (b *Builder) buildNodeCollector() *Collector {
genFunc := func(obj interface{}) []*metrics.Metric {
return generateNodeMetrics(b.opts.DisableNodeNonGenericResourceMetrics, obj)
}
store := metricsstore.NewMetricsStore(genFunc)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Node{}, store, b.namespaces, createNodeListWatch)
return NewCollector(store)
}
func (b *Builder) buildPersistentVolumeCollector() *Collector {
store := metricsstore.NewMetricsStore(generatePersistentVolumeMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.PersistentVolume{}, store, b.namespaces, createPersistentVolumeListWatch)
return NewCollector(store)
}
func (b *Builder) buildPersistentVolumeClaimCollector() *Collector {
store := metricsstore.NewMetricsStore(generatePersistentVolumeClaimMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.PersistentVolumeClaim{}, store, b.namespaces, createPersistentVolumeClaimListWatch)
return NewCollector(store)
}
func (b *Builder) buildPodDisruptionBudgetCollector() *Collector {
store := metricsstore.NewMetricsStore(generatePodDisruptionBudgetMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1beta1.PodDisruptionBudget{}, store, b.namespaces, createPodDisruptionBudgetListWatch)
return NewCollector(store)
}
func (b *Builder) buildReplicaSetCollector() *Collector {
store := metricsstore.NewMetricsStore(generateReplicaSetMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &extensions.ReplicaSet{}, store, b.namespaces, createReplicaSetListWatch)
return NewCollector(store)
}
func (b *Builder) buildReplicationControllerCollector() *Collector {
store := metricsstore.NewMetricsStore(generateReplicationControllerMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.ReplicationController{}, store, b.namespaces, createReplicationControllerListWatch)
return NewCollector(store)
}
func (b *Builder) buildResourceQuotaCollector() *Collector {
store := metricsstore.NewMetricsStore(generateResourceQuotaMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.ResourceQuota{}, store, b.namespaces, createResourceQuotaListWatch)
return NewCollector(store)
}
func (b *Builder) buildSecretCollector() *Collector {
store := metricsstore.NewMetricsStore(generateSecretMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Secret{}, store, b.namespaces, createSecretListWatch)
return NewCollector(store)
// "configmaps": func(b *Builder) *Collector { return b.buildConfigMapCollector() },
// "cronjobs": func(b *Builder) *Collector { return b.buildCronJobCollector() },
// "daemonsets": func(b *Builder) *Collector { return b.buildDaemonSetCollector() },
// "deployments": func(b *Builder) *Collector { return b.buildDeploymentCollector() },
// "endpoints": func(b *Builder) *Collector { return b.buildEndpointsCollector() },
// "horizontalpodautoscalers": func(b *Builder) *Collector { return b.buildHPACollector() },
// "jobs": func(b *Builder) *Collector { return b.buildJobCollector() },
// "limitranges": func(b *Builder) *Collector { return b.buildLimitRangeCollector() },
// "namespaces": func(b *Builder) *Collector { return b.buildNamespaceCollector() },
// "nodes": func(b *Builder) *Collector { return b.buildNodeCollector() },
// "persistentvolumeclaims": func(b *Builder) *Collector { return b.buildPersistentVolumeClaimCollector() },
// "persistentvolumes": func(b *Builder) *Collector { return b.buildPersistentVolumeCollector() },
// "poddisruptionbudgets": func(b *Builder) *Collector { return b.buildPodDisruptionBudgetCollector() },
// "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() },
}
// func (b *Builder) buildPodCollector() *Collector {
// genFunc := func(obj interface{}) []*metrics.Metric {
// return generatePodMetrics(b.opts.DisablePodNonGenericResourceMetrics, obj)
// }
// store := metricsstore.NewMetricsStore(genFunc)
// reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Pod{}, store, b.namespaces, createPodListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildCronJobCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateCronJobMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1beta1.CronJob{}, store, b.namespaces, createCronJobListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildConfigMapCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateConfigMapMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &v1.ConfigMap{}, store, b.namespaces, createConfigMapListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildDaemonSetCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateDaemonSetMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &extensions.DaemonSet{}, store, b.namespaces, createDaemonSetListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildDeploymentCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateDeploymentMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &extensions.Deployment{}, store, b.namespaces, createDeploymentListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildEndpointsCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateEndpointsMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Endpoints{}, store, b.namespaces, createEndpointsListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildHPACollector() *Collector {
// store := metricsstore.NewMetricsStore(generateHPAMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &autoscaling.HorizontalPodAutoscaler{}, store, b.namespaces, createHPAListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildJobCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateJobMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1.Job{}, store, b.namespaces, createJobListWatch)
//
// return NewCollector(store)
// }
//
// func (b *Builder) buildLimitRangeCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateLimitRangeMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &v1.LimitRange{}, store, b.namespaces, createLimitRangeListWatch)
//
// return NewCollector(store)
// }
func (b *Builder) buildServiceCollector() *Collector {
store := metricsstore.NewMetricsStore(generateServiceMetrics)
filteredMetricFamilies := filterMetricFamilies(b.metricWhitelist, b.metricBlacklist, serviceMetricFamilies)
store := metricsstore.NewMetricsStore(
composeMetricGenFuncs(filteredMetricFamilies),
)
reflectorPerNamespace(b.ctx, b.kubeClient, &v1.Service{}, store, b.namespaces, createServiceListWatch)
return NewCollector(store)
}
func (b *Builder) buildStatefulSetCollector() *Collector {
store := metricsstore.NewMetricsStore(generateStatefulSetMetrics)
reflectorPerNamespace(b.ctx, b.kubeClient, &apps.StatefulSet{}, store, b.namespaces, createStatefulSetListWatch)
// func (b *Builder) buildNodeCollector() *Collector {
// genFunc := func(obj interface{}) []*metrics.Metric {
// return generateNodeMetrics(b.opts.DisableNodeNonGenericResourceMetrics, obj)
// }
//
// return newCollector(store)
// }
return NewCollector(store)
// composeMetricGenFuncs takes a slice of metric families and returns a function
// that composes their metric generation functions into a single one.
func composeMetricGenFuncs(families []metrics.MetricFamily) func(obj interface{}) []*metrics.Metric {
funcs := []func(obj interface{}) []*metrics.Metric{}
for _, f := range families {
funcs = append(funcs, f.GenerateFunc)
}
return func(obj interface{}) []*metrics.Metric {
metrics := []*metrics.Metric{}
for _, f := range funcs {
metrics = append(metrics, f(obj)...)
}
return metrics
}
}
// filterMetricFamilies takes a white- and a blacklist and a slice of metric
// families and returns a filtered slice.
func filterMetricFamilies(white, black map[string]struct{}, families []metrics.MetricFamily) []metrics.MetricFamily {
if len(white) != 0 && len(black) != 0 {
panic("Whitelist and blacklist are both set. They are mutually exclusive, only one of them can be set.")
}
filtered := []metrics.MetricFamily{}
if len(white) != 0 {
for _, f := range families {
if _, whitelisted := white[f.Name]; whitelisted {
filtered = append(filtered, f)
}
}
return filtered
}
for _, f := range families {
if _, blacklisted := black[f.Name]; !blacklisted {
filtered = append(filtered, f)
}
}
return filtered
}
//
// func (b *Builder) buildStatefulSetCollector() *Collector {
// store := metricsstore.NewMetricsStore(generateStatefulSetMetrics)
// reflectorPerNamespace(b.ctx, b.kubeClient, &apps.StatefulSet{}, store, b.namespaces, createStatefulSetListWatch)
//
// return newCollector(store)
// }
// reflectorPerNamespace creates a Kubernetes client-go reflector with the given
// listWatchFunc for each given namespace and registers it with the given store.
func reflectorPerNamespace(
ctx context.Context,
kubeClient clientset.Interface,

View File

@ -32,63 +32,163 @@ var (
descServiceLabelsHelp = "Kubernetes labels converted to Prometheus labels."
descServiceLabelsDefaultLabels = []string{"namespace", "service"}
descServiceInfo = metrics.NewMetricFamilyDef(
"kube_service_info",
"Information about service.",
append(descServiceLabelsDefaultLabels, "cluster_ip", "external_name", "load_balancer_ip"),
nil,
)
serviceMetricFamilies = []metrics.MetricFamily{
{
"kube_service_info",
"Information about service.",
func(obj interface{}) []*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
descServiceCreated = metrics.NewMetricFamilyDef(
"kube_service_created",
"Unix creation timestamp",
descServiceLabelsDefaultLabels,
nil,
)
return []*metrics.Metric{
newServiceMetric(
sPointer,
"kube_service_info",
[]string{"cluster_ip", "external_name", "load_balancer_ip"},
[]string{s.Spec.ClusterIP, s.Spec.ExternalName, s.Spec.LoadBalancerIP},
1,
),
}
},
},
{
"kube_service_created",
"Unix creation timestamp",
func(obj interface{}) []*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
descServiceSpecType = metrics.NewMetricFamilyDef(
"kube_service_spec_type",
"Type about service.",
append(descServiceLabelsDefaultLabels, "type"),
nil,
)
if !s.CreationTimestamp.IsZero() {
return []*metrics.Metric{
newServiceMetric(
sPointer,
"kube_service_created",
nil,
nil,
float64(s.CreationTimestamp.Unix()),
),
}
}
return nil
},
},
{
"kube_service_spec_type",
"Type about service.",
func(obj interface{}) []*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
descServiceExternalName = metrics.NewMetricFamilyDef(
"kube_service_external_name",
"Service external name",
append(descServiceLabelsDefaultLabels, "external_name"),
nil,
)
return []*metrics.Metric{
newServiceMetric(
sPointer,
"kube_service_spec_type",
[]string{"type"},
[]string{string(s.Spec.Type)},
1,
),
}
},
},
{
descServiceLabelsName,
descServiceLabelsHelp,
func(obj interface{}) []*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
descServiceLoadBalancerIP = metrics.NewMetricFamilyDef(
"kube_service_load_balancer_ip",
"Load balancer IP of service",
append(descServiceLabelsDefaultLabels, "load_balancer_ip"),
nil,
)
labelKeys, labelValues := kubeLabelsToPrometheusLabels(s.Labels)
return []*metrics.Metric{
newServiceMetric(
sPointer,
descServiceLabelsName,
labelKeys,
labelValues,
1,
),
}
},
},
// Defined, but not used anywhere. See
// https://github.com/kubernetes/kube-state-metrics/pull/571#pullrequestreview-176215628.
// {
// "kube_service_external_name",
// "Service external name",
// // []string{"type"},
// },
// {
// "kube_service_load_balancer_ip",
// "Load balancer IP of service",
// // []string{"load_balancer_ip"},
// },
{
"kube_service_spec_external_ip",
"Service external ips. One series for each ip",
func(obj interface{}) []*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
descServiceSpecExternalIP = metrics.NewMetricFamilyDef(
"kube_service_spec_external_ip",
"Service external ips. One series for each ip",
append(descServiceLabelsDefaultLabels, "external_ip"),
nil,
)
metrics := []*metrics.Metric{}
descServiceLabels = metrics.NewMetricFamilyDef(
descServiceLabelsName,
descServiceLabelsHelp,
descServiceLabelsDefaultLabels,
nil,
)
if len(s.Spec.ExternalIPs) > 0 {
for _, externalIP := range s.Spec.ExternalIPs {
m := newServiceMetric(
sPointer,
"kube_service_spec_external_ip",
[]string{"external_ip"},
[]string{externalIP},
1,
)
descServiceStatusLoadBalancerIngress = metrics.NewMetricFamilyDef(
"kube_service_status_load_balancer_ingress",
"Service load balancer ingress status",
append(descServiceLabelsDefaultLabels, "ip", "hostname"),
nil,
)
metrics = append(metrics, m)
}
}
return metrics
},
},
{
"kube_service_status_load_balancer_ingress",
"Service load balancer ingress status",
func(obj interface{}) []*metrics.Metric {
sPointer := obj.(*v1.Service)
s := *sPointer
metrics := []*metrics.Metric{}
if len(s.Status.LoadBalancer.Ingress) > 0 {
for _, ingress := range s.Status.LoadBalancer.Ingress {
m := newServiceMetric(
sPointer,
"kube_service_status_load_balancer_ingress",
[]string{"ip", "hostname"},
[]string{ingress.IP, ingress.Hostname},
1,
)
metrics = append(metrics, m)
}
}
return metrics
},
},
}
)
func newServiceMetric(s *v1.Service, name string, lk []string, lv []string, v float64) *metrics.Metric {
lk = append(descServiceLabelsDefaultLabels, lk...)
lv = append([]string{s.Namespace, s.Name}, lv...)
m, err := metrics.NewMetric(name, lk, lv, v)
if err != nil {
// TODO: Move this panic into metrics.NewMetric
panic(err)
}
return m
}
func createServiceListWatch(kubeClient clientset.Interface, ns string) cache.ListWatch {
return cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
@ -99,56 +199,3 @@ func createServiceListWatch(kubeClient clientset.Interface, ns string) cache.Lis
},
}
}
func serviceLabelsDesc(labelKeys []string) *metrics.MetricFamilyDef {
return metrics.NewMetricFamilyDef(
descServiceLabelsName,
descServiceLabelsHelp,
append(descServiceLabelsDefaultLabels, labelKeys...),
nil,
)
}
func generateServiceMetrics(obj interface{}) []*metrics.Metric {
ms := []*metrics.Metric{}
// TODO: Refactor
sPointer := obj.(*v1.Service)
s := *sPointer
addConstMetric := func(desc *metrics.MetricFamilyDef, v float64, lv ...string) {
lv = append([]string{s.Namespace, s.Name}, lv...)
m, err := metrics.NewMetric(desc.Name, desc.LabelKeys, lv, v)
if err != nil {
panic(err)
}
ms = append(ms, m)
}
addGauge := func(desc *metrics.MetricFamilyDef, v float64, lv ...string) {
addConstMetric(desc, v, lv...)
}
addGauge(descServiceSpecType, 1, string(s.Spec.Type))
addGauge(descServiceInfo, 1, s.Spec.ClusterIP, s.Spec.ExternalName, s.Spec.LoadBalancerIP)
if !s.CreationTimestamp.IsZero() {
addGauge(descServiceCreated, float64(s.CreationTimestamp.Unix()))
}
labelKeys, labelValues := kubeLabelsToPrometheusLabels(s.Labels)
addGauge(serviceLabelsDesc(labelKeys), 1, labelValues...)
if len(s.Status.LoadBalancer.Ingress) > 0 {
for _, ingress := range s.Status.LoadBalancer.Ingress {
addGauge(descServiceStatusLoadBalancerIngress, 1, ingress.IP, ingress.Hostname)
}
}
if len(s.Spec.ExternalIPs) > 0 {
for _, external_ip := range s.Spec.ExternalIPs {
addGauge(descServiceSpecExternalIP, 1, external_ip)
}
}
return ms
}

View File

@ -199,7 +199,7 @@ func TestServiceCollector(t *testing.T) {
},
}
for i, c := range cases {
c.Func = generateServiceMetrics
c.Func = composeMetricGenFuncs(serviceMetricFamilies)
if err := c.run(); err != nil {
t.Errorf("unexpected collecting result in %vth run:\n%s", i, err)
}

View File

@ -36,6 +36,7 @@ type generateMetricsTestCase struct {
func (testCase *generateMetricsTestCase) run() error {
metrics := testCase.Func(testCase.Obj)
metrics = filterMetrics(metrics, testCase.MetricNames)
out := ""

View File

@ -22,11 +22,6 @@ import (
"strconv"
"strings"
"sync"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"k8s.io/kube-state-metrics/pkg/options"
)
const (
@ -42,6 +37,13 @@ var (
}
)
// MetricFamily represents a set of metrics with the same name and help text.
type MetricFamily struct {
Name string
Help string
GenerateFunc func(obj interface{}) []*Metric
}
// Metric represents a single line entry in the /metrics export format
type Metric string
@ -121,64 +123,3 @@ func writeFloat(w *strings.Builder, f float64) {
numBufPool.Put(bp)
}
}
// MetricFamilyDesc represents the HELP and TYPE string above a metric family list
type MetricFamilyDesc string
type gathererFunc func() ([]*dto.MetricFamily, error)
func (f gathererFunc) Gather() ([]*dto.MetricFamily, error) {
return f()
}
// FilteredGatherer wraps a prometheus.Gatherer to filter metrics based on a
// white or blacklist. Whitelist and blacklist are mutually exclusive.
// TODO: Bring white and blacklisting back
func FilteredGatherer(r prometheus.Gatherer, whitelist options.MetricSet, blacklist options.MetricSet) prometheus.Gatherer {
whitelistEnabled := !whitelist.IsEmpty()
blacklistEnabled := !blacklist.IsEmpty()
if whitelistEnabled {
return gathererFunc(func() ([]*dto.MetricFamily, error) {
metricFamilies, err := r.Gather()
if err != nil {
return nil, err
}
newMetricFamilies := []*dto.MetricFamily{}
for _, metricFamily := range metricFamilies {
// deferencing this string may be a performance bottleneck
name := *metricFamily.Name
_, onWhitelist := whitelist[name]
if onWhitelist {
newMetricFamilies = append(newMetricFamilies, metricFamily)
}
}
return newMetricFamilies, nil
})
}
if blacklistEnabled {
return gathererFunc(func() ([]*dto.MetricFamily, error) {
metricFamilies, err := r.Gather()
if err != nil {
return nil, err
}
newMetricFamilies := []*dto.MetricFamily{}
for _, metricFamily := range metricFamilies {
name := *metricFamily.Name
_, onBlacklist := blacklist[name]
if onBlacklist {
continue
}
newMetricFamilies = append(newMetricFamilies, metricFamily)
}
return newMetricFamilies, nil
})
}
return r
}

View File

@ -2,140 +2,8 @@ package metrics
import (
"testing"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/kube-state-metrics/pkg/options"
)
func TestFiltererdGatherer(t *testing.T) {
r := prometheus.NewRegistry()
c1 := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "test1",
Help: "test1 help",
},
)
c2 := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "test2",
Help: "test2 help",
},
)
c1.Inc()
c1.Inc()
c2.Inc()
r.MustRegister(c1)
r.MustRegister(c2)
res, err := FilteredGatherer(r, nil, nil).Gather()
if err != nil {
t.Fatal(err)
}
found1 := false
found2 := false
for _, mf := range res {
if *mf.Name == "test1" {
found1 = true
}
if *mf.Name == "test2" {
found2 = true
}
}
if !found1 || !found2 {
t.Fatal("No results expected to be filtered, but results were filtered.")
}
}
func TestFiltererdGathererWhitelist(t *testing.T) {
r := prometheus.NewRegistry()
c1 := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "test1",
Help: "test1 help",
},
)
c2 := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "test2",
Help: "test2 help",
},
)
c1.Inc()
c1.Inc()
c2.Inc()
r.MustRegister(c1)
r.MustRegister(c2)
whitelist := options.MetricSet{}
whitelist.Set("test1")
res, err := FilteredGatherer(r, whitelist, nil).Gather()
if err != nil {
t.Fatal(err)
}
found1 := false
found2 := false
for _, mf := range res {
if *mf.Name == "test1" {
found1 = true
}
if *mf.Name == "test2" {
found2 = true
}
}
if !found1 || found2 {
t.Fatalf("Expected `test2` to be filtered and `test1` not. `test1`: %t ; `test2`: %t.", found1, found2)
}
}
func TestFiltererdGathererBlacklist(t *testing.T) {
r := prometheus.NewRegistry()
c1 := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "test1",
Help: "test1 help",
},
)
c2 := prometheus.NewCounter(
prometheus.CounterOpts{
Name: "test2",
Help: "test2 help",
},
)
c1.Inc()
c1.Inc()
c2.Inc()
r.MustRegister(c1)
r.MustRegister(c2)
blacklist := options.MetricSet{}
blacklist.Set("test1")
res, err := FilteredGatherer(r, nil, blacklist).Gather()
if err != nil {
t.Fatal(err)
}
found1 := false
found2 := false
for _, mf := range res {
if *mf.Name == "test1" {
found1 = true
}
if *mf.Name == "test2" {
found2 = true
}
}
if found1 || !found2 {
t.Fatalf("Expected `test1` to be filtered and `test2` not. `test1`: %t ; `test2`: %t.", found1, found2)
}
}
func BenchmarkNewMetric(b *testing.B) {
tests := []struct {
testName string