mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
Add labels to PVCollector bound/unbound PVC metrics for VolumeAttributesClass Feature (#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
This commit is contained in:
parent
c01bc31fa2
commit
16c2ad5b84
@ -23,8 +23,10 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/component-base/metrics"
|
"k8s.io/component-base/metrics"
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
|
storagehelpers "k8s.io/component-helpers/storage/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
metricutil "k8s.io/kubernetes/pkg/volume/util"
|
metricutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,10 +41,11 @@ const (
|
|||||||
unboundPVCKey = "unbound_pvc_count"
|
unboundPVCKey = "unbound_pvc_count"
|
||||||
|
|
||||||
// Label names.
|
// Label names.
|
||||||
namespaceLabel = "namespace"
|
namespaceLabel = "namespace"
|
||||||
storageClassLabel = "storage_class"
|
storageClassLabel = "storage_class"
|
||||||
pluginNameLabel = "plugin_name"
|
volumeAttributesClassLabel = "volume_attributes_class"
|
||||||
volumeModeLabel = "volume_mode"
|
pluginNameLabel = "plugin_name"
|
||||||
|
volumeModeLabel = "volume_mode"
|
||||||
|
|
||||||
// String to use when plugin name cannot be determined
|
// String to use when plugin name cannot be determined
|
||||||
pluginNameNotAvailable = "N/A"
|
pluginNameNotAvailable = "N/A"
|
||||||
@ -86,6 +89,19 @@ type pvAndPVCCountCollector struct {
|
|||||||
pluginMgr *volume.VolumePluginMgr
|
pluginMgr *volume.VolumePluginMgr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Holds all dimensions for bound/unbound PVC metrics
|
||||||
|
type pvcBindingMetricDimensions struct {
|
||||||
|
namespace, storageClassName, volumeAttributesClassName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPVCMetricDimensions(pvc *v1.PersistentVolumeClaim) pvcBindingMetricDimensions {
|
||||||
|
return pvcBindingMetricDimensions{
|
||||||
|
namespace: pvc.Namespace,
|
||||||
|
storageClassName: storagehelpers.GetPersistentVolumeClaimClass(pvc),
|
||||||
|
volumeAttributesClassName: ptr.Deref(pvc.Spec.VolumeAttributesClassName, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if our collector implements necessary collector interface
|
// Check if our collector implements necessary collector interface
|
||||||
var _ metrics.StableCollector = &pvAndPVCCountCollector{}
|
var _ metrics.StableCollector = &pvAndPVCCountCollector{}
|
||||||
|
|
||||||
@ -109,12 +125,12 @@ var (
|
|||||||
boundPVCCountDesc = metrics.NewDesc(
|
boundPVCCountDesc = metrics.NewDesc(
|
||||||
metrics.BuildFQName("", pvControllerSubsystem, boundPVCKey),
|
metrics.BuildFQName("", pvControllerSubsystem, boundPVCKey),
|
||||||
"Gauge measuring number of persistent volume claim currently bound",
|
"Gauge measuring number of persistent volume claim currently bound",
|
||||||
[]string{namespaceLabel}, nil,
|
[]string{namespaceLabel, storageClassLabel, volumeAttributesClassLabel}, nil,
|
||||||
metrics.ALPHA, "")
|
metrics.ALPHA, "")
|
||||||
unboundPVCCountDesc = metrics.NewDesc(
|
unboundPVCCountDesc = metrics.NewDesc(
|
||||||
metrics.BuildFQName("", pvControllerSubsystem, unboundPVCKey),
|
metrics.BuildFQName("", pvControllerSubsystem, unboundPVCKey),
|
||||||
"Gauge measuring number of persistent volume claim currently unbound",
|
"Gauge measuring number of persistent volume claim currently unbound",
|
||||||
[]string{namespaceLabel}, nil,
|
[]string{namespaceLabel, storageClassLabel, volumeAttributesClassLabel}, nil,
|
||||||
metrics.ALPHA, "")
|
metrics.ALPHA, "")
|
||||||
|
|
||||||
volumeOperationErrorsMetric = metrics.NewCounterVec(
|
volumeOperationErrorsMetric = metrics.NewCounterVec(
|
||||||
@ -218,32 +234,32 @@ func (collector *pvAndPVCCountCollector) pvCollect(ch chan<- metrics.Metric) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (collector *pvAndPVCCountCollector) pvcCollect(ch chan<- metrics.Metric) {
|
func (collector *pvAndPVCCountCollector) pvcCollect(ch chan<- metrics.Metric) {
|
||||||
boundNumberByNamespace := make(map[string]int)
|
boundNumber := make(map[pvcBindingMetricDimensions]int)
|
||||||
unboundNumberByNamespace := make(map[string]int)
|
unboundNumber := make(map[pvcBindingMetricDimensions]int)
|
||||||
for _, pvcObj := range collector.pvcLister.List() {
|
for _, pvcObj := range collector.pvcLister.List() {
|
||||||
pvc, ok := pvcObj.(*v1.PersistentVolumeClaim)
|
pvc, ok := pvcObj.(*v1.PersistentVolumeClaim)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pvc.Status.Phase == v1.ClaimBound {
|
if pvc.Status.Phase == v1.ClaimBound {
|
||||||
boundNumberByNamespace[pvc.Namespace]++
|
boundNumber[getPVCMetricDimensions(pvc)]++
|
||||||
} else {
|
} else {
|
||||||
unboundNumberByNamespace[pvc.Namespace]++
|
unboundNumber[getPVCMetricDimensions(pvc)]++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for namespace, number := range boundNumberByNamespace {
|
for pvcLabels, number := range boundNumber {
|
||||||
ch <- metrics.NewLazyConstMetric(
|
ch <- metrics.NewLazyConstMetric(
|
||||||
boundPVCCountDesc,
|
boundPVCCountDesc,
|
||||||
metrics.GaugeValue,
|
metrics.GaugeValue,
|
||||||
float64(number),
|
float64(number),
|
||||||
namespace)
|
pvcLabels.namespace, pvcLabels.storageClassName, pvcLabels.volumeAttributesClassName)
|
||||||
}
|
}
|
||||||
for namespace, number := range unboundNumberByNamespace {
|
for pvcLabels, number := range unboundNumber {
|
||||||
ch <- metrics.NewLazyConstMetric(
|
ch <- metrics.NewLazyConstMetric(
|
||||||
unboundPVCCountDesc,
|
unboundPVCCountDesc,
|
||||||
metrics.GaugeValue,
|
metrics.GaugeValue,
|
||||||
float64(number),
|
float64(number),
|
||||||
namespace)
|
pvcLabels.namespace, pvcLabels.storageClassName, pvcLabels.volumeAttributesClassName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,9 @@ import (
|
|||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/component-base/metrics/testutil"
|
"k8s.io/component-base/metrics/testutil"
|
||||||
"k8s.io/component-helpers/storage/ephemeral"
|
"k8s.io/component-helpers/storage/ephemeral"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
|
kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||||
|
"k8s.io/kubernetes/test/e2e/feature"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
|
e2emetrics "k8s.io/kubernetes/test/e2e/framework/metrics"
|
||||||
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
||||||
@ -499,10 +501,11 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
|
|||||||
// Test for pv controller metrics, concretely: bound/unbound pv/pvc count.
|
// Test for pv controller metrics, concretely: bound/unbound pv/pvc count.
|
||||||
ginkgo.Describe("PVController", func() {
|
ginkgo.Describe("PVController", func() {
|
||||||
const (
|
const (
|
||||||
classKey = "storage_class"
|
namespaceKey = "namespace"
|
||||||
namespaceKey = "namespace"
|
pluginNameKey = "plugin_name"
|
||||||
pluginNameKey = "plugin_name"
|
volumeModeKey = "volume_mode"
|
||||||
volumeModeKey = "volume_mode"
|
storageClassKey = "storage_class"
|
||||||
|
volumeAttributeClassKey = "volume_attributes_class"
|
||||||
|
|
||||||
totalPVKey = "pv_collector_total_pv_count"
|
totalPVKey = "pv_collector_total_pv_count"
|
||||||
boundPVKey = "pv_collector_bound_pv_count"
|
boundPVKey = "pv_collector_bound_pv_count"
|
||||||
@ -515,22 +518,24 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
|
|||||||
pv *v1.PersistentVolume
|
pv *v1.PersistentVolume
|
||||||
pvc *v1.PersistentVolumeClaim
|
pvc *v1.PersistentVolumeClaim
|
||||||
|
|
||||||
className = "bound-unbound-count-test-sc"
|
storageClassName = "bound-unbound-count-test-sc"
|
||||||
pvConfig = e2epv.PersistentVolumeConfig{
|
pvConfig = e2epv.PersistentVolumeConfig{
|
||||||
PVSource: v1.PersistentVolumeSource{
|
PVSource: v1.PersistentVolumeSource{
|
||||||
HostPath: &v1.HostPathVolumeSource{Path: "/data"},
|
HostPath: &v1.HostPathVolumeSource{Path: "/data"},
|
||||||
},
|
},
|
||||||
NamePrefix: "pv-test-",
|
NamePrefix: "pv-test-",
|
||||||
StorageClassName: className,
|
StorageClassName: storageClassName,
|
||||||
}
|
}
|
||||||
pvcConfig = e2epv.PersistentVolumeClaimConfig{StorageClassName: &className}
|
// TODO: Insert volumeAttributesClassName into pvcConfig when "VolumeAttributesClass" is GA
|
||||||
|
volumeAttributesClassName = "bound-unbound-count-test-vac"
|
||||||
|
pvcConfig = e2epv.PersistentVolumeClaimConfig{StorageClassName: &storageClassName}
|
||||||
|
|
||||||
e2emetrics = []struct {
|
e2emetrics = []struct {
|
||||||
name string
|
name string
|
||||||
dimension string
|
dimension string
|
||||||
}{
|
}{
|
||||||
{boundPVKey, classKey},
|
{boundPVKey, storageClassKey},
|
||||||
{unboundPVKey, classKey},
|
{unboundPVKey, storageClassKey},
|
||||||
{boundPVCKey, namespaceKey},
|
{boundPVCKey, namespaceKey},
|
||||||
{unboundPVCKey, namespaceKey},
|
{unboundPVCKey, namespaceKey},
|
||||||
}
|
}
|
||||||
@ -556,7 +561,7 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
|
|||||||
if expectValues == nil {
|
if expectValues == nil {
|
||||||
expectValues = make(map[string]int64)
|
expectValues = make(map[string]int64)
|
||||||
}
|
}
|
||||||
// We using relative increment value instead of absolute value to reduce unexpected flakes.
|
// We use relative increment value instead of absolute value to reduce unexpected flakes.
|
||||||
// Concretely, we expect the difference of the updated values and original values for each
|
// Concretely, we expect the difference of the updated values and original values for each
|
||||||
// test suit are equal to expectValues.
|
// test suit are equal to expectValues.
|
||||||
actualValues := calculateRelativeValues(originMetricValues[i],
|
actualValues := calculateRelativeValues(originMetricValues[i],
|
||||||
@ -604,8 +609,8 @@ var _ = utils.SIGDescribe(framework.WithSerial(), "Volume metrics", func() {
|
|||||||
var err error
|
var err error
|
||||||
pv, err = e2epv.CreatePV(ctx, c, f.Timeouts, pv)
|
pv, err = e2epv.CreatePV(ctx, c, f.Timeouts, pv)
|
||||||
framework.ExpectNoError(err, "Error creating pv: %v", err)
|
framework.ExpectNoError(err, "Error creating pv: %v", err)
|
||||||
waitForPVControllerSync(ctx, metricsGrabber, unboundPVKey, classKey)
|
waitForPVControllerSync(ctx, metricsGrabber, unboundPVKey, storageClassKey)
|
||||||
validator(ctx, []map[string]int64{nil, {className: 1}, nil, nil})
|
validator(ctx, []map[string]int64{nil, {storageClassName: 1}, nil, nil})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("should create unbound pvc count metrics for pvc controller after creating pvc only",
|
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() {
|
|||||||
var err error
|
var err error
|
||||||
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
|
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
|
||||||
framework.ExpectNoError(err, "Error creating pv pvc: %v", err)
|
framework.ExpectNoError(err, "Error creating pv pvc: %v", err)
|
||||||
waitForPVControllerSync(ctx, metricsGrabber, boundPVKey, classKey)
|
waitForPVControllerSync(ctx, metricsGrabber, boundPVKey, storageClassKey)
|
||||||
waitForPVControllerSync(ctx, metricsGrabber, boundPVCKey, namespaceKey)
|
waitForPVControllerSync(ctx, metricsGrabber, boundPVCKey, namespaceKey)
|
||||||
validator(ctx, []map[string]int64{{className: 1}, nil, {ns: 1}, nil})
|
validator(ctx, []map[string]int64{{storageClassName: 1}, nil, {ns: 1}, nil})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO: Merge with bound/unbound tests when "VolumeAttributesClass" feature is enabled by default
|
||||||
|
f.It("should create unbound pvc count metrics for pvc controller with volume attributes class dimension after creating pvc only",
|
||||||
|
feature.VolumeAttributesClass, framework.WithFeatureGate(features.VolumeAttributesClass), func(ctx context.Context) {
|
||||||
|
var err error
|
||||||
|
dimensions := []string{namespaceKey, storageClassKey, volumeAttributeClassKey}
|
||||||
|
pvcConfigWithVAC := pvcConfig
|
||||||
|
pvcConfigWithVAC.VolumeAttributesClassName = &volumeAttributesClassName
|
||||||
|
pvcWithVAC := e2epv.MakePersistentVolumeClaim(pvcConfigWithVAC, ns)
|
||||||
|
pvc, err = e2epv.CreatePVC(ctx, c, ns, pvcWithVAC)
|
||||||
|
framework.ExpectNoError(err, "Error creating pvc: %v", err)
|
||||||
|
waitForPVControllerSync(ctx, metricsGrabber, unboundPVCKey, volumeAttributeClassKey)
|
||||||
|
controllerMetrics, err := metricsGrabber.GrabFromControllerManager(ctx)
|
||||||
|
framework.ExpectNoError(err, "Error getting c-m metricValues: %v", err)
|
||||||
|
err = testutil.ValidateMetrics(testutil.Metrics(controllerMetrics), unboundPVCKey, dimensions...)
|
||||||
|
framework.ExpectNoError(err, "Invalid metric in Controller Manager metrics: %q", unboundPVCKey)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: Merge with bound/unbound tests when "VolumeAttributesClass" feature is enabled by default
|
||||||
|
f.It("should create bound pv/pvc count metrics for pvc controller with volume attributes class dimension after creating both pv and pvc",
|
||||||
|
feature.VolumeAttributesClass, framework.WithFeatureGate(features.VolumeAttributesClass), func(ctx context.Context) {
|
||||||
|
var err error
|
||||||
|
dimensions := []string{namespaceKey, storageClassKey, volumeAttributeClassKey}
|
||||||
|
pvcConfigWithVAC := pvcConfig
|
||||||
|
pvcConfigWithVAC.VolumeAttributesClassName = &volumeAttributesClassName
|
||||||
|
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfigWithVAC, ns, true)
|
||||||
|
framework.ExpectNoError(err, "Error creating pv pvc: %v", err)
|
||||||
|
waitForPVControllerSync(ctx, metricsGrabber, boundPVKey, storageClassKey)
|
||||||
|
waitForPVControllerSync(ctx, metricsGrabber, boundPVCKey, volumeAttributeClassKey)
|
||||||
|
controllerMetrics, err := metricsGrabber.GrabFromControllerManager(ctx)
|
||||||
|
framework.ExpectNoError(err, "Error getting c-m metricValues: %v", err)
|
||||||
|
err = testutil.ValidateMetrics(testutil.Metrics(controllerMetrics), boundPVCKey, dimensions...)
|
||||||
|
framework.ExpectNoError(err, "Invalid metric in Controller Manager metrics: %q", boundPVCKey)
|
||||||
|
})
|
||||||
|
|
||||||
ginkgo.It("should create total pv count metrics for with plugin and volume mode labels after creating pv",
|
ginkgo.It("should create total pv count metrics for with plugin and volume mode labels after creating pv",
|
||||||
func(ctx context.Context) {
|
func(ctx context.Context) {
|
||||||
var err error
|
var err error
|
||||||
|
Loading…
Reference in New Issue
Block a user