diff --git a/docs/README.md b/docs/README.md
index 8e7d3eb9..194990ac 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -60,6 +60,7 @@ Per group of metrics there is one file for each metrics. See each file for speci
- [ResourceQuota Metrics](resourcequota-metrics.md)
- [Service Metrics](service-metrics.md)
- [StatefulSet Metrics](statefulset-metrics.md)
+- [StorageClass Metrics](storageclass-metrics.md)
- [Namespace Metrics](namespace-metrics.md)
- [Horizontal Pod Autoscaler Metrics](horizontalpodautoscaler-metrics.md)
- [Endpoint Metrics](endpoint-metrics.md)
diff --git a/docs/storageclass-metrics.md b/docs/storageclass-metrics.md
new file mode 100644
index 00000000..b3ac5230
--- /dev/null
+++ b/docs/storageclass-metrics.md
@@ -0,0 +1,8 @@
+# StorageClass Metrics
+
+| Metric name| Metric type | Labels/tags | Status |
+| ---------- | ----------- | ----------- | ----------- |
+| kube_storageclass_info | Gauge | `storageclass`=<storageclass-name>
`provisioner`=<storageclass-provisioner>
`reclaimPolicy`=<storageclass-reclaimPolicy>
`volumeBindingMode`=<storageclass-volumeBindingMode> | STABLE |
+| kube_storageclass_labels | Gauge | `storageclass`=<storageclass-name>
`label_STORAGECLASS_LABEL`=<STORAGECLASS_LABEL> | STABLE |
+| kube_storageclass_created | Gauge | `storageclass`=<storageclass-name> | STABLE |
+| kube_storageclass_metadata_resource_version | Gauge | `storageclass`=<storageclass-name>
`resource_version`=<storageclass-resource-version> | STABLE |
diff --git a/internal/collector/builder.go b/internal/collector/builder.go
index 5c28754d..7b0ee242 100644
--- a/internal/collector/builder.go
+++ b/internal/collector/builder.go
@@ -31,6 +31,7 @@ import (
v1 "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
policy "k8s.io/api/policy/v1beta1"
+ storagev1 "k8s.io/api/storage/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
coll "k8s.io/kube-state-metrics/pkg/collector"
@@ -135,6 +136,7 @@ var availableCollectors = map[string]func(f *Builder) *coll.Collector{
"secrets": func(b *Builder) *coll.Collector { return b.buildSecretCollector() },
"services": func(b *Builder) *coll.Collector { return b.buildServiceCollector() },
"statefulsets": func(b *Builder) *coll.Collector { return b.buildStatefulSetCollector() },
+ "storageclasses": func(b *Builder) *coll.Collector { return b.buildStorageClassCollector() },
}
func (b *Builder) buildConfigMapCollector() *coll.Collector {
@@ -217,6 +219,10 @@ func (b *Builder) buildStatefulSetCollector() *coll.Collector {
return b.buildCollector(statefulSetMetricFamilies, &appsv1.StatefulSet{}, createStatefulSetListWatch)
}
+func (b *Builder) buildStorageClassCollector() *coll.Collector {
+ return b.buildCollector(storageClassMetricFamilies, &storagev1.StorageClass{}, createStorageClassListWatch)
+}
+
func (b *Builder) buildPodCollector() *coll.Collector {
return b.buildCollector(podMetricFamilies, &v1.Pod{}, createPodListWatch)
}
diff --git a/internal/collector/storageclass.go b/internal/collector/storageclass.go
new file mode 100644
index 00000000..688cdd59
--- /dev/null
+++ b/internal/collector/storageclass.go
@@ -0,0 +1,122 @@
+/*
+Copyright 2019 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 collector
+
+import (
+ "k8s.io/kube-state-metrics/pkg/metric"
+
+ storagev1 "k8s.io/api/storage/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 (
+ descStorageClassLabelsName = "kube_storageclass_labels"
+ descStorageClassLabelsHelp = "Kubernetes labels converted to Prometheus labels."
+ descStorageClassLabelsDefaultLabels = []string{"storageclass"}
+
+ storageClassMetricFamilies = []metric.FamilyGenerator{
+ {
+ Name: "kube_storageclass_info",
+ Type: metric.Gauge,
+ Help: "Information about storageclass.",
+ GenerateFunc: wrapStorageClassFunc(func(s *storagev1.StorageClass) *metric.Family {
+ m := metric.Metric{
+ LabelKeys: []string{"provisioner", "reclaimPolicy", "volumeBindingMode"},
+ LabelValues: []string{s.Provisioner, string(*s.ReclaimPolicy), string(*s.VolumeBindingMode)},
+ Value: 1,
+ }
+ return &metric.Family{Metrics: []*metric.Metric{&m}}
+ }),
+ },
+ {
+ Name: "kube_storageclass_created",
+ Type: metric.Gauge,
+ Help: "Unix creation timestamp",
+ GenerateFunc: wrapStorageClassFunc(func(s *storagev1.StorageClass) *metric.Family {
+ ms := []*metric.Metric{}
+ if !s.CreationTimestamp.IsZero() {
+ ms = append(ms, &metric.Metric{
+ Value: float64(s.CreationTimestamp.Unix()),
+ })
+ }
+ return &metric.Family{
+ Metrics: ms,
+ }
+ }),
+ },
+ {
+ Name: "kube_storageclass_metadata_resource_version",
+ Type: metric.Gauge,
+ Help: "Resource version representing a specific version of storageclass.",
+ GenerateFunc: wrapStorageClassFunc(func(s *storagev1.StorageClass) *metric.Family {
+ return &metric.Family{
+ Metrics: []*metric.Metric{
+ {
+ LabelKeys: []string{"resource_version"},
+ LabelValues: []string{s.ObjectMeta.ResourceVersion},
+ Value: 1,
+ },
+ },
+ }
+ }),
+ },
+ {
+ Name: descStorageClassLabelsName,
+ Type: metric.Gauge,
+ Help: descStorageClassLabelsHelp,
+ GenerateFunc: wrapStorageClassFunc(func(s *storagev1.StorageClass) *metric.Family {
+ labelKeys, labelValues := kubeLabelsToPrometheusLabels(s.Labels)
+ return &metric.Family{
+ Metrics: []*metric.Metric{
+ {
+ LabelKeys: labelKeys,
+ LabelValues: labelValues,
+ Value: 1,
+ },
+ },
+ }
+ }),
+ },
+ }
+)
+
+func wrapStorageClassFunc(f func(*storagev1.StorageClass) *metric.Family) func(interface{}) *metric.Family {
+ return func(obj interface{}) *metric.Family {
+ storageClass := obj.(*storagev1.StorageClass)
+
+ metricFamily := f(storageClass)
+
+ for _, m := range metricFamily.Metrics {
+ m.LabelKeys = append(descStorageClassLabelsDefaultLabels, m.LabelKeys...)
+ m.LabelValues = append([]string{storageClass.Name}, m.LabelValues...)
+ }
+
+ return metricFamily
+ }
+}
+
+func createStorageClassListWatch(kubeClient clientset.Interface, ns string) cache.ListerWatcher {
+ return &cache.ListWatch{
+ ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
+ return kubeClient.StorageV1().StorageClasses().List(opts)
+ },
+ WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
+ return kubeClient.StorageV1().StorageClasses().Watch(opts)
+ },
+ }
+}
diff --git a/internal/collector/storageclass_test.go b/internal/collector/storageclass_test.go
new file mode 100644
index 00000000..aaac6909
--- /dev/null
+++ b/internal/collector/storageclass_test.go
@@ -0,0 +1,121 @@
+/*
+Copyright 2019 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 collector
+
+import (
+ "testing"
+
+ v1 "k8s.io/api/core/v1"
+ storagev1 "k8s.io/api/storage/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/kube-state-metrics/pkg/metric"
+)
+
+func TestStorageClassCollector(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)
+ reclaimPolicy := v1.PersistentVolumeReclaimDelete
+ volumeBindingMode := storagev1.VolumeBindingImmediate
+
+ const metadata = `
+ # HELP kube_storageclass_labels Kubernetes labels converted to Prometheus labels.
+ # TYPE kube_storageclass_labels gauge
+ # HELP kube_storageclass_info Information about storageclass.
+ # TYPE kube_storageclass_info gauge
+ # HELP kube_storageclass_created Unix creation timestamp
+ # TYPE kube_storageclass_created gauge
+ # HELP kube_storageclass_metadata_resource_version Resource version representing a specific version of secret.
+ # TYPE kube_storageclass_metadata_resource_version gauge
+ `
+ cases := []generateMetricsTestCase{
+ {
+ Obj: &storagev1.StorageClass{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test_storageclass-info",
+ },
+ Provisioner: "kubernetes.io/rbd",
+ ReclaimPolicy: &reclaimPolicy,
+ VolumeBindingMode: &volumeBindingMode,
+ },
+ Want: `
+ kube_storageclass_info{storageclass="test_storageclass-info",provisioner="kubernetes.io/rbd",reclaimPolicy="Delete",volumeBindingMode="Immediate"} 1
+ `,
+ MetricNames: []string{
+ "kube_storageclass_info",
+ },
+ },
+ {
+ Obj: &storagev1.StorageClass{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test_kube_storageclass_created",
+ CreationTimestamp: metav1StartTime,
+ },
+ Provisioner: "kubernetes.io/rbd",
+ ReclaimPolicy: &reclaimPolicy,
+ VolumeBindingMode: &volumeBindingMode,
+ },
+ Want: `
+ kube_storageclass_created{storageclass="test_kube_storageclass_created"} 1.501569018e+09
+ `,
+ MetricNames: []string{
+ "kube_storageclass_created",
+ },
+ },
+ {
+ Obj: &storagev1.StorageClass{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test_kube_storageclass_resource_version",
+ ResourceVersion: "abcdef",
+ },
+ Provisioner: "kubernetes.io/rbd",
+ ReclaimPolicy: &reclaimPolicy,
+ VolumeBindingMode: &volumeBindingMode,
+ },
+ Want: `
+ kube_storageclass_metadata_resource_version{storageclass="test_kube_storageclass_resource_version",resource_version="abcdef"} 1
+ `,
+ MetricNames: []string{
+ "kube_storageclass_metadata_resource_version",
+ },
+ },
+ {
+ Obj: &storagev1.StorageClass{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test_storageclass-labels",
+ Labels: map[string]string{
+ "foo": "bar",
+ },
+ },
+ Provisioner: "kubernetes.io/rbd",
+ ReclaimPolicy: &reclaimPolicy,
+ VolumeBindingMode: &volumeBindingMode,
+ },
+ Want: `
+ kube_storageclass_labels{storageclass="test_storageclass-labels",label_foo="bar"} 1
+ `,
+ MetricNames: []string{
+ "kube_storageclass_labels",
+ },
+ },
+ }
+ for i, c := range cases {
+ c.Func = metric.ComposeMetricGenFuncs(storageClassMetricFamilies)
+ if err := c.run(); err != nil {
+ t.Errorf("unexpected collecting result in %vth run:\n%s", i, err)
+ }
+ }
+}
diff --git a/kubernetes/kube-state-metrics-cluster-role.yaml b/kubernetes/kube-state-metrics-cluster-role.yaml
index de4099af..b7c57bca 100644
--- a/kubernetes/kube-state-metrics-cluster-role.yaml
+++ b/kubernetes/kube-state-metrics-cluster-role.yaml
@@ -50,4 +50,8 @@ rules:
resources:
- certificatesigningrequests
verbs: ["list", "watch"]
+- apiGroups: ["storage.k8s.io"]
+ resources:
+ - storageclasses
+ verbs: ["list", "watch"]
diff --git a/pkg/options/collector.go b/pkg/options/collector.go
index 7ecc1751..56345c34 100644
--- a/pkg/options/collector.go
+++ b/pkg/options/collector.go
@@ -48,5 +48,6 @@ var (
"secrets": struct{}{},
"services": struct{}{},
"statefulsets": struct{}{},
+ "storageclasses": struct{}{},
}
)
diff --git a/tests/manifests/storageclass.yaml b/tests/manifests/storageclass.yaml
new file mode 100644
index 00000000..1a776ca9
--- /dev/null
+++ b/tests/manifests/storageclass.yaml
@@ -0,0 +1,7 @@
+apiVersion: storage.k8s.io/v1
+kind: StorageClass
+metadata:
+ name: storageclass
+provisioner: kubernetes.io/rbd
+reclaimPolicy: Delete
+volumeBindingMode: Immediate
\ No newline at end of file
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 400bfdfc..1cb4f36f 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -126,6 +126,7 @@ k8s.io/api/certificates/v1beta1
k8s.io/api/core/v1
k8s.io/api/extensions/v1beta1
k8s.io/api/policy/v1beta1
+k8s.io/api/storage/v1
k8s.io/api/apps/v1beta1
k8s.io/api/admissionregistration/v1beta1
k8s.io/api/autoscaling/v1
@@ -151,7 +152,6 @@ k8s.io/api/scheduling/v1
k8s.io/api/scheduling/v1alpha1
k8s.io/api/scheduling/v1beta1
k8s.io/api/settings/v1alpha1
-k8s.io/api/storage/v1
k8s.io/api/storage/v1alpha1
k8s.io/api/storage/v1beta1
# k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1