From e9ab42bc4c429c44e55aa1988550c0cb375ecc68 Mon Sep 17 00:00:00 2001 From: David Haja Date: Sun, 3 Nov 2024 22:20:24 +0100 Subject: [PATCH 1/4] unmarshal camelcase matchLabels and matchExpressions in target allocator config --- .chloggen/3350-ta-matchlabels.yaml | 16 ++ cmd/otel-allocator/config/config.go | 108 +++++++- cmd/otel-allocator/config/config_test.go | 258 ++++++++++++++++++ ...e_selector_camelcase_expressions_test.yaml | 30 ++ .../pod_service_selector_camelcase_test.yaml | 18 ++ ...pod_service_selector_expressions_test.yaml | 30 ++ go.mod | 5 +- go.sum | 10 +- 8 files changed, 468 insertions(+), 7 deletions(-) create mode 100755 .chloggen/3350-ta-matchlabels.yaml create mode 100644 cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_expressions_test.yaml create mode 100644 cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_test.yaml create mode 100644 cmd/otel-allocator/config/testdata/pod_service_selector_expressions_test.yaml diff --git a/.chloggen/3350-ta-matchlabels.yaml b/.chloggen/3350-ta-matchlabels.yaml new file mode 100755 index 0000000000..321f3d91e7 --- /dev/null +++ b/.chloggen/3350-ta-matchlabels.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) +component: target allocator + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Support camelcase matchLabels and matchExpressions in target allocator config" + +# One or more tracking issues related to the change +issues: [3350] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/cmd/otel-allocator/config/config.go b/cmd/otel-allocator/config/config.go index 8a8b7c188a..43bc301e58 100644 --- a/cmd/otel-allocator/config/config.go +++ b/cmd/otel-allocator/config/config.go @@ -21,14 +21,16 @@ import ( "fmt" "io/fs" "os" + "strings" "time" + "dario.cat/mergo" "github.com/go-logr/logr" + yaml "github.com/goccy/go-yaml" "github.com/prometheus/common/model" promconfig "github.com/prometheus/prometheus/config" _ "github.com/prometheus/prometheus/discovery/install" "github.com/spf13/pflag" - "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -75,6 +77,11 @@ type HTTPSServerConfig struct { TLSKeyFilePath string `yaml:"tls_key_file_path,omitempty"` } +type LabeLSelectorPaths struct { + MatchLabelsPath string + MatchExpressionsPath string +} + func LoadFromFile(file string, target *Config) error { return unmarshal(target, file) } @@ -148,14 +155,113 @@ func LoadFromCLI(target *Config, flagSet *pflag.FlagSet) error { return nil } +// Extract value from specific YAMLPath +func readPath(p string, yamlFile []byte, f interface{}) error { + path, err := yaml.PathString(p) + if err != nil { + return err + } + if err := path.Read(strings.NewReader(string(yamlFile)), f); err != nil { + return err + } + return nil +} + +func readLabelSelectorPaths(ps LabeLSelectorPaths, yamlFile []byte, f *metav1.LabelSelector) (bool, error) { + founderrs := 0 + + if err := readPath(ps.MatchLabelsPath, yamlFile, &f.MatchLabels); err != nil { + if !errors.Is(err, yaml.ErrNotFoundNode) { + return false, err + } + founderrs += 1 + } + if err := readPath(ps.MatchExpressionsPath, yamlFile, &f.MatchExpressions); err != nil { + if !errors.Is(err, yaml.ErrNotFoundNode) { + return false, err + } + founderrs += 1 + } + + return founderrs < 2, nil +} + +func flexibleLabelSelector(yamlFile []byte, fieldPathsMap map[*metav1.LabelSelector]LabeLSelectorPaths) error { + for f, ps := range fieldPathsMap { + tmpls := metav1.LabelSelector{} + + found, err := readLabelSelectorPaths(ps, yamlFile, &tmpls) + if err != nil { + return err + } + + if found { + if err := mergo.Merge(f, tmpls, mergo.WithOverride); err != nil { + return err + } + } + } + return nil +} + +func flexibleCollectorSelector(yamlFile []byte, cfg *Config) error { + collectorSelectorFieldPathsMap := map[*metav1.LabelSelector]LabeLSelectorPaths{ + cfg.CollectorSelector: { + MatchLabelsPath: "$.collector_selector.matchlabels", + MatchExpressionsPath: "$.collector_selector.matchexpressions", + }, + } + + if err := flexibleLabelSelector(yamlFile, collectorSelectorFieldPathsMap); err != nil { + return err + } + return nil +} + +func flexiblePrometheusCR(yamlFile []byte, cfg *Config) error { + prometheusCRFieldPathsMap := map[*metav1.LabelSelector]LabeLSelectorPaths{ + cfg.PrometheusCR.PodMonitorSelector: { + MatchLabelsPath: "$.prometheus_cr.pod_monitor_selector.matchlabels", + MatchExpressionsPath: "$.prometheus_cr.pod_monitor_selector.matchexpressions", + }, + cfg.PrometheusCR.ServiceMonitorSelector: { + MatchLabelsPath: "$.prometheus_cr.service_monitor_selector.matchlabels", + MatchExpressionsPath: "$.prometheus_cr.service_monitor_selector.matchexpressions", + }, + cfg.PrometheusCR.ServiceMonitorNamespaceSelector: { + MatchLabelsPath: "$.prometheus_cr.service_monitor_namespace_selector.matchlabels", + MatchExpressionsPath: "$.prometheus_cr.service_monitor_namespace_selector.matchexpressions", + }, + cfg.PrometheusCR.PodMonitorNamespaceSelector: { + MatchLabelsPath: "$.prometheus_cr.pod_monitor_namespace_selector.matchlabels", + MatchExpressionsPath: "$.prometheus_cr.pod_monitor_namespace_selector.matchexpressions", + }, + } + + if err := flexibleLabelSelector(yamlFile, prometheusCRFieldPathsMap); err != nil { + return err + } + return nil +} + func unmarshal(cfg *Config, configFile string) error { yamlFile, err := os.ReadFile(configFile) if err != nil { return err } + if err = yaml.Unmarshal(yamlFile, cfg); err != nil { return fmt.Errorf("error unmarshaling YAML: %w", err) } + + if err := flexibleCollectorSelector(yamlFile, cfg); err != nil { + return err + } + + if err := flexiblePrometheusCR(yamlFile, cfg); err != nil { + return err + } + return nil } diff --git a/cmd/otel-allocator/config/config_test.go b/cmd/otel-allocator/config/config_test.go index c1b721b773..32fc3ad6e6 100644 --- a/cmd/otel-allocator/config/config_test.go +++ b/cmd/otel-allocator/config/config_test.go @@ -194,6 +194,264 @@ func TestLoad(t *testing.T) { }, wantErr: assert.NoError, }, + { + name: "service monitor pod monitor selector with camelcase", + args: args{ + file: "./testdata/pod_service_selector_camelcase_test.yaml", + }, + want: Config{ + AllocationStrategy: DefaultAllocationStrategy, + CollectorSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + FilterStrategy: DefaultFilterStrategy, + PrometheusCR: PrometheusCRConfig{ + PodMonitorSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "release": "test", + }, + }, + ServiceMonitorSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "release": "test", + }, + }, + ScrapeInterval: DefaultCRScrapeInterval, + }, + PromConfig: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{ + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeProtocols: defaultScrapeProtocols, + ScrapeTimeout: model.Duration(10 * time.Second), + EvaluationInterval: model.Duration(60 * time.Second), + }, + Runtime: promconfig.DefaultRuntimeConfig, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "prometheus", + EnableCompression: true, + HonorTimestamps: true, + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeProtocols: defaultScrapeProtocols, + ScrapeTimeout: model.Duration(10 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + }, + ServiceDiscoveryConfigs: []discovery.Config{ + discovery.StaticConfig{ + { + Targets: []model.LabelSet{ + {model.AddressLabel: "prom.domain:9001"}, + {model.AddressLabel: "prom.domain:9002"}, + {model.AddressLabel: "prom.domain:9003"}, + }, + Labels: model.LabelSet{ + "my": "label", + }, + Source: "0", + }, + }, + }, + }, + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "service monitor pod monitor selector with matchexpressions", + args: args{ + file: "./testdata/pod_service_selector_expressions_test.yaml", + }, + want: Config{ + AllocationStrategy: DefaultAllocationStrategy, + CollectorSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app.kubernetes.io/instance", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "default.test", + }, + }, + { + Key: "app.kubernetes.io/managed-by", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "opentelemetry-operator", + }, + }, + }, + }, + FilterStrategy: DefaultFilterStrategy, + PrometheusCR: PrometheusCRConfig{ + PodMonitorSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "release", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "test", + }, + }, + }, + }, + ServiceMonitorSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "release", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "test", + }, + }, + }, + }, + ScrapeInterval: DefaultCRScrapeInterval, + }, + PromConfig: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{ + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeProtocols: defaultScrapeProtocols, + ScrapeTimeout: model.Duration(10 * time.Second), + EvaluationInterval: model.Duration(60 * time.Second), + }, + Runtime: promconfig.DefaultRuntimeConfig, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "prometheus", + EnableCompression: true, + HonorTimestamps: true, + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeProtocols: defaultScrapeProtocols, + ScrapeTimeout: model.Duration(10 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + }, + ServiceDiscoveryConfigs: []discovery.Config{ + discovery.StaticConfig{ + { + Targets: []model.LabelSet{ + {model.AddressLabel: "prom.domain:9001"}, + {model.AddressLabel: "prom.domain:9002"}, + {model.AddressLabel: "prom.domain:9003"}, + }, + Labels: model.LabelSet{ + "my": "label", + }, + Source: "0", + }, + }, + }, + }, + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "service monitor pod monitor selector with camelcase matchexpressions", + args: args{ + file: "./testdata/pod_service_selector_camelcase_expressions_test.yaml", + }, + want: Config{ + AllocationStrategy: DefaultAllocationStrategy, + CollectorSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app.kubernetes.io/instance", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "default.test", + }, + }, + { + Key: "app.kubernetes.io/managed-by", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "opentelemetry-operator", + }, + }, + }, + }, + FilterStrategy: DefaultFilterStrategy, + PrometheusCR: PrometheusCRConfig{ + PodMonitorSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "release", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "test", + }, + }, + }, + }, + ServiceMonitorSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "release", + Operator: metav1.LabelSelectorOpIn, + Values: []string{ + "test", + }, + }, + }, + }, + ScrapeInterval: DefaultCRScrapeInterval, + }, + PromConfig: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{ + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeProtocols: defaultScrapeProtocols, + ScrapeTimeout: model.Duration(10 * time.Second), + EvaluationInterval: model.Duration(60 * time.Second), + }, + Runtime: promconfig.DefaultRuntimeConfig, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "prometheus", + EnableCompression: true, + HonorTimestamps: true, + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeProtocols: defaultScrapeProtocols, + ScrapeTimeout: model.Duration(10 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + }, + ServiceDiscoveryConfigs: []discovery.Config{ + discovery.StaticConfig{ + { + Targets: []model.LabelSet{ + {model.AddressLabel: "prom.domain:9001"}, + {model.AddressLabel: "prom.domain:9002"}, + {model.AddressLabel: "prom.domain:9003"}, + }, + Labels: model.LabelSet{ + "my": "label", + }, + Source: "0", + }, + }, + }, + }, + }, + }, + }, + wantErr: assert.NoError, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_expressions_test.yaml b/cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_expressions_test.yaml new file mode 100644 index 0000000000..ecf374650d --- /dev/null +++ b/cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_expressions_test.yaml @@ -0,0 +1,30 @@ +collector_selector: + matchExpressions: + - key: "app.kubernetes.io/instance" + operator: "In" + values: + - "default.test" + - key: "app.kubernetes.io/managed-by" + operator: "In" + values: + - "opentelemetry-operator" +prometheus_cr: + pod_monitor_selector: + matchExpressions: + - key: "release" + operator: "In" + values: + - "test" + service_monitor_selector: + matchExpressions: + - key: "release" + operator: "In" + values: + - "test" +config: + scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] + labels: + my: label \ No newline at end of file diff --git a/cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_test.yaml b/cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_test.yaml new file mode 100644 index 0000000000..b503c619ec --- /dev/null +++ b/cmd/otel-allocator/config/testdata/pod_service_selector_camelcase_test.yaml @@ -0,0 +1,18 @@ +collector_selector: + matchLabels: + app.kubernetes.io/instance: default.test + app.kubernetes.io/managed-by: opentelemetry-operator +prometheus_cr: + pod_monitor_selector: + matchLabels: + release: test + service_monitor_selector: + matchLabels: + release: test +config: + scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] + labels: + my: label \ No newline at end of file diff --git a/cmd/otel-allocator/config/testdata/pod_service_selector_expressions_test.yaml b/cmd/otel-allocator/config/testdata/pod_service_selector_expressions_test.yaml new file mode 100644 index 0000000000..0b2fd44b74 --- /dev/null +++ b/cmd/otel-allocator/config/testdata/pod_service_selector_expressions_test.yaml @@ -0,0 +1,30 @@ +collector_selector: + matchexpressions: + - key: "app.kubernetes.io/instance" + operator: "In" + values: + - "default.test" + - key: "app.kubernetes.io/managed-by" + operator: "In" + values: + - "opentelemetry-operator" +prometheus_cr: + pod_monitor_selector: + matchexpressions: + - key: "release" + operator: "In" + values: + - "test" + service_monitor_selector: + matchexpressions: + - key: "release" + operator: "In" + values: + - "test" +config: + scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] + labels: + my: label \ No newline at end of file diff --git a/go.mod b/go.mod index 0b9f77d2a5..acc5c8f1cc 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/go-kit/log v0.2.1 github.com/go-logr/logr v1.4.2 + github.com/goccy/go-yaml v1.13.4 github.com/google/uuid v1.6.0 github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.0 @@ -93,7 +94,7 @@ require ( github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect @@ -115,7 +116,7 @@ require ( github.com/go-openapi/validate v0.24.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/go-resty/resty/v2 v2.13.1 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect github.com/goccy/go-json v0.10.2 // indirect diff --git a/go.sum b/go.sum index 10bcc61145..213c9ce61c 100644 --- a/go.sum +++ b/go.sum @@ -165,8 +165,8 @@ github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -229,8 +229,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g= github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -240,6 +240,8 @@ github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.13.4 h1:XOnLX9GqT+kH/gB7YzCMUiDBFU9B7pm3HZz6kyeDPkk= +github.com/goccy/go-yaml v1.13.4/go.mod h1:IjYwxUiJDoqpx2RmbdjMUceGHZwYLon3sfOGl5Hi9lc= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= From 982d739191a6a3874eb18827d8d8b63762cbff05 Mon Sep 17 00:00:00 2001 From: David Haja Date: Sun, 3 Nov 2024 23:11:29 +0100 Subject: [PATCH 2/4] fix readPath comment --- cmd/otel-allocator/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/otel-allocator/config/config.go b/cmd/otel-allocator/config/config.go index 43bc301e58..d379450b5a 100644 --- a/cmd/otel-allocator/config/config.go +++ b/cmd/otel-allocator/config/config.go @@ -155,7 +155,7 @@ func LoadFromCLI(target *Config, flagSet *pflag.FlagSet) error { return nil } -// Extract value from specific YAMLPath +// readPath extracts value from specific YAMLPath. func readPath(p string, yamlFile []byte, f interface{}) error { path, err := yaml.PathString(p) if err != nil { From b082525ab8795771a8bd181e42f7280207bc831c Mon Sep 17 00:00:00 2001 From: David Haja Date: Tue, 5 Nov 2024 11:40:45 +0100 Subject: [PATCH 3/4] using mapstructure for flexible unmarshalling --- cmd/otel-allocator/config/config.go | 125 +++++++++------------------- go.mod | 5 +- go.sum | 10 +-- 3 files changed, 45 insertions(+), 95 deletions(-) diff --git a/cmd/otel-allocator/config/config.go b/cmd/otel-allocator/config/config.go index d379450b5a..20e6522509 100644 --- a/cmd/otel-allocator/config/config.go +++ b/cmd/otel-allocator/config/config.go @@ -21,16 +21,16 @@ import ( "fmt" "io/fs" "os" - "strings" + "reflect" "time" - "dario.cat/mergo" "github.com/go-logr/logr" - yaml "github.com/goccy/go-yaml" + "github.com/mitchellh/mapstructure" "github.com/prometheus/common/model" promconfig "github.com/prometheus/prometheus/config" _ "github.com/prometheus/prometheus/discovery/install" "github.com/spf13/pflag" + "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -41,7 +41,7 @@ import ( const ( DefaultResyncTime = 5 * time.Minute - DefaultConfigFilePath string = "/conf/targetallocator.yaml" + DefaultConfigFilePath string = "../../conf/targetallocator.yaml" DefaultCRScrapeInterval model.Duration = model.Duration(time.Second * 30) DefaultAllocationStrategy = "consistent-hashing" DefaultFilterStrategy = "relabel-config" @@ -77,11 +77,6 @@ type HTTPSServerConfig struct { TLSKeyFilePath string `yaml:"tls_key_file_path,omitempty"` } -type LabeLSelectorPaths struct { - MatchLabelsPath string - MatchExpressionsPath string -} - func LoadFromFile(file string, target *Config) error { return unmarshal(target, file) } @@ -155,92 +150,55 @@ func LoadFromCLI(target *Config, flagSet *pflag.FlagSet) error { return nil } -// readPath extracts value from specific YAMLPath. -func readPath(p string, yamlFile []byte, f interface{}) error { - path, err := yaml.PathString(p) - if err != nil { - return err - } - if err := path.Read(strings.NewReader(string(yamlFile)), f); err != nil { - return err - } - return nil -} - -func readLabelSelectorPaths(ps LabeLSelectorPaths, yamlFile []byte, f *metav1.LabelSelector) (bool, error) { - founderrs := 0 - - if err := readPath(ps.MatchLabelsPath, yamlFile, &f.MatchLabels); err != nil { - if !errors.Is(err, yaml.ErrNotFoundNode) { - return false, err +func StringToModelDurationHookFunc() mapstructure.DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil } - founderrs += 1 - } - if err := readPath(ps.MatchExpressionsPath, yamlFile, &f.MatchExpressions); err != nil { - if !errors.Is(err, yaml.ErrNotFoundNode) { - return false, err - } - founderrs += 1 - } - - return founderrs < 2, nil -} - -func flexibleLabelSelector(yamlFile []byte, fieldPathsMap map[*metav1.LabelSelector]LabeLSelectorPaths) error { - for f, ps := range fieldPathsMap { - tmpls := metav1.LabelSelector{} - - found, err := readLabelSelectorPaths(ps, yamlFile, &tmpls) - if err != nil { - return err + if t != reflect.TypeOf(model.Duration(5)) { + return data, nil } - if found { - if err := mergo.Merge(f, tmpls, mergo.WithOverride); err != nil { - return err - } - } + // Convert it by parsing + return time.ParseDuration(data.(string)) } - return nil } -func flexibleCollectorSelector(yamlFile []byte, cfg *Config) error { - collectorSelectorFieldPathsMap := map[*metav1.LabelSelector]LabeLSelectorPaths{ - cfg.CollectorSelector: { - MatchLabelsPath: "$.collector_selector.matchlabels", - MatchExpressionsPath: "$.collector_selector.matchexpressions", - }, +func decodeSubConfig(t interface{}, dc mapstructure.DecoderConfig) error { + dec, decError := mapstructure.NewDecoder(&dc) + if decError != nil { + return decError } - - if err := flexibleLabelSelector(yamlFile, collectorSelectorFieldPathsMap); err != nil { + if err := dec.Decode(t); err != nil { return err } return nil } -func flexiblePrometheusCR(yamlFile []byte, cfg *Config) error { - prometheusCRFieldPathsMap := map[*metav1.LabelSelector]LabeLSelectorPaths{ - cfg.PrometheusCR.PodMonitorSelector: { - MatchLabelsPath: "$.prometheus_cr.pod_monitor_selector.matchlabels", - MatchExpressionsPath: "$.prometheus_cr.pod_monitor_selector.matchexpressions", - }, - cfg.PrometheusCR.ServiceMonitorSelector: { - MatchLabelsPath: "$.prometheus_cr.service_monitor_selector.matchlabels", - MatchExpressionsPath: "$.prometheus_cr.service_monitor_selector.matchexpressions", - }, - cfg.PrometheusCR.ServiceMonitorNamespaceSelector: { - MatchLabelsPath: "$.prometheus_cr.service_monitor_namespace_selector.matchlabels", - MatchExpressionsPath: "$.prometheus_cr.service_monitor_namespace_selector.matchexpressions", - }, - cfg.PrometheusCR.PodMonitorNamespaceSelector: { - MatchLabelsPath: "$.prometheus_cr.pod_monitor_namespace_selector.matchlabels", - MatchExpressionsPath: "$.prometheus_cr.pod_monitor_namespace_selector.matchexpressions", - }, +func flexibleUnmarshal(yamlFile []byte, cfg *Config) error { + t := make(map[string]interface{}) + if err := yaml.Unmarshal(yamlFile, &t); err != nil { + return fmt.Errorf("error unmarshaling YAML: %w", err) } - if err := flexibleLabelSelector(yamlFile, prometheusCRFieldPathsMap); err != nil { - return err + if t["collector_selector"] != nil { + dc := mapstructure.DecoderConfig{TagName: "yaml", Result: cfg.CollectorSelector} + if err := decodeSubConfig(t["collector_selector"], dc); err != nil { + return err + } + } + + if t["prometheus_cr"] != nil { + dc := mapstructure.DecoderConfig{TagName: "yaml", Result: &cfg.PrometheusCR, DecodeHook: StringToModelDurationHookFunc()} + if err := decodeSubConfig(t["prometheus_cr"], dc); err != nil { + return err + } } + return nil } @@ -249,16 +207,11 @@ func unmarshal(cfg *Config, configFile string) error { if err != nil { return err } - if err = yaml.Unmarshal(yamlFile, cfg); err != nil { return fmt.Errorf("error unmarshaling YAML: %w", err) } - if err := flexibleCollectorSelector(yamlFile, cfg); err != nil { - return err - } - - if err := flexiblePrometheusCR(yamlFile, cfg); err != nil { + if err := flexibleUnmarshal(yamlFile, cfg); err != nil { return err } diff --git a/go.mod b/go.mod index acc5c8f1cc..0b9f77d2a5 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/go-kit/log v0.2.1 github.com/go-logr/logr v1.4.2 - github.com/goccy/go-yaml v1.13.4 github.com/google/uuid v1.6.0 github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.0 @@ -94,7 +93,7 @@ require ( github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect @@ -116,7 +115,7 @@ require ( github.com/go-openapi/validate v0.24.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.22.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/go-resty/resty/v2 v2.13.1 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect github.com/goccy/go-json v0.10.2 // indirect diff --git a/go.sum b/go.sum index 213c9ce61c..10bcc61145 100644 --- a/go.sum +++ b/go.sum @@ -165,8 +165,8 @@ github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -229,8 +229,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= -github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g= github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -240,8 +240,6 @@ github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.13.4 h1:XOnLX9GqT+kH/gB7YzCMUiDBFU9B7pm3HZz6kyeDPkk= -github.com/goccy/go-yaml v1.13.4/go.mod h1:IjYwxUiJDoqpx2RmbdjMUceGHZwYLon3sfOGl5Hi9lc= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= From dbdb7db6fcb49e4c1310870eaa82b37c36562cdb Mon Sep 17 00:00:00 2001 From: David Haja Date: Tue, 5 Nov 2024 11:42:54 +0100 Subject: [PATCH 4/4] revert deafultconfigfilepath --- cmd/otel-allocator/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/otel-allocator/config/config.go b/cmd/otel-allocator/config/config.go index 20e6522509..a6606d583b 100644 --- a/cmd/otel-allocator/config/config.go +++ b/cmd/otel-allocator/config/config.go @@ -41,7 +41,7 @@ import ( const ( DefaultResyncTime = 5 * time.Minute - DefaultConfigFilePath string = "../../conf/targetallocator.yaml" + DefaultConfigFilePath string = "/conf/targetallocator.yaml" DefaultCRScrapeInterval model.Duration = model.Duration(time.Second * 30) DefaultAllocationStrategy = "consistent-hashing" DefaultFilterStrategy = "relabel-config"