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

Support filtering label allowlist by "*" #1823

Merged
merged 1 commit into from
Nov 8, 2022
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
2 changes: 1 addition & 1 deletion docs/cli-arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Usage of ./kube-state-metrics:
--metric-allowlist string Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.
--metric-annotations-allowlist string Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional annotations provide a list of resource names in their plural form and Kubernetes annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. A single '*' can be provided per resource instead to allow any annotations, but that has severe performance implications (Example: '=pods=[*]').
--metric-denylist string Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.
--metric-labels-allowlist string Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional labels provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]').
--metric-labels-allowlist string Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional labels provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]'). Additionally, an asterisk (*) can be provided as a key, which will resolve to all resources, i.e., assuming '--resources=deployments,pods', '=*=[*]' will resolve to '=deployments=[*],pods=[*]'.
--metric-opt-in-list string Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists
--namespaces string Comma-separated list of namespaces to be enabled. Defaults to ""
--namespaces-denylist string Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.
Expand Down
10 changes: 9 additions & 1 deletion internal/store/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,19 @@ func (b *Builder) WithAllowAnnotations(annotations map[string][]string) {
func (b *Builder) WithAllowLabels(labels map[string][]string) error {
if len(labels) > 0 {
for label := range labels {
if !resourceExists(label) {
if !resourceExists(label) && label != "*" {
return fmt.Errorf("resource %s does not exist. Available resources: %s", label, strings.Join(availableResources(), ","))
}
}
b.allowLabelsList = labels
// "*" takes precedence over other specifications
if allowedLabels, ok := labels["*"]; ok {
m := make(map[string][]string)
for _, resource := range b.enabledResources {
m[resource] = allowedLabels
}
b.allowLabelsList = m
}
}
return nil
}
Expand Down
115 changes: 115 additions & 0 deletions internal/store/builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright 2022 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 (
"reflect"
"testing"

"k8s.io/kube-state-metrics/v2/pkg/options"
)

type LabelsAllowList options.LabelsAllowList

type expectedError struct {
expectedResourceError bool
expectedLabelError bool
expectedNotEqual bool
}

func TestWithAllowLabels(t *testing.T) {
tests := []struct {
Desc string
LabelsAllowlist map[string][]string
EnabledResources []string
Wanted LabelsAllowList
err expectedError
}{
{
Desc: "wildcard key-value as the only element",
LabelsAllowlist: map[string][]string{"*": {"*"}},
EnabledResources: []string{"cronjobs", "pods", "deployments"},
Wanted: LabelsAllowList(map[string][]string{
"deployments": {"*"},
"pods": {"*"},
"cronjobs": {"*"},
}),
},
{
Desc: "wildcard key-value as not the only element",
LabelsAllowlist: map[string][]string{"*": {"*"}, "pods": {"*"}, "cronjobs": {"*"}},
EnabledResources: []string{"cronjobs", "pods", "deployments"},
Wanted: LabelsAllowList(map[string][]string{
"deployments": {"*"},
"pods": {"*"},
"cronjobs": {"*"},
}),
},
{
Desc: "wildcard key-value as not the only element, with resource mismatch",
LabelsAllowlist: map[string][]string{"*": {"*"}, "pods": {"*"}, "cronjobs": {"*"}, "configmaps": {"*"}},
EnabledResources: []string{"cronjobs", "pods", "deployments"},
Wanted: LabelsAllowList{},
err: expectedError{
expectedNotEqual: true,
},
},
{
Desc: "wildcard key-value as not the only element, with other mutually-exclusive keys",
LabelsAllowlist: map[string][]string{"*": {"*"}, "foo": {"*"}, "bar": {"*"}, "cronjobs": {"*"}},
EnabledResources: []string{"cronjobs", "pods", "deployments"},
Wanted: LabelsAllowList(nil),
err: expectedError{
expectedLabelError: true,
},
},
{
Desc: "wildcard key-value as not the only element, with other resources that do not exist",
LabelsAllowlist: map[string][]string{"*": {"*"}, "cronjobs": {"*"}},
EnabledResources: []string{"cronjobs", "pods", "deployments", "foo", "bar"},
Wanted: LabelsAllowList{},
err: expectedError{
expectedResourceError: true,
},
},
}

for _, test := range tests {
b := NewBuilder()

// Set the enabled resources.
err := b.WithEnabledResources(test.EnabledResources)
if err != nil && !test.err.expectedResourceError {
t.Log("Did not expect error while setting resources (--resources).")
t.Errorf("Test error for Desc: %s. Got Error: %v", test.Desc, err)
}

// Resolve the allow list.
err = b.WithAllowLabels(test.LabelsAllowlist)
if err != nil && !test.err.expectedLabelError {
t.Log("Did not expect error while parsing allow list labels (--metric-labels-allowlist).")
t.Errorf("Test error for Desc: %s. Got Error: %v", test.Desc, err)
}
resolvedAllowLabels := LabelsAllowList(b.allowLabelsList)

// Evaluate.
if !reflect.DeepEqual(resolvedAllowLabels, test.Wanted) && !test.err.expectedNotEqual {
t.Log("Expected maps to be equal.")
t.Errorf("Test error for Desc: %s\n Want: \n%+v\n Got: \n%#+v", test.Desc, test.Wanted, resolvedAllowLabels)
}
}
}
2 changes: 1 addition & 1 deletion pkg/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (o *Options) AddFlags() {
o.flags.Var(&o.MetricDenylist, "metric-denylist", "Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.")
o.flags.Var(&o.MetricOptInList, "metric-opt-in-list", "Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists")
o.flags.Var(&o.AnnotationsAllowList, "metric-annotations-allowlist", "Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional annotations provide a list of resource names in their plural form and Kubernetes annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. A single '*' can be provided per resource instead to allow any annotations, but that has severe performance implications (Example: '=pods=[*]').")
o.flags.Var(&o.LabelsAllowList, "metric-labels-allowlist", "Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional labels provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]').")
o.flags.Var(&o.LabelsAllowList, "metric-labels-allowlist", "Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the metric contains only name and namespace labels. To include additional labels provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]'). Additionally, an asterisk (*) can be provided as a key, which will resolve to all resources, i.e., assuming '--resources=deployments,pods', '=*=[*]' will resolve to '=deployments=[*],pods=[*]'.")
o.flags.Int32Var(&o.Shard, "shard", int32(0), "The instances shard nominal (zero indexed) within the total number of shards. (default 0)")
o.flags.IntVar(&o.TotalShards, "total-shards", 1, "The total number of shards. Sharding is disabled when total shards is set to 1.")

Expand Down
9 changes: 9 additions & 0 deletions pkg/options/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,15 @@ func TestLabelsAllowListSet(t *testing.T) {
"bar",
"*"}}),
},
{
Desc: "with key as wildcard",
Value: "*=[*]",
Wanted: LabelsAllowList(map[string][]string{
"*": {
"*",
},
}),
},
}

for _, test := range tests {
Expand Down