mirror of
https://github.com/coreos/prometheus-operator.git
synced 2026-02-05 15:46:31 +01:00
feat: add prometheus_operator_feature_gate_info metric
This change also moves the feature gates to the operator config struct. It means that after a feature gate is enabled/disabled, the operator will reconcile the managed Prometheus resources which should be the right thing to do. Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
@@ -52,7 +52,9 @@ Usage of ./operator:
|
||||
-enable-config-reloader-probes
|
||||
Enable liveness and readiness for the config-reloader container. Default: false
|
||||
-feature-gates value
|
||||
Feature gates are a set of key=value pairs that describe Prometheus-Operator features. Available features: ["PrometheusAgentDaemonSet"].
|
||||
Feature gates are a set of key=value pairs that describe Prometheus-Operator features.
|
||||
Available feature gates:
|
||||
PrometheusAgentDaemonSet: Enables the DaemonSet mode for PrometheusAgent (enabled: false)
|
||||
-key-file string
|
||||
- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.
|
||||
-kubelet-node-address-priority value
|
||||
|
||||
@@ -120,7 +120,7 @@ var (
|
||||
kubeletSelector operator.LabelSelector
|
||||
nodeAddressPriority operator.NodeAddressPriority
|
||||
|
||||
featureGates *k8sflag.MapStringBool
|
||||
featureGates = k8sflag.NewMapStringBool(ptr.To(map[string]bool{}))
|
||||
)
|
||||
|
||||
func parseFlags(fs *flag.FlagSet) {
|
||||
@@ -173,11 +173,9 @@ func parseFlags(fs *flag.FlagSet) {
|
||||
fs.Var(&cfg.ThanosRulerSelector, "thanos-ruler-instance-selector", "Label selector to filter ThanosRuler Custom Resources to watch.")
|
||||
fs.Var(&cfg.SecretListWatchSelector, "secret-field-selector", "Field selector to filter Secrets to watch")
|
||||
|
||||
// Auto GOMEMLIMIT Ratio
|
||||
fs.Float64Var(&memlimitRatio, "auto-gomemlimit-ratio", defaultMemlimitRatio, "The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory. The value should be greater than 0.0 and less than 1.0. Default: 0.0 (disabled).")
|
||||
|
||||
featureGates = k8sflag.NewMapStringBool(ptr.To(make(map[string]bool)))
|
||||
fs.Var(featureGates, "feature-gates", fmt.Sprintf("Feature gates are a set of key=value pairs that describe Prometheus-Operator features. Available features: %q.", operator.AvailableFeatureGates()))
|
||||
cfg.RegisterFeatureGatesFlags(fs, featureGates)
|
||||
|
||||
logging.RegisterFlags(fs, &logConfig)
|
||||
versionutil.RegisterFlags(fs)
|
||||
@@ -199,17 +197,14 @@ func run(fs *flag.FlagSet) int {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
gates, err := operator.ValidateFeatureGates(featureGates)
|
||||
if err != nil {
|
||||
level.Error(logger).Log(
|
||||
"msg", "error validating feature gates",
|
||||
"error", err)
|
||||
if err := cfg.Gates.UpdateFeatureGates(*featureGates.Map); err != nil {
|
||||
level.Error(logger).Log("error", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
level.Info(logger).Log("msg", "Starting Prometheus Operator", "version", version.Info())
|
||||
level.Info(logger).Log("build_context", version.BuildContext())
|
||||
level.Info(logger).Log("feature_gates", gates)
|
||||
level.Info(logger).Log("feature_gates", cfg.Gates.String())
|
||||
goruntime.SetMaxProcs(logger)
|
||||
goruntime.SetMemLimit(logger, memlimitRatio)
|
||||
|
||||
@@ -509,6 +504,7 @@ func run(fs *flag.FlagSet) int {
|
||||
),
|
||||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||
versioncollector.NewCollector("prometheus_operator"),
|
||||
cfg.Gates,
|
||||
)
|
||||
|
||||
mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -26,6 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
k8sflag "k8s.io/component-base/cli/flag"
|
||||
)
|
||||
|
||||
// Config defines configuration parameters for the Operator.
|
||||
@@ -60,8 +62,11 @@ type Config struct {
|
||||
ThanosRulerSelector LabelSelector
|
||||
SecretListWatchSelector FieldSelector
|
||||
|
||||
// Controller id for pod ownership
|
||||
// Controller id for pod ownership.
|
||||
ControllerID string
|
||||
|
||||
// Feature gates.
|
||||
Gates *FeatureGates
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default operator configuration.
|
||||
@@ -81,9 +86,25 @@ func DefaultConfig(cpu, memory string) Config {
|
||||
AlertmanagerConfigAllowList: StringSet{},
|
||||
ThanosRulerAllowList: StringSet{},
|
||||
},
|
||||
Gates: &FeatureGates{
|
||||
PrometheusAgentDaemonSetFeature: FeatureGate{
|
||||
description: "Enables the DaemonSet mode for PrometheusAgent",
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) RegisterFeatureGatesFlags(fs *flag.FlagSet, flags *k8sflag.MapStringBool) {
|
||||
fs.Var(
|
||||
flags,
|
||||
"feature-gates",
|
||||
fmt.Sprintf("Feature gates are a set of key=value pairs that describe Prometheus-Operator features.\n"+
|
||||
"Available feature gates:\n %s", strings.Join(c.Gates.Descriptions(), "\n "),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// ContainerConfig holds some configuration for the ConfigReloader sidecar
|
||||
// that can be set through prometheus-operator command line arguments.
|
||||
type ContainerConfig struct {
|
||||
|
||||
@@ -19,47 +19,113 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
k8sflag "k8s.io/component-base/cli/flag"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// At the moment, the are no feature gates available.
|
||||
var defaultFeatureGates = map[string]bool{
|
||||
"PrometheusAgentDaemonSet": false,
|
||||
const (
|
||||
// PrometheusAgentDaemonSetFeature enables the DaemonSet mode for PrometheusAgent.
|
||||
PrometheusAgentDaemonSetFeature FeatureGateName = "PrometheusAgentDaemonSet"
|
||||
)
|
||||
|
||||
type FeatureGateName string
|
||||
|
||||
type FeatureGates map[FeatureGateName]FeatureGate
|
||||
|
||||
type FeatureGate struct {
|
||||
description string
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// ValidateFeatureGates merges the feature gate default values with
|
||||
func (fg FeatureGates) Enabled(name FeatureGateName) bool {
|
||||
return fg[name].enabled
|
||||
}
|
||||
|
||||
// UpdateFeatureGates merges the current feature gate values with
|
||||
// the values provided by the user.
|
||||
func ValidateFeatureGates(flags *k8sflag.MapStringBool) (string, error) {
|
||||
gates := defaultFeatureGates
|
||||
if flags.Empty() {
|
||||
return mapToString(gates), nil
|
||||
}
|
||||
|
||||
imgs := *flags.Map
|
||||
for k, v := range imgs {
|
||||
if _, ok := gates[k]; !ok {
|
||||
return "", fmt.Errorf("feature gate %v is unknown", k)
|
||||
func (fg *FeatureGates) UpdateFeatureGates(flags map[string]bool) error {
|
||||
for k := range flags {
|
||||
f, found := (*fg)[FeatureGateName(k)]
|
||||
if !found {
|
||||
return fmt.Errorf("feature gate %q is unknown (supported feature gates: %s)", k, fg.String())
|
||||
}
|
||||
gates[k] = v
|
||||
f.enabled = flags[k]
|
||||
(*fg)[FeatureGateName(k)] = f
|
||||
}
|
||||
return mapToString(gates), nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AvailableFeatureGates() []string {
|
||||
i := 0
|
||||
gates := make([]string, len(defaultFeatureGates))
|
||||
for k := range defaultFeatureGates {
|
||||
gates[i] = k
|
||||
i++
|
||||
func (fg *FeatureGates) keyValuePairs() ([]FeatureGateName, []FeatureGate) {
|
||||
if fg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
slices.Sort(gates)
|
||||
return gates
|
||||
|
||||
var (
|
||||
names = make([]FeatureGateName, 0, len(*fg))
|
||||
gates = make([]FeatureGate, 0, len(*fg))
|
||||
)
|
||||
for k := range *fg {
|
||||
names = append(names, k)
|
||||
}
|
||||
slices.Sort(names)
|
||||
|
||||
for _, v := range names {
|
||||
gates = append(gates, (*fg)[v])
|
||||
}
|
||||
|
||||
return names, gates
|
||||
}
|
||||
|
||||
func mapToString(m map[string]bool) string {
|
||||
var s []string
|
||||
for k, v := range m {
|
||||
s = append(s, fmt.Sprintf("%s=%t", k, v))
|
||||
func (fg *FeatureGates) Descriptions() []string {
|
||||
var (
|
||||
names, gates = fg.keyValuePairs()
|
||||
desc = make([]string, 0, len(names))
|
||||
)
|
||||
|
||||
for i := range names {
|
||||
desc = append(desc, fmt.Sprintf("%s: %s (enabled: %t)", names[i], gates[i].description, gates[i].enabled))
|
||||
}
|
||||
|
||||
return desc
|
||||
}
|
||||
|
||||
func (fg *FeatureGates) String() string {
|
||||
names, gates := fg.keyValuePairs()
|
||||
|
||||
s := make([]string, len(names))
|
||||
for i := range names {
|
||||
s[i] = fmt.Sprintf("%s=%t", names[i], gates[i].enabled)
|
||||
}
|
||||
|
||||
return strings.Join(s, ",")
|
||||
}
|
||||
|
||||
var featureGateInfoDesc = prometheus.NewDesc(
|
||||
"prometheus_operator_feature_gate",
|
||||
"Reports about the Prometheus operator feature gates. A value of 1 means that the feature gate is enabled. Otherwise the value is 0.",
|
||||
[]string{"name"},
|
||||
nil,
|
||||
)
|
||||
|
||||
// Describe implements the prometheus.Collector interface.
|
||||
func (fg *FeatureGates) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- featureGateInfoDesc
|
||||
}
|
||||
|
||||
// Collect implements the prometheus.Collector interface.
|
||||
func (fg *FeatureGates) Collect(ch chan<- prometheus.Metric) {
|
||||
names, gates := fg.keyValuePairs()
|
||||
|
||||
for i, v := range names {
|
||||
var val float64
|
||||
if gates[i].enabled {
|
||||
val = 1.0
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
featureGateInfoDesc,
|
||||
prometheus.GaugeValue,
|
||||
val,
|
||||
string(v),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,53 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
k8sflag "k8s.io/component-base/cli/flag"
|
||||
)
|
||||
|
||||
func TestDefaultFeatureGates(t *testing.T) {
|
||||
m := AvailableFeatureGates()
|
||||
func TestUpdateFeatureGates(t *testing.T) {
|
||||
newFg := func() *FeatureGates {
|
||||
return &FeatureGates{
|
||||
FeatureGateName("Foo"): {
|
||||
description: "foo",
|
||||
enabled: true,
|
||||
},
|
||||
FeatureGateName("Bar"): {
|
||||
description: "bar",
|
||||
enabled: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
require.GreaterOrEqual(t, len(m), 1)
|
||||
}
|
||||
|
||||
func TestMapToString(t *testing.T) {
|
||||
m := mapToString(defaultFeatureGates)
|
||||
|
||||
require.True(t, strings.Contains(m, "PrometheusAgentDaemonSet=false"))
|
||||
}
|
||||
|
||||
func TestValidateFeatureGatesWithNotSupportFeature(t *testing.T) {
|
||||
m, err := ValidateFeatureGates(k8sflag.NewMapStringBool(&map[string]bool{"NotSupportFeature1": true, "NotSupportFeature2": false}))
|
||||
require.Error(t, err)
|
||||
require.Equal(t, "", m)
|
||||
|
||||
m, err = ValidateFeatureGates(k8sflag.NewMapStringBool(&map[string]bool{"PrometheusAgentDaemonSet": true}))
|
||||
require.NoError(t, err)
|
||||
require.True(t, strings.Contains(m, "PrometheusAgentDaemonSet=true"))
|
||||
for _, tc := range []struct {
|
||||
flags map[string]bool
|
||||
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
flags: map[string]bool{
|
||||
"Foo": false,
|
||||
"Bar": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
flags: map[string]bool{"Foox": false, "Bar": true},
|
||||
err: true,
|
||||
},
|
||||
} {
|
||||
t.Run("", func(t *testing.T) {
|
||||
fg := newFg()
|
||||
err := fg.UpdateFeatureGates(tc.flags)
|
||||
if tc.err {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
for k, v := range tc.flags {
|
||||
require.Equal(t, fg.Enabled(FeatureGateName(k)), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user