Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add VolumeAttachment collector #946

Merged
merged 1 commit into from
Oct 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Per group of metrics there is one file for each metrics. See each file for speci
- [StorageClass Metrics](storageclass-metrics.md)
- [ValidatingWebhookConfiguration Metrics](validatingwebhookconfiguration.md)
- [VerticalPodAutoscaler Metrics](verticalpodautoscaler-metrics.md)
- [VolumeAttachment Metrics](volumeattachment-metrics.md)

## Join Metrics

Expand Down
2 changes: 1 addition & 1 deletion docs/cli-arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Usage of ./kube-state-metrics:
--add_dir_header If true, adds the file directory to the header
--alsologtostderr log to standard error as well as files
--apiserver string The URL of the apiserver to use as a master
--collectors string Comma-separated list of collectors to be enabled. Defaults to "certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,limitranges,mutatingwebhookconfigurations,namespaces,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations"
--collectors string Comma-separated list of collectors to be enabled. Defaults to "certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,limitranges,mutatingwebhookconfigurations,namespaces,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments"
--disable-node-non-generic-resource-metrics Disable node non generic resource request and limit metrics
--disable-pod-non-generic-resource-metrics Disable pod non generic resource request and limit metrics
--enable-gzip-encoding Gzip responses when requested by clients via 'Accept-Encoding: gzip' header.
Expand Down
10 changes: 10 additions & 0 deletions docs/volumeattachment-metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# PersistentVolume Metrics

| Metric name| Metric type | Labels/tags | Status |
| ---------- | ----------- | ----------- | ----------- |
| kube_volumeattachment_info | Gauge | `volumeattachment`=&lt;volumeattachment-name&gt; <br> `attacher`=&lt;attacher-name&gt; <br> `nodeName`=&lt;node-name&gt; | EXPERIMENTAL |
| kube_volumeattachment_created | Gauge | `volumeattachment`=&lt;volumeattachment-name&gt; | EXPERIMENTAL |
| kube_volumeattachment_labels | Gauge | `volumeattachment`=&lt;volumeattachment-name&gt; <br> `label_VOLUMEATTACHMENT_LABEL`=&lt;VOLUMEATTACHMENT_LABEL&gt; | EXPERIMENTAL |
| kube_volumeattachment_spec_source_persistentvolume | Gauge | `volumeattachment`=&lt;volumeattachment-name&gt; <br> `volumename`=&lt;persistentvolume-name&gt; | EXPERIMENTAL |
| kube_volumeattachment_status_attached | Gauge | `volumeattachment`=&lt;volumeattachment-name&gt; | EXPERIMENTAL |
| kube_volumeattachment_status_attachment_metadata | Gauge | `volumeattachment`=&lt;volumeattachment-name&gt; <br> `metadata_METADATA_KEY`=&lt;METADATA_VALUE&gt; | EXPERIMENTAL |
1 change: 1 addition & 0 deletions examples/autosharding/cluster-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ rules:
- storage.k8s.io
resources:
- storageclasses
- volumeattachments
verbs:
- list
- watch
1 change: 1 addition & 0 deletions examples/standard/cluster-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ rules:
- storage.k8s.io
resources:
- storageclasses
- volumeattachments
verbs:
- list
- watch
Expand Down
6 changes: 6 additions & 0 deletions internal/store/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
extensions "k8s.io/api/extensions/v1beta1"
policy "k8s.io/api/policy/v1beta1"
storagev1 "k8s.io/api/storage/v1"
storagev1beta1 "k8s.io/api/storage/v1beta1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use storagev1 for VolumeAttachment. It has been promoted to v1 since 1.13 if I am not mistaken.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should even support the watching and listing of VolumeAttachments created under v1beta1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it graduated to GA in 1.13 (as also stated in the PR). I started with storagev1 originally, but it failed in a Kubernetes 1.14 cluster -- kube-state-metrics got v1beta1 objects returned and paniced.

On the other hand, following the compatibility chart from the README we need to support Kubernetes 1.11? In this case, only storagev1beta1 is available.

vpaautoscaling "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1beta2"
vpaclientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
clientset "k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -172,6 +173,7 @@ var availableStores = map[string]func(f *Builder) *metricsstore.MetricsStore{
"statefulsets": func(b *Builder) *metricsstore.MetricsStore { return b.buildStatefulSetStore() },
"storageclasses": func(b *Builder) *metricsstore.MetricsStore { return b.buildStorageClassStore() },
"validatingwebhookconfigurations": func(b *Builder) *metricsstore.MetricsStore { return b.buildValidatingWebhookConfigurationStore() },
"volumeattachments": func(b *Builder) *metricsstore.MetricsStore { return b.buildVolumeAttachmentStore() },
"verticalpodautoscalers": func(b *Builder) *metricsstore.MetricsStore { return b.buildVPAStore() },
}

Expand Down Expand Up @@ -288,6 +290,10 @@ func (b *Builder) buildValidatingWebhookConfigurationStore() *metricsstore.Metri
return b.buildStore(validatingWebhookConfigurationMetricFamilies, &admissionregistration.ValidatingWebhookConfiguration{}, createValidatingWebhookConfigurationListWatch)
}

func (b *Builder) buildVolumeAttachmentStore() *metricsstore.MetricsStore {
return b.buildStore(volumeAttachmentMetricFamilies, &storagev1beta1.VolumeAttachment{}, createVolumeAttachmentListWatch)
}

func (b *Builder) buildVPAStore() *metricsstore.MetricsStore {
return b.buildStore(vpaMetricFamilies, &vpaautoscaling.VerticalPodAutoscaler{}, createVPAListWatchFunc(b.vpaClient))
}
Expand Down
6 changes: 5 additions & 1 deletion internal/store/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ func addConditionMetrics(cs v1.ConditionStatus) []*metric.Metric {
}

func kubeLabelsToPrometheusLabels(labels map[string]string) ([]string, []string) {
return mapToPrometheusLabels(labels, "label")
}

func mapToPrometheusLabels(labels map[string]string, prefix string) ([]string, []string) {
labelKeys := make([]string, 0, len(labels))
for k := range labels {
labelKeys = append(labelKeys, k)
Expand All @@ -66,7 +70,7 @@ func kubeLabelsToPrometheusLabels(labels map[string]string) ([]string, []string)

labelValues := make([]string, 0, len(labels))
for i, k := range labelKeys {
labelKeys[i] = "label_" + sanitizeLabelName(k)
labelKeys[i] = prefix + "_" + sanitizeLabelName(k)
labelValues = append(labelValues, labels[k])
}
return labelKeys, labelValues
Expand Down
164 changes: 164 additions & 0 deletions internal/store/volumeattachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
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 store

import (
storagev1beta1 "k8s.io/api/storage/v1beta1"
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"

"k8s.io/kube-state-metrics/pkg/metric"
)

var (
descVolumeAttachmentLabelsName = "kube_volumeattachment_labels"
descVolumeAttachmentLabelsHelp = "Kubernetes labels converted to Prometheus labels."
descVolumeAttachmentLabelsDefaultLabels = []string{"volumeattachment"}

volumeAttachmentMetricFamilies = []metric.FamilyGenerator{
{
Name: descVolumeAttachmentLabelsName,
Type: metric.Gauge,
Help: descVolumeAttachmentLabelsHelp,
GenerateFunc: wrapVolumeAttachmentFunc(func(va *storagev1beta1.VolumeAttachment) *metric.Family {
labelKeys, labelValues := kubeLabelsToPrometheusLabels(va.Labels)
return &metric.Family{
Metrics: []*metric.Metric{
{
LabelKeys: labelKeys,
LabelValues: labelValues,
Value: 1,
},
},
}
}),
},
{
Name: "kube_volumeattachment_info",
Type: metric.Gauge,
Help: "Information about volumeattachment.",
GenerateFunc: wrapVolumeAttachmentFunc(func(va *storagev1beta1.VolumeAttachment) *metric.Family {
return &metric.Family{
Metrics: []*metric.Metric{
{
LabelKeys: []string{"attacher", "nodeName"},
LabelValues: []string{va.Spec.Attacher, va.Spec.NodeName},
Value: 1,
},
},
}
}),
},
{
Name: "kube_volumeattachment_created",
Type: metric.Gauge,
Help: "Unix creation timestamp",
GenerateFunc: wrapVolumeAttachmentFunc(func(va *storagev1beta1.VolumeAttachment) *metric.Family {
if !va.CreationTimestamp.IsZero() {
m := metric.Metric{
LabelKeys: nil,
LabelValues: nil,
Value: float64(va.CreationTimestamp.Unix()),
}
return &metric.Family{Metrics: []*metric.Metric{&m}}
}
return &metric.Family{Metrics: []*metric.Metric{}}
}),
},
{
Name: "kube_volumeattachment_spec_source_persistentvolume",
Type: metric.Gauge,
Help: "PersistentVolume source reference.",
GenerateFunc: wrapVolumeAttachmentFunc(func(va *storagev1beta1.VolumeAttachment) *metric.Family {
if va.Spec.Source.PersistentVolumeName != nil {
return &metric.Family{
Metrics: []*metric.Metric{
{
LabelKeys: []string{"volumename"},
LabelValues: []string{*va.Spec.Source.PersistentVolumeName},
Value: 1,
},
},
}
}
return &metric.Family{}
}),
},
{
Name: "kube_volumeattachment_status_attached",
Type: metric.Gauge,
Help: "Information about volumeattachment.",
GenerateFunc: wrapVolumeAttachmentFunc(func(va *storagev1beta1.VolumeAttachment) *metric.Family {
return &metric.Family{
Metrics: []*metric.Metric{
{
LabelKeys: nil,
LabelValues: nil,
Value: boolFloat64(va.Status.Attached),
},
},
}
}),
},
{
Name: "kube_volumeattachment_status_attachment_metadata",
Type: metric.Gauge,
Help: "volumeattachment metadata.",
GenerateFunc: wrapVolumeAttachmentFunc(func(va *storagev1beta1.VolumeAttachment) *metric.Family {
labelKeys, labelValues := mapToPrometheusLabels(va.Status.AttachmentMetadata, "metadata")
return &metric.Family{
Metrics: []*metric.Metric{
{
LabelKeys: labelKeys,
LabelValues: labelValues,
Value: 1,
},
},
}
}),
},
}
)

func wrapVolumeAttachmentFunc(f func(*storagev1beta1.VolumeAttachment) *metric.Family) func(interface{}) *metric.Family {
return func(obj interface{}) *metric.Family {
va := obj.(*storagev1beta1.VolumeAttachment)

metricFamily := f(va)

for _, m := range metricFamily.Metrics {
m.LabelKeys = append(descVolumeAttachmentLabelsDefaultLabels, m.LabelKeys...)
m.LabelValues = append([]string{va.Name}, m.LabelValues...)
}

return metricFamily
}
}

func createVolumeAttachmentListWatch(kubeClient clientset.Interface, _ string) cache.ListerWatcher {
return &cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
return kubeClient.StorageV1beta1().VolumeAttachments().List(opts)
},
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
return kubeClient.StorageV1beta1().VolumeAttachments().Watch(opts)
},
}
}
96 changes: 96 additions & 0 deletions internal/store/volumeattachment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
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 store

import (
storagev1beta1 "k8s.io/api/storage/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"testing"

"k8s.io/kube-state-metrics/pkg/metric"
)

func TestVolumeAttachmentStore(t *testing.T) {
const metadata = `
# HELP kube_volumeattachment_created Unix creation timestamp
# HELP kube_volumeattachment_info Information about volumeattachment.
# HELP kube_volumeattachment_labels Kubernetes labels converted to Prometheus labels.
# HELP kube_volumeattachment_spec_source_persistentvolume PersistentVolume source reference.
# HELP kube_volumeattachment_status_attached Information about volumeattachment.
# HELP kube_volumeattachment_status_attachment_metadata volumeattachment metadata.
# TYPE kube_volumeattachment_created gauge
# TYPE kube_volumeattachment_info gauge
# TYPE kube_volumeattachment_labels gauge
# TYPE kube_volumeattachment_spec_source_persistentvolume gauge
# TYPE kube_volumeattachment_status_attached gauge
# TYPE kube_volumeattachment_status_attachment_metadata gauge
`

var (
volumename = "pvc-44f6ff3f-ba9b-49c4-9b95-8b01c4bd4bab"
cases = []generateMetricsTestCase{
{
Obj: &storagev1beta1.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Generation: 2,
Name: "csi-5ff16a1ad085261021e21c6cb3a6defb979a8794f25a4f90f6285664cff37224",
Labels: map[string]string{
"app": "foobar",
},
},
Spec: storagev1beta1.VolumeAttachmentSpec{
Attacher: "cinder.csi.openstack.org",
NodeName: "node1",
Source: storagev1beta1.VolumeAttachmentSource{
PersistentVolumeName: &volumename,
InlineVolumeSpec: nil,
},
},
Status: storagev1beta1.VolumeAttachmentStatus{
Attached: true,
AttachmentMetadata: map[string]string{
"DevicePath": "/dev/sdd",
},
},
},
Want: metadata + `
kube_volumeattachment_info{attacher="cinder.csi.openstack.org",nodeName="node1",volumeattachment="csi-5ff16a1ad085261021e21c6cb3a6defb979a8794f25a4f90f6285664cff37224"} 1
kube_volumeattachment_labels{label_app="foobar",volumeattachment="csi-5ff16a1ad085261021e21c6cb3a6defb979a8794f25a4f90f6285664cff37224"} 1
kube_volumeattachment_spec_source_persistentvolume{volumeattachment="csi-5ff16a1ad085261021e21c6cb3a6defb979a8794f25a4f90f6285664cff37224",volumename="pvc-44f6ff3f-ba9b-49c4-9b95-8b01c4bd4bab"} 1
kube_volumeattachment_status_attached{volumeattachment="csi-5ff16a1ad085261021e21c6cb3a6defb979a8794f25a4f90f6285664cff37224"} 1
kube_volumeattachment_status_attachment_metadata{metadata_DevicePath="/dev/sdd",volumeattachment="csi-5ff16a1ad085261021e21c6cb3a6defb979a8794f25a4f90f6285664cff37224"} 1
`,
MetricNames: []string{
"kube_volumeattachment_labels",
"kube_volumeattachment_info",
"kube_volumeattachment_created",
"kube_volumeattachment_spec_source_persistentvolume",
"kube_volumeattachment_status_attached",
"kube_volumeattachment_status_attachment_metadata",
},
},
}
)
for i, c := range cases {
c.Func = metric.ComposeMetricGenFuncs(volumeAttachmentMetricFamilies)
c.Headers = metric.ExtractMetricFamilyHeaders(volumeAttachmentMetricFamilies)
if err := c.run(); err != nil {
t.Errorf("unexpected collecting result in %vth run:\n%s", i, err)
}
}
}
1 change: 1 addition & 0 deletions pkg/options/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ var (
"statefulsets": struct{}{},
"storageclasses": struct{}{},
"validatingwebhookconfigurations": struct{}{},
"volumeattachments": struct{}{},
}
)
9 changes: 9 additions & 0 deletions tests/manifests/volumeattachment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
name: volumeattachment
spec:
attacher: attacher
nodeName: minikube
source:
persistentVolumeName: persistentvolume