Skip to content

Commit 4e431f6

Browse files
authored
Merge pull request #2234 from xonvanetta/feature/add-wilcard-allowlist-annotations
feat: Support filtering annotations allowlist by "*"
2 parents 48b967a + e4d2bd4 commit 4e431f6

File tree

6 files changed

+122
-26
lines changed

6 files changed

+122
-26
lines changed

docs/cli-arguments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Flags:
5252
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
5353
--logtostderr log to standard error instead of files (default true)
5454
--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.
55-
--metric-annotations-allowlist string Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the annotations metrics are not exposed. To include them, 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=[*]').
55+
--metric-annotations-allowlist string Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the annotations metrics are not exposed. To include them, 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=[*]'). 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=[*]'.
5656
--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.
5757
--metric-labels-allowlist string Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the labels metrics are not exposed. To include them, 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=[*]'.
5858
--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

internal/store/builder.go

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -219,32 +219,43 @@ func (b *Builder) WithCustomResourceStoreFactories(fs ...customresource.Registry
219219
}
220220
}
221221

222-
// WithAllowAnnotations configures which annotations can be returned for metrics
223-
func (b *Builder) WithAllowAnnotations(annotations map[string][]string) {
224-
if len(annotations) > 0 {
225-
b.allowAnnotationsList = annotations
222+
// allowList validates the given map and checks if the resources exists.
223+
// If there is a '*' as key, return new map with all enabled resources.
224+
func (b *Builder) allowList(list map[string][]string) (map[string][]string, error) {
225+
if len(list) == 0 {
226+
return nil, nil
227+
}
228+
229+
for l := range list {
230+
if !resourceExists(l) && l != "*" {
231+
return nil, fmt.Errorf("resource %s does not exist. Available resources: %s", l, strings.Join(availableResources(), ","))
232+
}
233+
}
234+
235+
// "*" takes precedence over other specifications
236+
allowedList, ok := list["*"]
237+
if !ok {
238+
return list, nil
239+
}
240+
m := make(map[string][]string)
241+
for _, resource := range b.enabledResources {
242+
m[resource] = allowedList
226243
}
244+
return m, nil
245+
}
246+
247+
// WithAllowAnnotations configures which annotations can be returned for metrics
248+
func (b *Builder) WithAllowAnnotations(annotations map[string][]string) error {
249+
var err error
250+
b.allowAnnotationsList, err = b.allowList(annotations)
251+
return err
227252
}
228253

229254
// WithAllowLabels configures which labels can be returned for metrics
230255
func (b *Builder) WithAllowLabels(labels map[string][]string) error {
231-
if len(labels) > 0 {
232-
for label := range labels {
233-
if !resourceExists(label) && label != "*" {
234-
return fmt.Errorf("resource %s does not exist. Available resources: %s", label, strings.Join(availableResources(), ","))
235-
}
236-
}
237-
b.allowLabelsList = labels
238-
// "*" takes precedence over other specifications
239-
if allowedLabels, ok := labels["*"]; ok {
240-
m := make(map[string][]string)
241-
for _, resource := range b.enabledResources {
242-
m[resource] = allowedLabels
243-
}
244-
b.allowLabelsList = m
245-
}
246-
}
247-
return nil
256+
var err error
257+
b.allowLabelsList, err = b.allowList(labels)
258+
return err
248259
}
249260

250261
// Build initializes and registers all enabled stores.

internal/store/builder_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,86 @@ func TestWithAllowLabels(t *testing.T) {
113113
}
114114
}
115115
}
116+
117+
func TestWithAllowAnnotations(t *testing.T) {
118+
tests := []struct {
119+
Desc string
120+
AnnotationsAllowlist map[string][]string
121+
EnabledResources []string
122+
Wanted LabelsAllowList
123+
err expectedError
124+
}{
125+
{
126+
Desc: "wildcard key-value as the only element",
127+
AnnotationsAllowlist: map[string][]string{"*": {"*"}},
128+
EnabledResources: []string{"cronjobs", "pods", "deployments"},
129+
Wanted: LabelsAllowList(map[string][]string{
130+
"deployments": {"*"},
131+
"pods": {"*"},
132+
"cronjobs": {"*"},
133+
}),
134+
},
135+
{
136+
Desc: "wildcard key-value as not the only element",
137+
AnnotationsAllowlist: map[string][]string{"*": {"*"}, "pods": {"*"}, "cronjobs": {"*"}},
138+
EnabledResources: []string{"cronjobs", "pods", "deployments"},
139+
Wanted: LabelsAllowList(map[string][]string{
140+
"deployments": {"*"},
141+
"pods": {"*"},
142+
"cronjobs": {"*"},
143+
}),
144+
},
145+
{
146+
Desc: "wildcard key-value as not the only element, with resource mismatch",
147+
AnnotationsAllowlist: map[string][]string{"*": {"*"}, "pods": {"*"}, "cronjobs": {"*"}, "configmaps": {"*"}},
148+
EnabledResources: []string{"cronjobs", "pods", "deployments"},
149+
Wanted: LabelsAllowList{},
150+
err: expectedError{
151+
expectedNotEqual: true,
152+
},
153+
},
154+
{
155+
Desc: "wildcard key-value as not the only element, with other mutually-exclusive keys",
156+
AnnotationsAllowlist: map[string][]string{"*": {"*"}, "foo": {"*"}, "bar": {"*"}, "cronjobs": {"*"}},
157+
EnabledResources: []string{"cronjobs", "pods", "deployments"},
158+
Wanted: LabelsAllowList(nil),
159+
err: expectedError{
160+
expectedLabelError: true,
161+
},
162+
},
163+
{
164+
Desc: "wildcard key-value as not the only element, with other resources that do not exist",
165+
AnnotationsAllowlist: map[string][]string{"*": {"*"}, "cronjobs": {"*"}},
166+
EnabledResources: []string{"cronjobs", "pods", "deployments", "foo", "bar"},
167+
Wanted: LabelsAllowList{},
168+
err: expectedError{
169+
expectedResourceError: true,
170+
},
171+
},
172+
}
173+
174+
for _, test := range tests {
175+
b := NewBuilder()
176+
177+
// Set the enabled resources.
178+
err := b.WithEnabledResources(test.EnabledResources)
179+
if err != nil && !test.err.expectedResourceError {
180+
t.Log("Did not expect error while setting resources (--resources).")
181+
t.Errorf("Test error for Desc: %s. Got Error: %v", test.Desc, err)
182+
}
183+
184+
// Resolve the allow list.
185+
err = b.WithAllowAnnotations(test.AnnotationsAllowlist)
186+
if err != nil && !test.err.expectedLabelError {
187+
t.Log("Did not expect error while parsing allow list annotations (--metric-annotations-allowlist).")
188+
t.Errorf("Test error for Desc: %s. Got Error: %v", test.Desc, err)
189+
}
190+
resolvedAllowAnnotations := LabelsAllowList(b.allowAnnotationsList)
191+
192+
// Evaluate.
193+
if !reflect.DeepEqual(resolvedAllowAnnotations, test.Wanted) && !test.err.expectedNotEqual {
194+
t.Log("Expected maps to be equal.")
195+
t.Errorf("Test error for Desc: %s\n Want: \n%+v\n Got: \n%#+v", test.Desc, test.Wanted, resolvedAllowAnnotations)
196+
}
197+
}
198+
}

pkg/app/server.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
250250
storeBuilder.WithKubeClient(kubeClient)
251251

252252
storeBuilder.WithSharding(opts.Shard, opts.TotalShards)
253-
storeBuilder.WithAllowAnnotations(opts.AnnotationsAllowList)
253+
if err := storeBuilder.WithAllowAnnotations(opts.AnnotationsAllowList); err != nil {
254+
return fmt.Errorf("failed to set up annotations allowlist: %v", err)
255+
}
254256
if err := storeBuilder.WithAllowLabels(opts.LabelsAllowList); err != nil {
255257
return fmt.Errorf("failed to set up labels allowlist: %v", err)
256258
}

pkg/builder/builder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ func (b *Builder) WithFamilyGeneratorFilter(l generator.FamilyGeneratorFilter) {
101101
}
102102

103103
// WithAllowAnnotations configures which annotations can be returned for metrics
104-
func (b *Builder) WithAllowAnnotations(annotations map[string][]string) {
105-
b.internal.WithAllowAnnotations(annotations)
104+
func (b *Builder) WithAllowAnnotations(annotations map[string][]string) error {
105+
return b.internal.WithAllowAnnotations(annotations)
106106
}
107107

108108
// WithAllowLabels configures which labels can be returned for metrics

pkg/builder/types/interfaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ type BuilderInterface interface {
4242
WithCustomResourceClients(cs map[string]interface{})
4343
WithUsingAPIServerCache(u bool)
4444
WithFamilyGeneratorFilter(l generator.FamilyGeneratorFilter)
45-
WithAllowAnnotations(a map[string][]string)
45+
WithAllowAnnotations(a map[string][]string) error
4646
WithAllowLabels(l map[string][]string) error
4747
WithGenerateStoresFunc(f BuildStoresFunc)
4848
DefaultGenerateStoresFunc() BuildStoresFunc

0 commit comments

Comments
 (0)