Skip to content

Commit 143f94d

Browse files
authored
Merge pull request #1916 from mrueg/config-change-metrics
Add metrics for config file changes
2 parents 4e30f4e + ee89176 commit 143f94d

File tree

4 files changed

+49
-6
lines changed

4 files changed

+49
-6
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ please check the blog post [here](https://www.robustperception.io/exposing-the-s
165165
Sharding metrics expose `--shard` and `--total-shards` flags and can be used to validate
166166
run-time configuration, see [`/examples/prometheus-alerting-rules`](./examples/prometheus-alerting-rules).
167167

168+
kube-state-metrics also exposes metrics about it config file:
169+
170+
```
171+
kube_state_metrics_config_hash{type="config", filename="config.yml"} 4.0061079457904e+13
172+
kube_state_metrics_config_last_reload_success_timestamp_seconds{type="config", filename="config.yml"} 1.6697483049487052e+09
173+
kube_state_metrics_config_last_reload_successful{type="config", filename="config.yml"} 1
174+
```
175+
168176
### Scaling kube-state-metrics
169177

170178
#### Resource recommendation

internal/wrapper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func RunKubeStateMetricsWrapper(opts *options.Options) {
5454
}
5555
}
5656
ctx, cancel := context.WithCancel(context.Background())
57-
if file := options.GetOptsConfigFile(*opts); file != "" {
57+
if file := options.GetConfigFile(*opts); file != "" {
5858
viper.SetConfigType("yaml")
5959
viper.SetConfigFile(file)
6060
if err := viper.ReadInConfig(); err != nil {

pkg/app/server.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package app
1818

1919
import (
2020
"context"
21+
"crypto/md5" //nolint:gosec
22+
"encoding/binary"
2123
"fmt"
2224
"net"
2325
"net/http"
@@ -99,24 +101,46 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options, factories .
99101
ConstLabels: prometheus.Labels{"handler": "metrics"},
100102
}, []string{"method"},
101103
)
104+
configHash := promauto.With(ksmMetricsRegistry).NewGaugeVec(
105+
prometheus.GaugeOpts{
106+
Name: "kube_state_metrics_config_hash",
107+
Help: "Hash of the currently loaded configuration.",
108+
}, []string{"type", "filename"})
109+
configSuccess := promauto.With(ksmMetricsRegistry).NewGaugeVec(
110+
prometheus.GaugeOpts{
111+
Name: "kube_state_metrics_last_config_reload_successful",
112+
Help: "Whether the last configuration reload attempt was successful.",
113+
}, []string{"type", "filename"})
114+
configSuccessTime := promauto.With(ksmMetricsRegistry).NewGaugeVec(
115+
prometheus.GaugeOpts{
116+
Name: "kube_state_metrics_last_config_reload_success_timestamp_seconds",
117+
Help: "Timestamp of the last successful configuration reload.",
118+
}, []string{"type", "filename"})
119+
102120
storeBuilder.WithMetrics(ksmMetricsRegistry)
103121

104-
got := options.GetOptsConfigFile(*opts)
122+
got := options.GetConfigFile(*opts)
105123
if got != "" {
106-
optsConfigFile, err := os.ReadFile(filepath.Clean(got))
124+
configFile, err := os.ReadFile(filepath.Clean(got))
107125
if err != nil {
108126
return fmt.Errorf("failed to read opts config file: %v", err)
109127
}
110128
// NOTE: Config value will override default values of intersecting options.
111-
err = yaml.Unmarshal(optsConfigFile, opts)
129+
err = yaml.Unmarshal(configFile, opts)
112130
if err != nil {
113131
// DO NOT end the process.
114132
// We want to allow the user to still be able to fix the misconfigured config (redeploy or edit the configmaps) and reload KSM automatically once that's done.
115133
klog.Warningf("failed to unmarshal opts config file: %v", err)
116134
// Wait for the next reload.
117135
klog.Infof("misconfigured config detected, KSM will automatically reload on next write to the config")
118136
klog.Infof("waiting for config to be fixed")
137+
configSuccess.WithLabelValues("config", filepath.Clean(got)).Set(0)
119138
<-ctx.Done()
139+
} else {
140+
configSuccess.WithLabelValues("config", filepath.Clean(got)).Set(1)
141+
configSuccessTime.WithLabelValues("config", filepath.Clean(got)).SetToCurrentTime()
142+
hash := md5HashAsMetricValue(configFile)
143+
configHash.WithLabelValues("config", filepath.Clean(got)).Set(hash)
120144
}
121145
}
122146
var resources []string
@@ -371,3 +395,14 @@ func buildMetricsServer(m *metricshandler.MetricsHandler, durationObserver prome
371395
})
372396
return mux
373397
}
398+
399+
// md5HashAsMetricValue creates an md5 hash and returns the most significant bytes that fit into a float64
400+
// Taken from https://github.com/prometheus/alertmanager/blob/6ef6e6868dbeb7984d2d577dd4bf75c65bf1904f/config/coordinator.go#L149
401+
func md5HashAsMetricValue(data []byte) float64 {
402+
sum := md5.Sum(data) //nolint:gosec
403+
// We only want 48 bits as a float64 only has a 53 bit mantissa.
404+
smallSum := sum[0:6]
405+
bytes := make([]byte, 8)
406+
copy(bytes, smallSum)
407+
return float64(binary.LittleEndian.Uint64(bytes))
408+
}

pkg/options/options.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ type Options struct {
6262
cmd *cobra.Command
6363
}
6464

65-
// GetOptsConfigFile is the getter for --options-config-file value.
66-
func GetOptsConfigFile(opt Options) string {
65+
// GetConfigFile is the getter for --config value.
66+
func GetConfigFile(opt Options) string {
6767
return opt.Config
6868
}
6969

0 commit comments

Comments
 (0)