Path: blob/main/component/prometheus/operator/configgen/config_gen_servicemonitor.go
5403 views
package configgen12import (3"fmt"4"net/url"5"sort"6"strings"78promopv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"9namespacelabeler "github.com/prometheus-operator/prometheus-operator/pkg/namespace-labeler"10commonConfig "github.com/prometheus/common/config"11"github.com/prometheus/common/model"12"github.com/prometheus/prometheus/config"13promk8s "github.com/prometheus/prometheus/discovery/kubernetes"14"github.com/prometheus/prometheus/model/relabel"15metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"16)1718func (cg *ConfigGenerator) GenerateServiceMonitorConfig(m *promopv1.ServiceMonitor, ep promopv1.Endpoint, i int) (cfg *config.ScrapeConfig, err error) {19c := config.DefaultScrapeConfig20cfg = &c2122cfg.ScrapeInterval = config.DefaultGlobalConfig.ScrapeInterval23cfg.ScrapeTimeout = config.DefaultGlobalConfig.ScrapeTimeout24cfg.JobName = fmt.Sprintf("serviceMonitor/%s/%s/%d", m.Namespace, m.Name, i)25cfg.HonorLabels = ep.HonorLabels26if ep.HonorTimestamps != nil {27cfg.HonorTimestamps = *ep.HonorTimestamps28}29dConfig := cg.generateK8SSDConfig(m.Spec.NamespaceSelector, m.Namespace, promk8s.RoleEndpoint, m.Spec.AttachMetadata)30cfg.ServiceDiscoveryConfigs = append(cfg.ServiceDiscoveryConfigs, dConfig)3132if ep.Interval != "" {33if cfg.ScrapeInterval, err = model.ParseDuration(string(ep.Interval)); err != nil {34return nil, fmt.Errorf("parsing interval from serviceMonitor: %w", err)35}36}37if ep.ScrapeTimeout != "" {38if cfg.ScrapeTimeout, err = model.ParseDuration(string(ep.ScrapeTimeout)); err != nil {39return nil, fmt.Errorf("parsing timeout from serviceMonitor: %w", err)40}41}42if ep.Path != "" {43cfg.MetricsPath = ep.Path44}45if ep.ProxyURL != nil {46if u, err := url.Parse(*ep.ProxyURL); err != nil {47return nil, fmt.Errorf("parsing ProxyURL from serviceMonitor: %w", err)48} else {49cfg.HTTPClientConfig.ProxyURL = commonConfig.URL{URL: u}50}51}52if ep.Params != nil {53cfg.Params = ep.Params54}55if ep.Scheme != "" {56cfg.Scheme = ep.Scheme57}58if ep.FollowRedirects != nil {59cfg.HTTPClientConfig.FollowRedirects = *ep.FollowRedirects60}61if ep.EnableHttp2 != nil {62cfg.HTTPClientConfig.EnableHTTP2 = *ep.EnableHttp263}64if ep.TLSConfig != nil {65if cfg.HTTPClientConfig.TLSConfig, err = cg.generateSafeTLS(ep.TLSConfig.SafeTLSConfig); err != nil {66return nil, err67}68}69if ep.BearerTokenSecret.Name != "" {70return nil, fmt.Errorf("bearer tokens in serviceMonitors not supported yet: %w", err)71}72if ep.BasicAuth != nil {73return nil, fmt.Errorf("basic auth in serviceMonitors not supported yet: %w", err)74}75// TODO: Add support for ep.OAuth2 and ep.Authorization7677relabels := cg.initRelabelings()7879// Filter targets by services selected by the monitor.8081// Exact label matches.82var labelKeys []string83for k := range m.Spec.Selector.MatchLabels {84labelKeys = append(labelKeys, k)85}86sort.Strings(labelKeys)87for _, k := range labelKeys {88regex, err := relabel.NewRegexp(fmt.Sprintf("(%s);true", m.Spec.Selector.MatchLabels[k]))89if err != nil {90return nil, fmt.Errorf("parsing MatchLabels regex: %w", err)91}92relabels.add(&relabel.Config{93SourceLabels: model.LabelNames{"__meta_kubernetes_service_label_" + sanitizeLabelName(k), "__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(k)},94Action: "keep",95Regex: regex,96})97}9899// Set based label matching. We have to map the valid relations100// `In`, `NotIn`, `Exists`, and `DoesNotExist`, into relabeling rules.101for _, exp := range m.Spec.Selector.MatchExpressions {102switch exp.Operator {103case metav1.LabelSelectorOpIn:104regex, err := relabel.NewRegexp(fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|")))105if err != nil {106return nil, fmt.Errorf("parsing MatchExpressions regex: %w", err)107}108relabels.add(&relabel.Config{109SourceLabels: model.LabelNames{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)},110Action: "keep",111Regex: regex,112})113case metav1.LabelSelectorOpNotIn:114regex, err := relabel.NewRegexp(fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|")))115if err != nil {116return nil, fmt.Errorf("parsing MatchExpressions regex: %w", err)117}118relabels.add(&relabel.Config{119SourceLabels: model.LabelNames{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)},120Action: "drop",121Regex: regex,122})123case metav1.LabelSelectorOpExists:124relabels.add(&relabel.Config{125SourceLabels: model.LabelNames{"__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)},126Action: "keep",127Regex: regexTrue,128})129case metav1.LabelSelectorOpDoesNotExist:130relabels.add(&relabel.Config{131SourceLabels: model.LabelNames{"__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)},132Action: "drop",133Regex: regexTrue,134})135}136}137138// Filter targets based on correct port for the endpoint.139if ep.Port != "" {140regex, err := relabel.NewRegexp(ep.Port)141if err != nil {142return nil, fmt.Errorf("parsing Port as regex: %w", err)143}144relabels.add(&relabel.Config{145SourceLabels: model.LabelNames{"__meta_kubernetes_endpoint_port_name"},146Action: "keep",147Regex: regex,148})149} else if ep.TargetPort != nil { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.150//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.151regex, err := relabel.NewRegexp(ep.TargetPort.String())152if err != nil {153return nil, fmt.Errorf("parsing TargetPort as regex: %w", err)154}155if ep.TargetPort.StrVal != "" {156relabels.add(&relabel.Config{157SourceLabels: model.LabelNames{"__meta_kubernetes_pod_container_port_name"},158Action: "keep",159Regex: regex,160})161}162} else if ep.TargetPort.IntVal != 0 { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.163regex, err := relabel.NewRegexp(ep.TargetPort.String()) //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.164if err != nil {165return nil, fmt.Errorf("parsing TargetPort as regex: %w", err)166}167relabels.add(&relabel.Config{168SourceLabels: model.LabelNames{"__meta_kubernetes_pod_container_port_number"},169Action: "keep",170Regex: regex,171})172}173174sourceLabels := model.LabelNames{"__meta_kubernetes_endpoint_address_target_kind", "__meta_kubernetes_endpoint_address_target_name"}175// Relabel namespace and pod and service labels into proper labels.176// Relabel node labels with meta labels available with Prometheus >= v2.3.177relabels.add(&relabel.Config{178SourceLabels: sourceLabels,179Separator: ";",180Regex: regexNode,181Replacement: "${1}",182TargetLabel: "node",183})184// Relabel pod labels for >=v2.3 meta labels185relabels.add(&relabel.Config{186SourceLabels: sourceLabels,187Separator: ";",188Regex: regexPod,189Replacement: "${1}",190TargetLabel: "pod",191})192relabels.add(&relabel.Config{193SourceLabels: model.LabelNames{"__meta_kubernetes_namespace"},194TargetLabel: "namespace",195}, &relabel.Config{196SourceLabels: model.LabelNames{"__meta_kubernetes_service_name"},197TargetLabel: "service",198}, &relabel.Config{199SourceLabels: model.LabelNames{"__meta_kubernetes_pod_container_name"},200TargetLabel: "container",201}, &relabel.Config{202SourceLabels: model.LabelNames{"__meta_kubernetes_pod_name"},203TargetLabel: "pod",204})205206if ep.FilterRunning == nil || *ep.FilterRunning {207relabels.add(&relabel.Config{208SourceLabels: model.LabelNames{"__meta_kubernetes_pod_phase"},209Action: "drop",210Regex: regexFilterRunning,211})212}213// Relabel targetLabels from Service onto target.214for _, l := range m.Spec.TargetLabels {215relabels.add(&relabel.Config{216SourceLabels: model.LabelNames{"__meta_kubernetes_service_label_" + sanitizeLabelName(l)},217Replacement: "${1}",218Regex: regexAnything,219TargetLabel: string(sanitizeLabelName(l)),220})221}222for _, l := range m.Spec.PodTargetLabels {223relabels.add(&relabel.Config{224SourceLabels: model.LabelNames{"__meta_kubernetes_pod_label_" + sanitizeLabelName(l)},225Replacement: "${1}",226Regex: regexAnything,227TargetLabel: string(sanitizeLabelName(l)),228})229}230231// By default, generate a safe job name from the service name. We also keep232// this around if a jobLabel is set in case the targets don't actually have a233// value for it.234235relabels.add(&relabel.Config{236SourceLabels: model.LabelNames{"__meta_kubernetes_service_name"},237Replacement: "${1}",238TargetLabel: "job",239})240if m.Spec.JobLabel != "" {241relabels.add(&relabel.Config{242Replacement: "${1}",243TargetLabel: "job",244Regex: regexAnything,245SourceLabels: model.LabelNames{"__meta_kubernetes_service_label_" + sanitizeLabelName(m.Spec.JobLabel)},246})247}248249// A single service may potentially have multiple metrics250// endpoints, therefore the endpoints labels is filled with the ports name or251// as a fallback the port number.252253if ep.Port != "" {254relabels.add(&relabel.Config{255Replacement: ep.Port,256TargetLabel: "endpoint",257})258} else if ep.TargetPort != nil && ep.TargetPort.String() != "" {259relabels.add(&relabel.Config{260TargetLabel: "endpoint",261Replacement: ep.TargetPort.String(),262})263}264265labeler := namespacelabeler.New("", nil, false)266err = relabels.addFromV1(labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, ep.RelabelConfigs)...)267if err != nil {268return nil, err269}270cfg.RelabelConfigs = relabels.configs271272metricRelabels := relabeler{}273err = metricRelabels.addFromV1(labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, ep.MetricRelabelConfigs)...)274if err != nil {275return nil, err276}277cfg.MetricRelabelConfigs = metricRelabels.configs278279cfg.SampleLimit = uint(m.Spec.SampleLimit)280cfg.TargetLimit = uint(m.Spec.TargetLimit)281cfg.LabelLimit = uint(m.Spec.LabelLimit)282cfg.LabelNameLengthLimit = uint(m.Spec.LabelNameLengthLimit)283cfg.LabelValueLengthLimit = uint(m.Spec.LabelValueLengthLimit)284285return cfg, nil286}287288289