Skip to content

Commit 16c2ad5

Browse files
Add labels to PVCollector bound/unbound PVC metrics for VolumeAttributesClass Feature (kubernetes#126166)
* Add labels to PVCollector bound/unbound PVC metrics * fixup! Add labels to PVCollector bound/unbound PVC metrics * wip: Fix 'Unknown Decorator' * fixup! Add labels to PVCollector bound/unbound PVC metrics
1 parent c01bc31 commit 16c2ad5

File tree

2 files changed

+84
-29
lines changed

2 files changed

+84
-29
lines changed

pkg/controller/volume/persistentvolume/metrics/metrics.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import (
2323
v1 "k8s.io/api/core/v1"
2424
"k8s.io/component-base/metrics"
2525
"k8s.io/component-base/metrics/legacyregistry"
26+
storagehelpers "k8s.io/component-helpers/storage/volume"
2627
"k8s.io/kubernetes/pkg/volume"
2728
metricutil "k8s.io/kubernetes/pkg/volume/util"
29+
"k8s.io/utils/ptr"
2830
)
2931

3032
const (
@@ -39,10 +41,11 @@ const (
3941
unboundPVCKey = "unbound_pvc_count"
4042

4143
// Label names.
42-
namespaceLabel = "namespace"
43-
storageClassLabel = "storage_class"
44-
pluginNameLabel = "plugin_name"
45-
volumeModeLabel = "volume_mode"
44+
namespaceLabel = "namespace"
45+
storageClassLabel = "storage_class"
46+
volumeAttributesClassLabel = "volume_attributes_class"
47+
pluginNameLabel = "plugin_name"
48+
volumeModeLabel = "volume_mode"
4649

4750
// String to use when plugin name cannot be determined
4851
pluginNameNotAvailable = "N/A"
@@ -86,6 +89,19 @@ type pvAndPVCCountCollector struct {
8689
pluginMgr *volume.VolumePluginMgr
8790
}
8891

92+
// Holds all dimensions for bound/unbound PVC metrics
93+
type pvcBindingMetricDimensions struct {
94+
namespace, storageClassName, volumeAttributesClassName string
95+
}
96+
97+
func getPVCMetricDimensions(pvc *v1.PersistentVolumeClaim) pvcBindingMetricDimensions {
98+
return pvcBindingMetricDimensions{
99+
namespace: pvc.Namespace,
100+
storageClassName: storagehelpers.GetPersistentVolumeClaimClass(pvc),
101+
volumeAttributesClassName: ptr.Deref(pvc.Spec.VolumeAttributesClassName, ""),
102+
}
103+
}
104+
89105
// Check if our collector implements necessary collector interface
90106
var _ metrics.StableCollector = &pvAndPVCCountCollector{}
91107

@@ -109,12 +125,12 @@ var (
109125
boundPVCCountDesc = metrics.NewDesc(
110126
metrics.BuildFQName("", pvControllerSubsystem, boundPVCKey),
111127
"Gauge measuring number of persistent volume claim currently bound",
112-
[]string{namespaceLabel}, nil,
128+
[]string{namespaceLabel, storageClassLabel, volumeAttributesClassLabel}, nil,
113129
metrics.ALPHA, "")
114130
unboundPVCCountDesc = metrics.NewDesc(
115131
metrics.BuildFQName("", pvControllerSubsystem, unboundPVCKey),
116132
"Gauge measuring number of persistent volume claim currently unbound",
117-
[]string{namespaceLabel}, nil,
133+
[]string{namespaceLabel, storageClassLabel, volumeAttributesClassLabel}, nil,
118134
metrics.ALPHA, "")
119135

120136
volumeOperationErrorsMetric = metrics.NewCounterVec(
@@ -218,32 +234,32 @@ func (collector *pvAndPVCCountCollector) pvCollect(ch chan<- metrics.Metric) {
218234
}
219235

220236
func (collector *pvAndPVCCountCollector) pvcCollect(ch chan<- metrics.Metric) {
221-
boundNumberByNamespace := make(map[string]int)
222-
unboundNumberByNamespace := make(map[string]int)
237+
boundNumber := make(map[pvcBindingMetricDimensions]int)
238+
unboundNumber := make(map[pvcBindingMetricDimensions]int)
223239
for _, pvcObj := range collector.pvcLister.List() {
224240
pvc, ok := pvcObj.(*v1.PersistentVolumeClaim)
225241
if !ok {
226242
continue
227243
}
228244
if pvc.Status.Phase == v1.ClaimBound {
229-
boundNumberByNamespace[pvc.Namespace]++
245+
boundNumber[getPVCMetricDimensions(pvc)]++
230246
} else {
231-
unboundNumberByNamespace[pvc.Namespace]++
247+
unboundNumber[getPVCMetricDimensions(pvc)]++
232248
}
233249
}
234-
for namespace, number := range boundNumberByNamespace {
250+
for pvcLabels, number := range boundNumber {
235251
ch <- metrics.NewLazyConstMetric(
236252
boundPVCCountDesc,
237253
metrics.GaugeValue,
238254
float64(number),
239-
namespace)
255+
pvcLabels.namespace, pvcLabels.storageClassName, pvcLabels.volumeAttributesClassName)
240256
}
241-
for namespace, number := range unboundNumberByNamespace {
257+
for pvcLabels, number := range unboundNumber {
242258
ch <- metrics.NewLazyConstMetric(
243259
unboundPVCCountDesc,
244260
metrics.GaugeValue,
245261
float64(number),
246-
namespace)
262+
pvcLabels.namespace, pvcLabels.storageClassName, pvcLabels.volumeAttributesClassName)
247263
}
248264
}
249265

test/e2e/storage/volume_metrics.go

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ import (
3232
clientset "k8s.io/client-go/kubernetes"
3333
"k8s.io/component-base/metrics/testutil"
3434
"k8s.io/component-helpers/storage/ephemeral"
35+
"k8s.io/kubernetes/pkg/features"
3536
kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
37+
"k8s.io/kubernetes/test/e2e/feature"
3638
"k8s.io/kubernetes/test/e2e/framework"
3739
e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
3840
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
@@ -499,10 +501,11 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
499501
// Test for pv controller metrics, concretely: bound/unbound pv/pvc count.
500502
ginkgo.Describe("PVController", func() {
501503
const (
502-
classKey = "storage_class"
503-
namespaceKey = "namespace"
504-
pluginNameKey = "plugin_name"
505-
volumeModeKey = "volume_mode"
504+
namespaceKey = "namespace"
505+
pluginNameKey = "plugin_name"
506+
volumeModeKey = "volume_mode"
507+
storageClassKey = "storage_class"
508+
volumeAttributeClassKey = "volume_attributes_class"
506509

507510
totalPVKey = "pv_collector_total_pv_count"
508511
boundPVKey = "pv_collector_bound_pv_count"
@@ -515,22 +518,24 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
515518
pv *v1.PersistentVolume
516519
pvc *v1.PersistentVolumeClaim
517520

518-
className = "bound-unbound-count-test-sc"
519-
pvConfig = e2epv.PersistentVolumeConfig{
521+
storageClassName = "bound-unbound-count-test-sc"
522+
pvConfig = e2epv.PersistentVolumeConfig{
520523
PVSource: v1.PersistentVolumeSource{
521524
HostPath: &v1.HostPathVolumeSource{Path: "/data"},
522525
},
523526
NamePrefix: "pv-test-",
524-
StorageClassName: className,
527+
StorageClassName: storageClassName,
525528
}
526-
pvcConfig = e2epv.PersistentVolumeClaimConfig{StorageClassName: &className}
529+
// TODO: Insert volumeAttributesClassName into pvcConfig when "VolumeAttributesClass" is GA
530+
volumeAttributesClassName = "bound-unbound-count-test-vac"
531+
pvcConfig = e2epv.PersistentVolumeClaimConfig{StorageClassName: &storageClassName}
527532

528533
e2emetrics = []struct {
529534
name string
530535
dimension string
531536
}{
532-
{boundPVKey, classKey},
533-
{unboundPVKey, classKey},
537+
{boundPVKey, storageClassKey},
538+
{unboundPVKey, storageClassKey},
534539
{boundPVCKey, namespaceKey},
535540
{unboundPVCKey, namespaceKey},
536541
}
@@ -556,7 +561,7 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
556561
if expectValues == nil {
557562
expectValues = make(map[string]int64)
558563
}
559-
// We using relative increment value instead of absolute value to reduce unexpected flakes.
564+
// We use relative increment value instead of absolute value to reduce unexpected flakes.
560565
// Concretely, we expect the difference of the updated values and original values for each
561566
// test suit are equal to expectValues.
562567
actualValues := calculateRelativeValues(originMetricValues[i],
@@ -604,8 +609,8 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
604609
var err error
605610
pv, err = e2epv.CreatePV(ctx, c, f.Timeouts, pv)
606611
framework.ExpectNoError(err, "Error creating pv: %v", err)
607-
waitForPVControllerSync(ctx, metricsGrabber, unboundPVKey, classKey)
608-
validator(ctx, []map[string]int64{nil, {className: 1}, nil, nil})
612+
waitForPVControllerSync(ctx, metricsGrabber, unboundPVKey, storageClassKey)
613+
validator(ctx, []map[string]int64{nil, {storageClassName: 1}, nil, nil})
609614
})
610615

611616
ginkgo.It("should create unbound pvc count metrics for pvc controller after creating pvc only",
@@ -622,11 +627,45 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
622627
var err error
623628
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
624629
framework.ExpectNoError(err, "Error creating pv pvc: %v", err)
625-
waitForPVControllerSync(ctx, metricsGrabber, boundPVKey, classKey)
630+
waitForPVControllerSync(ctx, metricsGrabber, boundPVKey, storageClassKey)
626631
waitForPVControllerSync(ctx, metricsGrabber, boundPVCKey, namespaceKey)
627-
validator(ctx, []map[string]int64{{className: 1}, nil, {ns: 1}, nil})
632+
validator(ctx, []map[string]int64{{storageClassName: 1}, nil, {ns: 1}, nil})
633+
})
634+
635+
// TODO: Merge with bound/unbound tests when "VolumeAttributesClass" feature is enabled by default
636+
f.It("should create unbound pvc count metrics for pvc controller with volume attributes class dimension after creating pvc only",
637+
feature.VolumeAttributesClass, framework.WithFeatureGate(features.VolumeAttributesClass), func(ctx context.Context) {
638+
var err error
639+
dimensions := []string{namespaceKey, storageClassKey, volumeAttributeClassKey}
640+
pvcConfigWithVAC := pvcConfig
641+
pvcConfigWithVAC.VolumeAttributesClassName = &volumeAttributesClassName
642+
pvcWithVAC := e2epv.MakePersistentVolumeClaim(pvcConfigWithVAC, ns)
643+
pvc, err = e2epv.CreatePVC(ctx, c, ns, pvcWithVAC)
644+
framework.ExpectNoError(err, "Error creating pvc: %v", err)
645+
waitForPVControllerSync(ctx, metricsGrabber, unboundPVCKey, volumeAttributeClassKey)
646+
controllerMetrics, err := metricsGrabber.GrabFromControllerManager(ctx)
647+
framework.ExpectNoError(err, "Error getting c-m metricValues: %v", err)
648+
err = testutil.ValidateMetrics(testutil.Metrics(controllerMetrics), unboundPVCKey, dimensions...)
649+
framework.ExpectNoError(err, "Invalid metric in Controller Manager metrics: %q", unboundPVCKey)
650+
})
628651

652+
// TODO: Merge with bound/unbound tests when "VolumeAttributesClass" feature is enabled by default
653+
f.It("should create bound pv/pvc count metrics for pvc controller with volume attributes class dimension after creating both pv and pvc",
654+
feature.VolumeAttributesClass, framework.WithFeatureGate(features.VolumeAttributesClass), func(ctx context.Context) {
655+
var err error
656+
dimensions := []string{namespaceKey, storageClassKey, volumeAttributeClassKey}
657+
pvcConfigWithVAC := pvcConfig
658+
pvcConfigWithVAC.VolumeAttributesClassName = &volumeAttributesClassName
659+
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfigWithVAC, ns, true)
660+
framework.ExpectNoError(err, "Error creating pv pvc: %v", err)
661+
waitForPVControllerSync(ctx, metricsGrabber, boundPVKey, storageClassKey)
662+
waitForPVControllerSync(ctx, metricsGrabber, boundPVCKey, volumeAttributeClassKey)
663+
controllerMetrics, err := metricsGrabber.GrabFromControllerManager(ctx)
664+
framework.ExpectNoError(err, "Error getting c-m metricValues: %v", err)
665+
err = testutil.ValidateMetrics(testutil.Metrics(controllerMetrics), boundPVCKey, dimensions...)
666+
framework.ExpectNoError(err, "Invalid metric in Controller Manager metrics: %q", boundPVCKey)
629667
})
668+
630669
ginkgo.It("should create total pv count metrics for with plugin and volume mode labels after creating pv",
631670
func(ctx context.Context) {
632671
var err error

0 commit comments

Comments
 (0)