Skip to content

Commit 9747258

Browse files
merging with latest main
2 parents 378224d + d680e8c commit 9747258

File tree

4 files changed

+152
-2
lines changed

4 files changed

+152
-2
lines changed

docs/developer/cli-arguments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Flags:
4444
--auth-filter If true, requires authentication and authorization through Kubernetes API to access metrics endpoints
4545
--auto-gomemlimit Automatically set GOMEMLIMIT to match container or system memory limit. (experimental)
4646
--auto-gomemlimit-ratio float The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory. (experimental) (default 0.9)
47-
--config string Path to the kube-state-metrics options config file
47+
--config string Path to the kube-state-metrics options config YAML file. If this flag is set, the flags defined in the file override the command line flags.
4848
--continue-without-config If true, kube-state-metrics continues to run even if the config file specified by --config is not present. This is useful for scenarios where config file is not provided at startup but is provided later, for e.g., via configmap. Kube-state-metrics will not exit with an error if the config file is not found, instead watches and reloads when it is created.
4949
--continue-without-custom-resource-state-config-file If true, Kube-state-metrics continues to run even if the config file specified by --custom-resource-state-config-file is not present. This is useful for scenarios where config file is not provided at startup but is provided later, for e.g., via configmap. Kube-state-metrics will not exit with an error if the custom-resource-state-config file is not found, instead watches and reloads when it is created.
5050
--custom-resource-state-config string Inline Custom Resource State Metrics config YAML (experimental)

pkg/app/server.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
153153
configHash.WithLabelValues("config", filepath.Clean(got)).Set(hash)
154154
}
155155
}
156+
opts = configureResourcesAndMetrics(opts, configFile)
156157
}
157158

158159
if opts.AutoGoMemlimit {
@@ -271,9 +272,12 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
271272
if err := storeBuilder.WithAllowAnnotations(opts.AnnotationsAllowList); err != nil {
272273
return fmt.Errorf("failed to set up annotations allowlist: %v", err)
273274
}
275+
klog.InfoS("Using annotations allowlist", "annotationsAllowList", opts.AnnotationsAllowList)
276+
274277
if err := storeBuilder.WithAllowLabels(opts.LabelsAllowList); err != nil {
275278
return fmt.Errorf("failed to set up labels allowlist: %v", err)
276279
}
280+
klog.InfoS("Using labels allowlist", "labelsAllowList", opts.LabelsAllowList)
277281

278282
ksmMetricsRegistry.MustRegister(
279283
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
@@ -385,6 +389,59 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
385389
return nil
386390
}
387391

392+
func configureResourcesAndMetrics(opts *options.Options, configFile []byte) *options.Options {
393+
// If the config file is set, we will overwrite the opts with the config file.
394+
// This is only needed for maps because the default behaviour of yaml.Unmarshal is to append keys (and overwrite any conflicting ones).
395+
config := options.NewOptions()
396+
err := yaml.Unmarshal(configFile, &config)
397+
if err == nil {
398+
if len(config.Resources) > 0 {
399+
opts.Resources = options.ResourceSet{}
400+
for resource := range config.Resources {
401+
opts.Resources[resource] = struct{}{}
402+
}
403+
}
404+
405+
if len(config.MetricAllowlist) > 0 {
406+
opts.MetricAllowlist = options.MetricSet{}
407+
for metric := range config.MetricAllowlist {
408+
opts.MetricAllowlist[metric] = struct{}{}
409+
}
410+
}
411+
412+
if len(config.MetricDenylist) > 0 {
413+
opts.MetricDenylist = options.MetricSet{}
414+
for metric := range config.MetricDenylist {
415+
opts.MetricDenylist[metric] = struct{}{}
416+
}
417+
}
418+
419+
if len(config.MetricOptInList) > 0 {
420+
opts.MetricOptInList = options.MetricSet{}
421+
for metric := range config.MetricOptInList {
422+
opts.MetricOptInList[metric] = struct{}{}
423+
}
424+
}
425+
426+
if len(config.LabelsAllowList) > 0 {
427+
opts.LabelsAllowList = options.LabelsAllowList{}
428+
for label, value := range config.LabelsAllowList {
429+
opts.LabelsAllowList[label] = value
430+
}
431+
}
432+
433+
if len(config.AnnotationsAllowList) > 0 {
434+
opts.AnnotationsAllowList = options.LabelsAllowList{}
435+
for annotation, value := range config.AnnotationsAllowList {
436+
opts.AnnotationsAllowList[annotation] = value
437+
}
438+
}
439+
} else {
440+
klog.ErrorS(err, "failed to unmarshal configFile")
441+
}
442+
return opts
443+
}
444+
388445
func buildTelemetryServer(registry prometheus.Gatherer, authFilter bool, kubeConfig *rest.Config) *http.ServeMux {
389446
mux := http.NewServeMux()
390447

pkg/app/server_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,3 +981,96 @@ func (f *fooFactory) ListWatch(customResourceClient interface{}, ns string, fiel
981981
},
982982
}
983983
}
984+
func TestConfigureResourcesAndMetrics(t *testing.T) {
985+
// Prepare a config file in YAML format
986+
configYAML := `
987+
"resources":
988+
"pod": {}
989+
"service": {}
990+
"metric_allowlist":
991+
"kube_pod_info": {}
992+
"metric_denylist":
993+
"kube_pod_labels": {}
994+
"metric_opt_in_list":
995+
"kube_pod_status_phase": {}
996+
"labels_allow_list":
997+
"labelX":
998+
- foo
999+
- bar
1000+
"annotations_allow_list":
1001+
"annotationY":
1002+
- baz
1003+
`
1004+
opts := options.NewOptions()
1005+
// Set some initial values to be overwritten
1006+
opts.Resources = options.ResourceSet{"oldresource": {}}
1007+
opts.MetricAllowlist = options.MetricSet{"oldallow": {}}
1008+
opts.MetricDenylist = options.MetricSet{"olddeny": {}}
1009+
opts.MetricOptInList = options.MetricSet{"oldoptin": {}}
1010+
opts.LabelsAllowList = options.LabelsAllowList{"oldlabel": {"oldvalue"}}
1011+
opts.AnnotationsAllowList = options.LabelsAllowList{"oldannotation": {"oldvalue"}}
1012+
1013+
newOpts := configureResourcesAndMetrics(opts, []byte(configYAML))
1014+
1015+
// Check resources
1016+
expectedResources := []string{"pod", "service"}
1017+
for _, r := range expectedResources {
1018+
if _, ok := newOpts.Resources[r]; !ok {
1019+
t.Errorf("expected resource %q in opts.Resources", r)
1020+
}
1021+
}
1022+
if _, ok := newOpts.Resources["oldresource"]; ok {
1023+
t.Errorf("expected oldresource to be overwritten")
1024+
}
1025+
1026+
// Check metric allowlist
1027+
if _, ok := newOpts.MetricAllowlist["kube_pod_info"]; !ok {
1028+
t.Errorf("expected kube_pod_info in MetricAllowlist")
1029+
}
1030+
if _, ok := newOpts.MetricAllowlist["oldallow"]; ok {
1031+
t.Errorf("expected oldallow to be overwritten")
1032+
}
1033+
1034+
// Check metric denylist
1035+
if _, ok := newOpts.MetricDenylist["kube_pod_labels"]; !ok {
1036+
t.Errorf("expected kube_pod_labels in MetricDenylist")
1037+
}
1038+
if _, ok := newOpts.MetricDenylist["olddeny"]; ok {
1039+
t.Errorf("expected olddeny to be overwritten")
1040+
}
1041+
1042+
// Check metric opt-in list
1043+
if _, ok := newOpts.MetricOptInList["kube_pod_status_phase"]; !ok {
1044+
t.Errorf("expected kube_pod_status_phase in MetricOptInList")
1045+
}
1046+
if _, ok := newOpts.MetricOptInList["oldoptin"]; ok {
1047+
t.Errorf("expected oldoptin to be overwritten")
1048+
}
1049+
1050+
// Check labels allow list
1051+
if vals, ok := newOpts.LabelsAllowList["labelX"]; !ok || len(vals) != 2 || vals[0] != "foo" || vals[1] != "bar" {
1052+
t.Errorf("expected labelX with values [foo bar], got %v", vals)
1053+
}
1054+
if vals, ok := newOpts.LabelsAllowList["oldlabel"]; ok {
1055+
t.Errorf("expected oldlabel to be overwritten, got %v", vals)
1056+
}
1057+
1058+
// Check annotations allow list
1059+
if vals, ok := newOpts.AnnotationsAllowList["annotationY"]; !ok || len(vals) != 1 || vals[0] != "baz" {
1060+
t.Errorf("expected annotationY with value [baz], got %v", vals)
1061+
}
1062+
if vals, ok := newOpts.AnnotationsAllowList["oldannotation"]; ok {
1063+
t.Errorf("expected oldannotation to be overwritten, got %v", vals)
1064+
}
1065+
1066+
}
1067+
1068+
func TestConfigureResourcesAndMetrics_InvalidYAML(t *testing.T) {
1069+
opts := options.NewOptions()
1070+
invalidYAML := []byte("invalid: [unclosed")
1071+
// Should not panic or overwrite opts
1072+
result := configureResourcesAndMetrics(opts, invalidYAML)
1073+
if result != opts {
1074+
t.Errorf("expected opts to be returned unchanged on invalid YAML")
1075+
}
1076+
}

pkg/options/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func (o *Options) AddFlags(cmd *cobra.Command) {
165165
o.cmd.Flags().StringVar(&o.Pod, "pod", "", "Name of the pod that contains the kube-state-metrics container. "+autoshardingNotice)
166166
o.cmd.Flags().StringVar(&o.TLSConfig, "tls-config", "", "Path to the TLS configuration file")
167167
o.cmd.Flags().StringVar(&o.TelemetryHost, "telemetry-host", "::", `Host to expose kube-state-metrics self metrics on.`)
168-
o.cmd.Flags().StringVar(&o.Config, "config", "", "Path to the kube-state-metrics options config file")
168+
o.cmd.Flags().StringVar(&o.Config, "config", "", "Path to the kube-state-metrics options config YAML file. If this flag is set, the flags defined in the file override the command line flags.")
169169
o.cmd.Flags().BoolVar(&o.ContinueWithoutConfig, "continue-without-config", false, "If true, kube-state-metrics continues to run even if the config file specified by --config is not present. This is useful for scenarios where config file is not provided at startup but is provided later, for e.g., via configmap. Kube-state-metrics will not exit with an error if the config file is not found, instead watches and reloads when it is created.")
170170
o.cmd.Flags().StringVar((*string)(&o.Node), "node", "", "Name of the node that contains the kube-state-metrics pod. Most likely it should be passed via the downward API. This is used for daemonset sharding. Only available for resources (pod metrics) that support spec.nodeName fieldSelector. This is experimental.")
171171
o.cmd.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 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=[*]').")

0 commit comments

Comments
 (0)