add secret and configmap metrics

This commit is contained in:
Andy Xie 2018-02-28 19:01:25 +08:00
parent 507a2f8ce6
commit 4b6ee09141
8 changed files with 503 additions and 0 deletions

View File

@ -23,3 +23,5 @@ Per group of metrics there is one file for each metrics. See each file for speci
* [Namespace Metrics](namespace-metrics.md)
* [Horizontal Pod Autoscaler Metrics](horizontalpodautoscaler-metrics.md)
* [Endpoint Metrics](endpoint-metrics.md)
* [Secret Metrics](secret-metrics.md)
* [ConfigMap Metrics](configmap-metrics.md)

View File

@ -0,0 +1,7 @@
# ConfigMap Metrics
| Metric name| Metric type | Labels/tags |
| ---------- | ----------- | ----------- |
| kube_configmap_info | Gauge | `configmap`=&lt;configmap-name&gt; <br> `namespace`=&lt;configmap-namespace&gt; |
| kube_configmap_created | Gauge | `configmap`=&lt;configmap-name&gt; <br> `namespace`=&lt;configmap-namespace&gt; |
| kube_configmap_metadata_resource_version | Gauge | `configmap`=&lt;configmap-name&gt; <br> `namespace`=&lt;configmap-namespace&gt; <br> `resource_version`=&lt;secret-resource-version&gt; |

View File

@ -0,0 +1,9 @@
# Secret Metrics
| Metric name| Metric type | Labels/tags |
| ---------- | ----------- | ----------- |
| kube_secret_info | Gauge | `secret`=&lt;secret-name&gt; <br> `namespace`=&lt;secret-namespace&gt; |
| kube_secret_type | Gauge | `secret`=&lt;secret-name&gt; <br> `namespace`=&lt;secret-namespace&gt; <br> `type`=&lt;secret-type&gt; |
| kube_secret_labels | Gauge | `secret`=&lt;secret-name&gt; <br> `namespace`=&lt;secret-namespace&gt; <br> `label_SECRET_LABEL`=&lt;SECRET_LABEL&gt; |
| kube_secret_created | Gauge | `secret`=&lt;secret-name&gt; <br> `namespace`=&lt;secret-namespace&gt; |
| kube_secret_metadata_resource_version | Gauge | `secret`=&lt;secret-name&gt; <br> `namespace`=&lt;secret-namespace&gt; <br> `resource_version`=&lt;secret-resource-version&gt; |

121
collectors/configmap.go Normal file
View File

@ -0,0 +1,121 @@
/*
Copyright 2018 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 (
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/context"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
)
var (
descConfigMapInfo = prometheus.NewDesc(
"kube_configmap_info",
"Information about configmap.",
[]string{"namespace", "configmap"}, nil,
)
descConfigMapCreated = prometheus.NewDesc(
"kube_configmap_created",
"Unix creation timestamp",
[]string{"namespace", "configmap"}, nil,
)
descConfigMapMetadataResourceVersion = prometheus.NewDesc(
"kube_configmap_metadata_resource_version",
"Resource version representing a specific version of the configmap.",
[]string{"namespace", "configmap", "resource_version"}, nil,
)
)
type ConfigMapLister func() ([]v1.ConfigMap, error)
func (l ConfigMapLister) List() ([]v1.ConfigMap, error) {
return l()
}
func RegisterConfigMapCollector(registry prometheus.Registerer, kubeClient kubernetes.Interface, namespace string) {
client := kubeClient.CoreV1().RESTClient()
glog.Infof("collect configmap with %s", client.APIVersion())
cmlw := cache.NewListWatchFromClient(client, "configmaps", namespace, fields.Everything())
cminf := cache.NewSharedInformer(cmlw, &v1.ConfigMap{}, resyncPeriod)
configMapLister := ConfigMapLister(func() (configMaps []v1.ConfigMap, err error) {
for _, m := range cminf.GetStore().List() {
configMaps = append(configMaps, *m.(*v1.ConfigMap))
}
return configMaps, nil
})
registry.MustRegister(&configMapCollector{store: configMapLister})
go cminf.Run(context.Background().Done())
}
type configMapStore interface {
List() (configMaps []v1.ConfigMap, err error)
}
// configMapCollector collects metrics about all configMaps in the cluster.
type configMapCollector struct {
store configMapStore
}
// Describe implements the prometheus.Collector interface.
func (sc *configMapCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- descConfigMapInfo
ch <- descConfigMapCreated
ch <- descConfigMapMetadataResourceVersion
}
// Collect implements the prometheus.Collector interface.
func (cmc *configMapCollector) Collect(ch chan<- prometheus.Metric) {
configMaps, err := cmc.store.List()
if err != nil {
ScrapeErrorTotalMetric.With(prometheus.Labels{"resource": "configmap"}).Inc()
glog.Errorf("listing configmaps failed: %s", err)
return
}
ScrapeErrorTotalMetric.With(prometheus.Labels{"resource": "configmap"}).Add(0)
ResourcesPerScrapeMetric.With(prometheus.Labels{"resource": "configmap"}).Observe(float64(len(configMaps)))
for _, s := range configMaps {
cmc.collectConfigMap(ch, s)
}
glog.V(4).Infof("collected %d configmaps", len(configMaps))
}
func (cmc *configMapCollector) collectConfigMap(ch chan<- prometheus.Metric, s v1.ConfigMap) {
addConstMetric := func(desc *prometheus.Desc, t prometheus.ValueType, v float64, lv ...string) {
lv = append([]string{s.Namespace, s.Name}, lv...)
ch <- prometheus.MustNewConstMetric(desc, t, v, lv...)
}
addGauge := func(desc *prometheus.Desc, v float64, lv ...string) {
addConstMetric(desc, prometheus.GaugeValue, v, lv...)
}
addGauge(descConfigMapInfo, 1)
if !s.CreationTimestamp.IsZero() {
addGauge(descConfigMapCreated, float64(s.CreationTimestamp.Unix()))
}
addGauge(descConfigMapMetadataResourceVersion, 1, string(s.ObjectMeta.ResourceVersion))
}

View File

@ -0,0 +1,92 @@
/*
Copyright 2018 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"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type mockConfigMapStore struct {
f func() ([]v1.ConfigMap, error)
}
func (ds mockConfigMapStore) List() (configMaps []v1.ConfigMap, err error) {
return ds.f()
}
func TestConfigMapCollector(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.
startTime := 1501569018
metav1StartTime := metav1.Unix(int64(startTime), 0)
const metadata = `
# HELP kube_configmap_info Information about configmap.
# TYPE kube_configmap_info gauge
# HELP kube_configmap_created Unix creation timestamp
# TYPE kube_configmap_created gauge
# HELP kube_configmap_metadata_resource_version Resource version representing a specific version of the configmap.
# TYPE kube_configmap_metadata_resource_version gauge
`
cases := []struct {
configMaps []v1.ConfigMap
metrics []string
want string
}{
{
configMaps: []v1.ConfigMap{
{
ObjectMeta: metav1.ObjectMeta{
Name: "configmap1",
Namespace: "ns1",
ResourceVersion: "123456",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "configmap2",
Namespace: "ns2",
CreationTimestamp: metav1StartTime,
ResourceVersion: "abcdef",
},
},
},
want: metadata + `
kube_configmap_info{configmap="configmap1",namespace="ns1"} 1
kube_configmap_info{configmap="configmap2",namespace="ns2"} 1
kube_configmap_created{configmap="configmap2",namespace="ns2"} 1.501569018e+09
kube_configmap_metadata_resource_version{configmap="configmap1",namespace="ns1",resource_version="123456"} 1
kube_configmap_metadata_resource_version{configmap="configmap2",namespace="ns2",resource_version="abcdef"} 1
`,
metrics: []string{"kube_configmap_info", "kube_configmap_created", "kube_configmap_metadata_resource_version"},
},
}
for _, c := range cases {
cmc := &configMapCollector{
store: mockConfigMapStore{
f: func() ([]v1.ConfigMap, error) { return c.configMaps, nil },
},
}
if err := gatherAndCompare(cmc, c.want, c.metrics); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
}

151
collectors/secret.go Normal file
View File

@ -0,0 +1,151 @@
/*
Copyright 2018 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 (
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/context"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
)
var (
descSecretLabelsName = "kube_secret_labels"
descSecretLabelsHelp = "Kubernetes labels converted to Prometheus labels."
descSecretLabelsDefaultLabels = []string{"namespace", "secret"}
descSecretInfo = prometheus.NewDesc(
"kube_secret_info",
"Information about secret.",
[]string{"namespace", "secret"}, nil,
)
descSecretType = prometheus.NewDesc(
"kube_secret_type",
"Type about secret.",
[]string{"namespace", "secret", "type"}, nil,
)
descSecretLabels = prometheus.NewDesc(
descSecretLabelsName,
descSecretLabelsHelp,
descSecretLabelsDefaultLabels, nil,
)
descSecretCreated = prometheus.NewDesc(
"kube_secret_created",
"Unix creation timestamp",
[]string{"namespace", "secret"}, nil,
)
descSecretMetadataResourceVersion = prometheus.NewDesc(
"kube_secret_metadata_resource_version",
"Resource version representing a specific version of secret.",
[]string{"namespace", "secret", "resource_version"}, nil,
)
)
type SecretLister func() ([]v1.Secret, error)
func (l SecretLister) List() ([]v1.Secret, error) {
return l()
}
func RegisterSecretCollector(registry prometheus.Registerer, kubeClient kubernetes.Interface, namespace string) {
client := kubeClient.CoreV1().RESTClient()
glog.Infof("collect secret with %s", client.APIVersion())
slw := cache.NewListWatchFromClient(client, "secrets", namespace, fields.Everything())
sinf := cache.NewSharedInformer(slw, &v1.Secret{}, resyncPeriod)
secretLister := SecretLister(func() (secrets []v1.Secret, err error) {
for _, m := range sinf.GetStore().List() {
secrets = append(secrets, *m.(*v1.Secret))
}
return secrets, nil
})
registry.MustRegister(&secretCollector{store: secretLister})
go sinf.Run(context.Background().Done())
}
type secretStore interface {
List() (secrets []v1.Secret, err error)
}
// secretCollector collects metrics about all secrets in the cluster.
type secretCollector struct {
store secretStore
}
// Describe implements the prometheus.Collector interface.
func (sc *secretCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- descSecretInfo
ch <- descSecretCreated
ch <- descSecretLabels
ch <- descSecretMetadataResourceVersion
ch <- descSecretType
}
// Collect implements the prometheus.Collector interface.
func (sc *secretCollector) Collect(ch chan<- prometheus.Metric) {
secrets, err := sc.store.List()
if err != nil {
ScrapeErrorTotalMetric.With(prometheus.Labels{"resource": "secret"}).Inc()
glog.Errorf("listing secrets failed: %s", err)
return
}
ScrapeErrorTotalMetric.With(prometheus.Labels{"resource": "secret"}).Add(0)
ResourcesPerScrapeMetric.With(prometheus.Labels{"resource": "secret"}).Observe(float64(len(secrets)))
for _, s := range secrets {
sc.collectSecret(ch, s)
}
glog.V(4).Infof("collected %d secrets", len(secrets))
}
func secretLabelsDesc(labelKeys []string) *prometheus.Desc {
return prometheus.NewDesc(
descSecretLabelsName,
descSecretLabelsHelp,
append(descSecretLabelsDefaultLabels, labelKeys...),
nil,
)
}
func (sc *secretCollector) collectSecret(ch chan<- prometheus.Metric, s v1.Secret) {
addConstMetric := func(desc *prometheus.Desc, t prometheus.ValueType, v float64, lv ...string) {
lv = append([]string{s.Namespace, s.Name}, lv...)
ch <- prometheus.MustNewConstMetric(desc, t, v, lv...)
}
addGauge := func(desc *prometheus.Desc, v float64, lv ...string) {
addConstMetric(desc, prometheus.GaugeValue, v, lv...)
}
addGauge(descSecretInfo, 1)
addGauge(descSecretType, 1, string(s.Type))
if !s.CreationTimestamp.IsZero() {
addGauge(descSecretCreated, float64(s.CreationTimestamp.Unix()))
}
labelKeys, labelValues := kubeLabelsToPrometheusLabels(s.Labels)
addGauge(secretLabelsDesc(labelKeys), 1, labelValues...)
addGauge(descSecretMetadataResourceVersion, 1, string(s.ObjectMeta.ResourceVersion))
}

117
collectors/secret_test.go Normal file
View File

@ -0,0 +1,117 @@
/*
Copyright 2018 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"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type mockSecretStore struct {
f func() ([]v1.Secret, error)
}
func (ss mockSecretStore) List() (secrets []v1.Secret, err error) {
return ss.f()
}
func TestSecretCollector(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.
startTime := 1501569018
metav1StartTime := metav1.Unix(int64(startTime), 0)
const metadata = `
# HELP kube_secret_labels Kubernetes labels converted to Prometheus labels.
# TYPE kube_secret_labels gauge
# HELP kube_secret_info Information about secret.
# TYPE kube_secret_info gauge
# HELP kube_secret_type Type about secret.
# TYPE kube_secret_type gauge
# HELP kube_secret_created Unix creation timestamp
# TYPE kube_secret_created gauge
# HELP kube_secret_metadata_resource_version Resource version representing a specific version of secret.
# TYPE kube_secret_metadata_resource_version gauge
`
cases := []struct {
secrets []v1.Secret
metrics []string
want string
}{
{
secrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "secret1",
Namespace: "ns1",
ResourceVersion: "000000",
},
Type: v1.SecretTypeOpaque,
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "secret2",
Namespace: "ns2",
CreationTimestamp: metav1StartTime,
ResourceVersion: "123456",
},
Type: v1.SecretTypeServiceAccountToken,
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "secret3",
Namespace: "ns3",
CreationTimestamp: metav1StartTime,
Labels: map[string]string{"test-3": "test-3"},
ResourceVersion: "abcdef",
},
Type: v1.SecretTypeDockercfg,
},
},
want: metadata + `
kube_secret_info{secret="secret1",namespace="ns1"} 1
kube_secret_info{secret="secret2",namespace="ns2"} 1
kube_secret_info{secret="secret3",namespace="ns3"} 1
kube_secret_type{secret="secret1",namespace="ns1",type="Opaque"} 1
kube_secret_type{secret="secret2",namespace="ns2",type="kubernetes.io/service-account-token"} 1
kube_secret_type{secret="secret3",namespace="ns3",type="kubernetes.io/dockercfg"} 1
kube_secret_created{secret="secret2",namespace="ns2"} 1.501569018e+09
kube_secret_created{secret="secret3",namespace="ns3"} 1.501569018e+09
kube_secret_metadata_resource_version{secret="secret1",namespace="ns1",resource_version="000000"} 1
kube_secret_metadata_resource_version{secret="secret2",namespace="ns2",resource_version="123456"} 1
kube_secret_metadata_resource_version{secret="secret3",namespace="ns3",resource_version="abcdef"} 1
kube_secret_labels{secret="secret3",namespace="ns3",label_test_3="test-3"} 1
kube_secret_labels{secret="secret2",namespace="ns2"} 1
kube_secret_labels{secret="secret1",namespace="ns1"} 1
`,
metrics: []string{"kube_secret_info", "kube_secret_metadata_resource_version", "kube_secret_created", "kube_secret_labels", "kube_secret_type"},
},
}
for _, c := range cases {
sc := &secretCollector{
store: mockSecretStore{
f: func() ([]v1.Secret, error) { return c.secrets, nil },
},
}
if err := gatherAndCompare(sc, c.want, c.metrics); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
}

View File

@ -66,6 +66,8 @@ var (
"namespaces": struct{}{},
"horizontalpodautoscalers": struct{}{},
"endpoints": struct{}{},
"secrets": struct{}{},
"configmaps": struct{}{},
}
availableCollectors = map[string]func(registry prometheus.Registerer, kubeClient clientset.Interface, namespace string){
"cronjobs": kcollectors.RegisterCronJobCollector,
@ -85,6 +87,8 @@ var (
"namespaces": kcollectors.RegisterNamespaceCollector,
"horizontalpodautoscalers": kcollectors.RegisterHorizontalPodAutoScalerCollector,
"endpoints": kcollectors.RegisterEndpointCollector,
"secrets": kcollectors.RegisterSecretCollector,
"configmaps": kcollectors.RegisterConfigMapCollector,
}
)