prefix GVK labels in CustomResourceMonitoring

This will prefix the auto-generated GVK labels for CustomResources with
customresource_ to make it a bit more clear that these labels got generated.

Signed-off-by: Mario Constanti <mario@constanti.de>
This commit is contained in:
Mario Constanti 2023-01-03 13:28:09 +01:00
parent f1288f943a
commit 7130bd0d2b
5 changed files with 190 additions and 14 deletions

View File

@ -49,7 +49,7 @@ spec:
- --resources=certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,foos,horizontalpodautoscalers,ingresses,jobs,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments,verticalpodautoscalers - --resources=certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,foos,horizontalpodautoscalers,ingresses,jobs,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,pods,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments,verticalpodautoscalers
``` ```
NOTE: The `group`, `version`, and `kind` common labels are reserved, and will be overwritten by the values from the `groupVersionKind` field. NOTE: The `customresource_group`, `customresource_version`, and `customresource_kind` common labels are reserved, and will be overwritten by the values from the `groupVersionKind` field.
### Examples ### Examples

View File

@ -26,6 +26,10 @@ import (
"k8s.io/kube-state-metrics/v2/pkg/customresource" "k8s.io/kube-state-metrics/v2/pkg/customresource"
) )
// customResourceState is used to prefix the auto-generated GVK labels as well as an appendix for the metric itself
// if no custom metric name is defined
const customResourceState string = "customresource"
// Metrics is the top level configuration object. // Metrics is the top level configuration object.
type Metrics struct { type Metrics struct {
Spec MetricsSpec `yaml:"spec" json:"spec"` Spec MetricsSpec `yaml:"spec" json:"spec"`
@ -64,7 +68,7 @@ type Resource struct {
func (r Resource) GetMetricNamePrefix() string { func (r Resource) GetMetricNamePrefix() string {
p := r.MetricNamePrefix p := r.MetricNamePrefix
if p == nil { if p == nil {
return "kube_crd" return "kube_" + customResourceState
} }
return *p return *p
} }

View File

@ -17,18 +17,25 @@ limitations under the License.
package customresourcestate package customresourcestate
import ( import (
"encoding/json"
"reflect"
"testing" "testing"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/utils/pointer"
) )
func TestNewCustomResourceMetrics(t *testing.T) { func TestNewCustomResourceMetrics(t *testing.T) {
tests := []struct { tests := []struct {
r Resource r Resource
wantErr bool wantErr bool
name string wantResult *customResourceMetrics
name string
}{ }{
{ {
// https://github.com/kubernetes/kube-state-metrics/issues/1886 // https://github.com/kubernetes/kube-state-metrics/issues/1886
name: "dynamic metric type (not just hardcoded to gauge)", name: "cr metric with dynamic metric type",
r: Resource{ r: Resource{
GroupVersionKind: GroupVersionKind{ GroupVersionKind: GroupVersionKind{
Group: "apps", Group: "apps",
@ -39,6 +46,9 @@ func TestNewCustomResourceMetrics(t *testing.T) {
LabelsFromPath: map[string][]string{ LabelsFromPath: map[string][]string{
"name": {"metadata", "name"}, "name": {"metadata", "name"},
}, },
CommonLabels: map[string]string{
"hello": "world",
},
}, },
Metrics: []Generator{ Metrics: []Generator{
{ {
@ -60,16 +70,178 @@ func TestNewCustomResourceMetrics(t *testing.T) {
}, },
}, },
wantErr: false, wantErr: false,
wantResult: &customResourceMetrics{
MetricNamePrefix: "kube_customresource",
GroupVersionKind: schema.GroupVersionKind{
Group: "apps",
Version: "v1",
Kind: "Deployment",
},
ResourceName: "deployments",
Families: []compiledFamily{
{
Name: "kube_customresource_test_metrics",
Help: "metrics for testing",
Each: &compiledInfo{},
Labels: map[string]string{
"customresource_group": "apps",
"customresource_kind": "Deployment",
"customresource_version": "v1",
"hello": "world",
},
LabelFromPath: map[string]valuePath{
"name": mustCompilePath(t, "metadata", "name"),
},
},
},
},
},
{
name: "cr metric with custom prefix",
r: Resource{
GroupVersionKind: GroupVersionKind{
Group: "apps",
Version: "v1",
Kind: "Deployment",
},
Labels: Labels{
LabelsFromPath: map[string][]string{
"name": {"metadata", "name"},
},
CommonLabels: map[string]string{
"hello": "world",
},
},
Metrics: []Generator{
{
Name: "test_metrics",
Help: "metrics for testing",
Each: Metric{
Type: MetricTypeInfo,
Info: &MetricInfo{
MetricMeta: MetricMeta{
Path: []string{
"metadata",
"annotations",
},
},
LabelFromKey: "test",
},
},
},
},
MetricNamePrefix: pointer.String("apps_deployment"),
},
wantErr: false,
wantResult: &customResourceMetrics{
MetricNamePrefix: "apps_deployment",
GroupVersionKind: schema.GroupVersionKind{
Group: "apps",
Version: "v1",
Kind: "Deployment",
},
ResourceName: "deployments",
Families: []compiledFamily{
{
Name: "apps_deployment_test_metrics",
Help: "metrics for testing",
Each: &compiledInfo{},
Labels: map[string]string{
"customresource_group": "apps",
"customresource_kind": "Deployment",
"customresource_version": "v1",
"hello": "world",
},
LabelFromPath: map[string]valuePath{
"name": mustCompilePath(t, "metadata", "name"),
},
},
},
},
},
{
name: "cr metric with custom prefix - expect error",
r: Resource{
GroupVersionKind: GroupVersionKind{
Group: "apps",
Version: "v1",
Kind: "Deployment",
},
Labels: Labels{
LabelsFromPath: map[string][]string{
"name": {"metadata", "name"},
},
CommonLabels: map[string]string{
"hello": "world",
},
},
Metrics: []Generator{
{
Name: "test_metrics",
Help: "metrics for testing",
Each: Metric{
Type: MetricTypeInfo,
Info: &MetricInfo{
MetricMeta: MetricMeta{
Path: []string{
"metadata",
"annotations",
},
},
LabelFromKey: "test",
},
},
},
},
MetricNamePrefix: pointer.String("apps_deployment"),
},
wantErr: true,
wantResult: &customResourceMetrics{
GroupVersionKind: schema.GroupVersionKind{
Group: "apps",
Version: "v1",
Kind: "Deployment",
},
ResourceName: "deployments",
Families: []compiledFamily{
{
Name: "apps_deployment_test_metrics",
Help: "metrics for testing",
Each: &compiledInfo{},
Labels: map[string]string{
"customresource_group": "apps",
"customresource_kind": "Deployment",
"customresource_version": "v1",
"hello": "world",
},
LabelFromPath: map[string]valuePath{
"name": mustCompilePath(t, "metadata", "name"),
},
},
},
},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
v, err := NewCustomResourceMetrics(tt.r) v, err := NewCustomResourceMetrics(tt.r)
expectedError := v.(*customResourceMetrics).Families[0].Each.Type() != "info" if err != nil {
if (err != nil) != tt.wantErr || expectedError { t.Errorf(err.Error())
t.Errorf("NewCustomResourceMetrics() error = %v, wantErr %v", err, tt.wantErr) }
return
// convert to JSON for easier nil comparison
ttWantJSON, _ := json.Marshal(tt.wantResult)
customResourceMetricJSON, _ := json.Marshal(v.(*customResourceMetrics))
if !tt.wantErr && !reflect.DeepEqual(ttWantJSON, customResourceMetricJSON) {
t.Errorf("NewCustomResourceMetrics: error expected %v", tt.wantErr)
t.Errorf("NewCustomResourceMetrics:\n %#v\n is not deep equal\n %#v", v, tt.wantResult)
}
if tt.wantErr && reflect.DeepEqual(ttWantJSON, customResourceMetricJSON) {
t.Errorf("NewCustomResourceMetrics: error expected %v", tt.wantErr)
t.Errorf("NewCustomResourceMetrics:\n %#v\n is not deep equal\n %#v", v, tt.wantResult)
} }
}) })
} }

View File

@ -38,9 +38,9 @@ func compile(resource Resource) ([]compiledFamily, error) {
if resource.CommonLabels == nil { if resource.CommonLabels == nil {
resource.CommonLabels = map[string]string{} resource.CommonLabels = map[string]string{}
} }
resource.CommonLabels["group"] = resource.GroupVersionKind.Group resource.CommonLabels[customResourceState+"_group"] = resource.GroupVersionKind.Group
resource.CommonLabels["version"] = resource.GroupVersionKind.Version resource.CommonLabels[customResourceState+"_version"] = resource.GroupVersionKind.Version
resource.CommonLabels["kind"] = resource.GroupVersionKind.Kind resource.CommonLabels[customResourceState+"_kind"] = resource.GroupVersionKind.Kind
for _, f := range resource.Metrics { for _, f := range resource.Metrics {
family, err := compileFamily(f, resource) family, err := compileFamily(f, resource)
if err != nil { if err != nil {

View File

@ -333,7 +333,7 @@ func Test_fullName(t *testing.T) {
resource: r(nil), resource: r(nil),
f: count, f: count,
}, },
want: "kube_crd_count", want: "kube_customresource_count",
}, },
{ {
name: "no prefix", name: "no prefix",