mirror of
https://github.com/prometheus-operator/prometheus-operator.git
synced 2025-04-16 01:06:27 +00:00
* add fallback scrape protocol --------- Signed-off-by: dongjiang1989 <dongjiang1989@126.com>
4698 lines
146 KiB
Go
4698 lines
146 KiB
Go
// Copyright 2016 The prometheus-operator Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package prometheus
|
|
|
|
import (
|
|
"cmp"
|
|
"fmt"
|
|
"log/slog"
|
|
"math"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"reflect"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/alecthomas/units"
|
|
"github.com/blang/semver/v4"
|
|
"github.com/prometheus/common/model"
|
|
"gopkg.in/yaml.v2"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
"k8s.io/utils/ptr"
|
|
|
|
"github.com/prometheus-operator/prometheus-operator/internal/util"
|
|
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
|
|
monitoringv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1"
|
|
"github.com/prometheus-operator/prometheus-operator/pkg/assets"
|
|
namespacelabeler "github.com/prometheus-operator/prometheus-operator/pkg/namespacelabeler"
|
|
"github.com/prometheus-operator/prometheus-operator/pkg/operator"
|
|
)
|
|
|
|
const (
|
|
kubernetesSDRoleEndpoint = "endpoints"
|
|
kubernetesSDRoleEndpointSlice = "endpointslice"
|
|
kubernetesSDRolePod = "pod"
|
|
kubernetesSDRoleIngress = "ingress"
|
|
|
|
defaultPrometheusExternalLabelName = "prometheus"
|
|
defaultReplicaExternalLabelName = "prometheus_replica"
|
|
)
|
|
|
|
var invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)
|
|
|
|
func sanitizeLabelName(name string) string {
|
|
return invalidLabelCharRE.ReplaceAllString(name, "_")
|
|
}
|
|
|
|
// ConfigGenerator knows how to generate a Prometheus configuration which is
|
|
// compatible with a given Prometheus version.
|
|
type ConfigGenerator struct {
|
|
logger *slog.Logger
|
|
version semver.Version
|
|
notCompatible bool
|
|
prom monitoringv1.PrometheusInterface
|
|
useEndpointSlice bool // Whether to use EndpointSlice for service discovery from `ServiceMonitor` objects.
|
|
scrapeClasses map[string]monitoringv1.ScrapeClass
|
|
defaultScrapeClassName string
|
|
daemonSet bool
|
|
}
|
|
|
|
type ConfigGeneratorOption func(*ConfigGenerator)
|
|
|
|
func WithEndpointSliceSupport() ConfigGeneratorOption {
|
|
return func(cg *ConfigGenerator) {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
cg.useEndpointSlice = ptr.Deref(cpf.ServiceDiscoveryRole, monitoringv1.EndpointsRole) == monitoringv1.EndpointSliceRole
|
|
}
|
|
}
|
|
|
|
func WithDaemonSet() ConfigGeneratorOption {
|
|
return func(cg *ConfigGenerator) {
|
|
cg.daemonSet = true
|
|
}
|
|
}
|
|
|
|
// NewConfigGenerator creates a ConfigGenerator for the provided Prometheus resource.
|
|
func NewConfigGenerator(
|
|
logger *slog.Logger,
|
|
p monitoringv1.PrometheusInterface,
|
|
opts ...ConfigGeneratorOption,
|
|
) (*ConfigGenerator, error) {
|
|
if logger == nil {
|
|
logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
|
// slog level math.MaxInt means no logging
|
|
// We would like to use the slog buil-in No-op level once it is available
|
|
// More: https://github.com/golang/go/issues/62005
|
|
Level: slog.Level(math.MaxInt),
|
|
}))
|
|
}
|
|
|
|
cpf := p.GetCommonPrometheusFields()
|
|
|
|
promVersion := operator.StringValOrDefault(cpf.Version, operator.DefaultPrometheusVersion)
|
|
version, err := semver.ParseTolerant(promVersion)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse Prometheus version: %w", err)
|
|
}
|
|
|
|
if version.Major != 2 && version.Major != 3 {
|
|
return nil, fmt.Errorf("unsupported Prometheus version %q", promVersion)
|
|
}
|
|
|
|
logger = logger.With("version", promVersion)
|
|
|
|
scrapeClasses, defaultScrapeClassName, err := getScrapeClassConfig(p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse scrape classes: %w", err)
|
|
}
|
|
|
|
cg := &ConfigGenerator{
|
|
logger: logger,
|
|
version: version,
|
|
prom: p,
|
|
scrapeClasses: scrapeClasses,
|
|
defaultScrapeClassName: defaultScrapeClassName,
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(cg)
|
|
}
|
|
|
|
return cg, nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) endpointRoleFlavor() string {
|
|
role := kubernetesSDRoleEndpoint
|
|
if cg.version.GTE(semver.MustParse("2.21.0")) && cg.useEndpointSlice {
|
|
role = kubernetesSDRoleEndpointSlice
|
|
}
|
|
|
|
return role
|
|
}
|
|
|
|
func getScrapeClassConfig(p monitoringv1.PrometheusInterface) (map[string]monitoringv1.ScrapeClass, string, error) {
|
|
var (
|
|
cpf = p.GetCommonPrometheusFields()
|
|
scrapeClasses = make(map[string]monitoringv1.ScrapeClass, len(cpf.ScrapeClasses))
|
|
defaultScrapeClass string
|
|
)
|
|
|
|
for _, scrapeClass := range cpf.ScrapeClasses {
|
|
lcv, err := NewLabelConfigValidator(p)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
if err := lcv.Validate(scrapeClass.Relabelings); err != nil {
|
|
return nil, "", fmt.Errorf("invalid relabelings for scrapeClass %s: %w", scrapeClass.Name, err)
|
|
}
|
|
|
|
if err := lcv.Validate(scrapeClass.MetricRelabelings); err != nil {
|
|
return nil, "", fmt.Errorf("invalid metric relabelings for scrapeClass %s: %w", scrapeClass.Name, err)
|
|
}
|
|
|
|
if ptr.Deref(scrapeClass.Default, false) {
|
|
if defaultScrapeClass != "" {
|
|
return nil, "", fmt.Errorf("multiple default scrape classes defined")
|
|
}
|
|
|
|
defaultScrapeClass = scrapeClass.Name
|
|
}
|
|
|
|
scrapeClasses[scrapeClass.Name] = scrapeClass
|
|
}
|
|
|
|
return scrapeClasses, defaultScrapeClass, nil
|
|
}
|
|
|
|
// Version returns the currently configured Prometheus version.
|
|
func (cg *ConfigGenerator) Version() semver.Version {
|
|
return cg.version
|
|
}
|
|
|
|
// WithKeyVals returns a new ConfigGenerator with the same characteristics as
|
|
// the current object, expect that the keyvals are appended to the existing
|
|
// logger.
|
|
func (cg *ConfigGenerator) WithKeyVals(keyvals ...interface{}) *ConfigGenerator {
|
|
return &ConfigGenerator{
|
|
logger: cg.logger.With(keyvals...),
|
|
version: cg.version,
|
|
notCompatible: cg.notCompatible,
|
|
prom: cg.prom,
|
|
useEndpointSlice: cg.useEndpointSlice,
|
|
scrapeClasses: cg.scrapeClasses,
|
|
defaultScrapeClassName: cg.defaultScrapeClassName,
|
|
daemonSet: cg.daemonSet,
|
|
}
|
|
}
|
|
|
|
// WithMinimumVersion returns a new ConfigGenerator that does nothing (except
|
|
// logging a warning message) if the Prometheus version is lesser than the
|
|
// given version.
|
|
// The method panics if version isn't a valid SemVer value.
|
|
func (cg *ConfigGenerator) WithMinimumVersion(version string) *ConfigGenerator {
|
|
minVersion := semver.MustParse(version)
|
|
|
|
if cg.version.LT(minVersion) {
|
|
return &ConfigGenerator{
|
|
logger: cg.logger.With("minimum_version", version),
|
|
version: cg.version,
|
|
notCompatible: true,
|
|
prom: cg.prom,
|
|
useEndpointSlice: cg.useEndpointSlice,
|
|
scrapeClasses: cg.scrapeClasses,
|
|
defaultScrapeClassName: cg.defaultScrapeClassName,
|
|
daemonSet: cg.daemonSet,
|
|
}
|
|
}
|
|
|
|
return cg
|
|
}
|
|
|
|
// WithMaximumVersion returns a new ConfigGenerator that does nothing (except
|
|
// logging a warning message) if the Prometheus version is greater than or
|
|
// equal to the given version.
|
|
// The method panics if version isn't a valid SemVer value.
|
|
func (cg *ConfigGenerator) WithMaximumVersion(version string) *ConfigGenerator {
|
|
minVersion := semver.MustParse(version)
|
|
|
|
if cg.version.GTE(minVersion) {
|
|
return &ConfigGenerator{
|
|
logger: cg.logger.With("maximum_version", version),
|
|
version: cg.version,
|
|
notCompatible: true,
|
|
prom: cg.prom,
|
|
useEndpointSlice: cg.useEndpointSlice,
|
|
scrapeClasses: cg.scrapeClasses,
|
|
defaultScrapeClassName: cg.defaultScrapeClassName,
|
|
daemonSet: cg.daemonSet,
|
|
}
|
|
}
|
|
|
|
return cg
|
|
}
|
|
|
|
// AppendMapItem appends the k/v item to the given yaml.MapSlice and returns
|
|
// the updated slice.
|
|
func (cg *ConfigGenerator) AppendMapItem(m yaml.MapSlice, k string, v interface{}) yaml.MapSlice {
|
|
if cg.notCompatible {
|
|
cg.Warn(k)
|
|
return m
|
|
}
|
|
|
|
return append(m, yaml.MapItem{Key: k, Value: v})
|
|
}
|
|
|
|
// AppendCommandlineArgument appends the name/v argument to the given []monitoringv1.Argument and returns
|
|
// the updated slice.
|
|
func (cg *ConfigGenerator) AppendCommandlineArgument(m []monitoringv1.Argument, argument monitoringv1.Argument) []monitoringv1.Argument {
|
|
if cg.notCompatible {
|
|
cg.logger.Warn(fmt.Sprintf("ignoring command line argument %q=%q not supported by Prometheus", argument.Name, argument.Value))
|
|
return m
|
|
}
|
|
|
|
return append(m, argument)
|
|
}
|
|
|
|
// IsCompatible return true or false depending if the version being used is compatible.
|
|
func (cg *ConfigGenerator) IsCompatible() bool {
|
|
return !cg.notCompatible
|
|
}
|
|
|
|
// Warn logs a warning.
|
|
func (cg *ConfigGenerator) Warn(field string) {
|
|
cg.logger.Warn(fmt.Sprintf("ignoring %q not supported by Prometheus", field))
|
|
}
|
|
|
|
type limitKey struct {
|
|
specField string
|
|
prometheusField string
|
|
minVersion string
|
|
}
|
|
|
|
var (
|
|
sampleLimitKey = limitKey{
|
|
specField: "sampleLimit",
|
|
prometheusField: "sample_limit",
|
|
}
|
|
targetLimitKey = limitKey{
|
|
specField: "targetLimit",
|
|
prometheusField: "target_limit",
|
|
minVersion: "2.21.0",
|
|
}
|
|
labelLimitKey = limitKey{
|
|
specField: "labelLimit",
|
|
prometheusField: "label_limit",
|
|
minVersion: "2.27.0",
|
|
}
|
|
labelNameLengthLimitKey = limitKey{
|
|
specField: "labelNameLengthLimit",
|
|
prometheusField: "label_name_length_limit",
|
|
minVersion: "2.27.0",
|
|
}
|
|
labelValueLengthLimitKey = limitKey{
|
|
specField: "labelValueLengthLimit",
|
|
prometheusField: "label_value_length_limit",
|
|
minVersion: "2.27.0",
|
|
}
|
|
keepDroppedTargetsKey = limitKey{
|
|
specField: "keepDroppedTargets",
|
|
prometheusField: "keep_dropped_targets",
|
|
minVersion: "2.47.0",
|
|
}
|
|
)
|
|
|
|
// AddLimitsToYAML appends the given limit key to the configuration if
|
|
// supported by the Prometheus version.
|
|
func (cg *ConfigGenerator) AddLimitsToYAML(cfg yaml.MapSlice, k limitKey, limit *uint64, enforcedLimit *uint64) yaml.MapSlice {
|
|
finalLimit := cg.getLimit(limit, enforcedLimit)
|
|
if finalLimit == nil {
|
|
return cfg
|
|
}
|
|
|
|
if k.minVersion == "" {
|
|
return cg.AppendMapItem(cfg, k.prometheusField, finalLimit)
|
|
}
|
|
|
|
return cg.WithMinimumVersion(k.minVersion).AppendMapItem(cfg, k.prometheusField, finalLimit)
|
|
}
|
|
|
|
// AddHonorTimestamps adds the honor_timestamps field into scrape configurations.
|
|
// honor_timestamps is false only when the user specified it or when the global
|
|
// override applies.
|
|
// For backwards compatibility with Prometheus <2.9.0 we don't set
|
|
// honor_timestamps.
|
|
func (cg *ConfigGenerator) AddHonorTimestamps(cfg yaml.MapSlice, userHonorTimestamps *bool) yaml.MapSlice {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
// Fast path.
|
|
if userHonorTimestamps == nil && !cpf.OverrideHonorTimestamps {
|
|
return cfg
|
|
}
|
|
|
|
honor := false
|
|
if userHonorTimestamps != nil {
|
|
honor = *userHonorTimestamps
|
|
}
|
|
|
|
return cg.WithMinimumVersion("2.9.0").AppendMapItem(cfg, "honor_timestamps", honor && !cpf.OverrideHonorTimestamps)
|
|
}
|
|
|
|
// AddTrackTimestampsStaleness adds the track_timestamps_staleness field into scrape configurations.
|
|
// For backwards compatibility with Prometheus <2.48.0 we don't set
|
|
// track_timestamps_staleness.
|
|
func (cg *ConfigGenerator) AddTrackTimestampsStaleness(cfg yaml.MapSlice, trackTimestampsStaleness *bool) yaml.MapSlice {
|
|
// Fast path.
|
|
if trackTimestampsStaleness == nil {
|
|
return cfg
|
|
}
|
|
|
|
return cg.WithMinimumVersion("2.48.0").AppendMapItem(cfg, "track_timestamps_staleness", *trackTimestampsStaleness)
|
|
}
|
|
|
|
// addScrapeProtocols adds the scrape_protocols field into the configuration.
|
|
func (cg *ConfigGenerator) addScrapeProtocols(cfg yaml.MapSlice, scrapeProtocols []monitoringv1.ScrapeProtocol) yaml.MapSlice {
|
|
if len(scrapeProtocols) == 0 {
|
|
return cfg
|
|
}
|
|
|
|
sps := make([]string, 0, len(scrapeProtocols))
|
|
for _, sp := range scrapeProtocols {
|
|
// PrometheusText1.0.0 requires Prometheus v3.0.0 at least.
|
|
if sp == monitoringv1.PrometheusText1_0_0 && !cg.WithMinimumVersion("3.0.0-rc.0").IsCompatible() {
|
|
cg.Warn(fmt.Sprintf("scrapeProtocol=%s", monitoringv1.PrometheusText1_0_0))
|
|
continue
|
|
}
|
|
|
|
sps = append(sps, string(sp))
|
|
}
|
|
|
|
return cg.WithMinimumVersion("2.49.0").AppendMapItem(cfg, "scrape_protocols", sps)
|
|
}
|
|
|
|
// addScrapeFallbackProtocol adds the fallback_scrape_protocol field into the configuration.
|
|
func (cg *ConfigGenerator) addScrapeFallbackProtocol(cfg yaml.MapSlice, scrapeFallbackProtocol *monitoringv1.ScrapeProtocol) yaml.MapSlice {
|
|
if scrapeFallbackProtocol == nil {
|
|
return cfg
|
|
}
|
|
|
|
return cg.WithMinimumVersion("3.0.0-rc.0").AppendMapItem(cfg, "fallback_scrape_protocol", scrapeFallbackProtocol)
|
|
}
|
|
|
|
// AddHonorLabels adds the honor_labels field into scrape configurations.
|
|
// if OverrideHonorLabels is true then honor_labels is always false.
|
|
func (cg *ConfigGenerator) AddHonorLabels(cfg yaml.MapSlice, honorLabels bool) yaml.MapSlice {
|
|
if cg.prom.GetCommonPrometheusFields().OverrideHonorLabels {
|
|
honorLabels = false
|
|
}
|
|
|
|
return cg.AppendMapItem(cfg, "honor_labels", honorLabels)
|
|
}
|
|
|
|
// addNativeHistogramConfig adds the native histogram field into scrape configurations.
|
|
func (cg *ConfigGenerator) addNativeHistogramConfig(cfg yaml.MapSlice, nhc monitoringv1.NativeHistogramConfig) yaml.MapSlice {
|
|
if reflect.ValueOf(nhc).IsZero() {
|
|
return cfg
|
|
}
|
|
|
|
if nhc.NativeHistogramBucketLimit != nil {
|
|
cfg = cg.WithMinimumVersion("2.45.0").AppendMapItem(cfg, "native_histogram_bucket_limit", nhc.NativeHistogramBucketLimit)
|
|
}
|
|
|
|
if nhc.NativeHistogramMinBucketFactor != nil {
|
|
cfg = cg.WithMinimumVersion("2.50.0").AppendMapItem(cfg, "native_histogram_min_bucket_factor", nhc.NativeHistogramMinBucketFactor.AsApproximateFloat64())
|
|
}
|
|
|
|
if nhc.ScrapeClassicHistograms != nil {
|
|
switch cg.version.Major {
|
|
case 3:
|
|
cfg = cg.AppendMapItem(cfg, "always_scrape_classic_histograms", nhc.ScrapeClassicHistograms)
|
|
default:
|
|
cfg = cg.WithMinimumVersion("2.45.0").AppendMapItem(cfg, "scrape_classic_histograms", nhc.ScrapeClassicHistograms)
|
|
}
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
// stringMapToMapSlice returns a yaml.MapSlice from a string map to ensure that
|
|
// the output is deterministic.
|
|
func stringMapToMapSlice[V any](m map[string]V) yaml.MapSlice {
|
|
res := yaml.MapSlice{}
|
|
|
|
for _, k := range util.SortedKeys(m) {
|
|
res = append(res, yaml.MapItem{Key: k, Value: m[k]})
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func mergeSafeTLSConfigWithScrapeClass(tlsConfig *monitoringv1.SafeTLSConfig, scrapeClass monitoringv1.ScrapeClass) *monitoringv1.TLSConfig {
|
|
if tlsConfig == nil || reflect.ValueOf(*tlsConfig).IsZero() {
|
|
return mergeTLSConfigWithScrapeClass(nil, scrapeClass)
|
|
}
|
|
|
|
return mergeTLSConfigWithScrapeClass(&monitoringv1.TLSConfig{SafeTLSConfig: *tlsConfig}, scrapeClass)
|
|
}
|
|
|
|
func mergeTLSConfigWithScrapeClass(tlsConfig *monitoringv1.TLSConfig, scrapeClass monitoringv1.ScrapeClass) *monitoringv1.TLSConfig {
|
|
if tlsConfig == nil {
|
|
return scrapeClass.TLSConfig
|
|
}
|
|
|
|
if scrapeClass.TLSConfig == nil {
|
|
return tlsConfig
|
|
}
|
|
|
|
if tlsConfig.CAFile == "" && tlsConfig.SafeTLSConfig.CA == (monitoringv1.SecretOrConfigMap{}) {
|
|
tlsConfig.CAFile = scrapeClass.TLSConfig.CAFile
|
|
}
|
|
|
|
if tlsConfig.CertFile == "" && tlsConfig.SafeTLSConfig.Cert == (monitoringv1.SecretOrConfigMap{}) {
|
|
tlsConfig.CertFile = scrapeClass.TLSConfig.CertFile
|
|
}
|
|
|
|
if tlsConfig.KeyFile == "" && tlsConfig.SafeTLSConfig.KeySecret == nil {
|
|
tlsConfig.KeyFile = scrapeClass.TLSConfig.KeyFile
|
|
}
|
|
|
|
return tlsConfig
|
|
}
|
|
|
|
func mergeAttachMetadataWithScrapeClass(attachMetadata *monitoringv1.AttachMetadata, scrapeClass monitoringv1.ScrapeClass, minimumVersion string) *attachMetadataConfig {
|
|
if attachMetadata == nil {
|
|
attachMetadata = scrapeClass.AttachMetadata
|
|
}
|
|
|
|
if attachMetadata == nil {
|
|
return nil
|
|
}
|
|
|
|
return &attachMetadataConfig{
|
|
MinimumVersion: minimumVersion,
|
|
attachMetadata: attachMetadata,
|
|
}
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addBasicAuthToYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
basicAuth *monitoringv1.BasicAuth,
|
|
) yaml.MapSlice {
|
|
if basicAuth == nil {
|
|
return cfg
|
|
}
|
|
|
|
username, err := store.GetSecretKey(basicAuth.Username)
|
|
if err != nil {
|
|
cg.logger.Error("invalid username reference", "err", err)
|
|
}
|
|
|
|
password, err := store.GetSecretKey(basicAuth.Password)
|
|
if err != nil {
|
|
cg.logger.Error("invalid password reference", "err", err)
|
|
}
|
|
|
|
auth := yaml.MapSlice{
|
|
yaml.MapItem{Key: "username", Value: string(username)},
|
|
yaml.MapItem{Key: "password", Value: string(password)},
|
|
}
|
|
|
|
return cg.AppendMapItem(cfg, "basic_auth", auth)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addSigv4ToYaml(cfg yaml.MapSlice,
|
|
assetStoreKey string,
|
|
store assets.StoreGetter,
|
|
sigv4 *monitoringv1.Sigv4,
|
|
) yaml.MapSlice {
|
|
if sigv4 == nil {
|
|
return cfg
|
|
}
|
|
|
|
sigv4Cfg := yaml.MapSlice{}
|
|
if sigv4.Region != "" {
|
|
sigv4Cfg = append(sigv4Cfg, yaml.MapItem{Key: "region", Value: sigv4.Region})
|
|
}
|
|
|
|
if sigv4.AccessKey != nil && sigv4.SecretKey != nil {
|
|
var ak, sk []byte
|
|
|
|
ak, err := store.GetSecretKey(*sigv4.AccessKey)
|
|
if err != nil {
|
|
cg.logger.Error("invalid SigV4 access key reference", "err", err)
|
|
}
|
|
|
|
sk, err = store.GetSecretKey(*sigv4.SecretKey)
|
|
if err != nil {
|
|
cg.logger.Error("invalid SigV4 secret key reference", "err", err)
|
|
}
|
|
|
|
if len(ak) > 0 && len(sk) > 0 {
|
|
sigv4Cfg = append(sigv4Cfg,
|
|
yaml.MapItem{Key: "access_key", Value: string(ak)},
|
|
yaml.MapItem{Key: "secret_key", Value: string(sk)},
|
|
)
|
|
}
|
|
}
|
|
|
|
if sigv4.Profile != "" {
|
|
sigv4Cfg = append(sigv4Cfg, yaml.MapItem{Key: "profile", Value: sigv4.Profile})
|
|
}
|
|
|
|
if sigv4.RoleArn != "" {
|
|
sigv4Cfg = append(sigv4Cfg, yaml.MapItem{Key: "role_arn", Value: sigv4.RoleArn})
|
|
}
|
|
|
|
return cg.WithKeyVals("component", strings.Split(assetStoreKey, "/")[0]).AppendMapItem(cfg, "sigv4", sigv4Cfg)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addSafeAuthorizationToYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
auth *monitoringv1.SafeAuthorization,
|
|
) yaml.MapSlice {
|
|
if auth == nil {
|
|
return cfg
|
|
}
|
|
|
|
authCfg := yaml.MapSlice{}
|
|
if auth.Type == "" {
|
|
auth.Type = "Bearer"
|
|
}
|
|
|
|
authCfg = append(authCfg, yaml.MapItem{Key: "type", Value: strings.TrimSpace(auth.Type)})
|
|
if auth.Credentials != nil {
|
|
b, err := store.GetSecretKey(*auth.Credentials)
|
|
if err != nil {
|
|
cg.logger.Error("invalid credentials reference", "err", err)
|
|
} else {
|
|
authCfg = append(authCfg, yaml.MapItem{Key: "credentials", Value: string(b)})
|
|
}
|
|
}
|
|
|
|
return cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "authorization", authCfg)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addAuthorizationToYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
auth *monitoringv1.Authorization,
|
|
) yaml.MapSlice {
|
|
if auth == nil {
|
|
return cfg
|
|
}
|
|
|
|
// reuse addSafeAuthorizationToYaml and unpack the part we're interested
|
|
// in, namely the value under the "authorization" key
|
|
authCfg := cg.addSafeAuthorizationToYaml(yaml.MapSlice{}, store, &auth.SafeAuthorization)[0].Value.(yaml.MapSlice)
|
|
|
|
if auth.CredentialsFile != "" {
|
|
authCfg = append(authCfg, yaml.MapItem{Key: "credentials_file", Value: auth.CredentialsFile})
|
|
}
|
|
|
|
return cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "authorization", authCfg)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) buildExternalLabels() yaml.MapSlice {
|
|
m := map[string]string{}
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
objMeta := cg.prom.GetObjectMeta()
|
|
|
|
prometheusExternalLabelName := defaultPrometheusExternalLabelName
|
|
if cpf.PrometheusExternalLabelName != nil {
|
|
prometheusExternalLabelName = *cpf.PrometheusExternalLabelName
|
|
}
|
|
|
|
// Do not add the external label if the resulting value is empty.
|
|
if prometheusExternalLabelName != "" {
|
|
m[prometheusExternalLabelName] = fmt.Sprintf("%s/%s", objMeta.GetNamespace(), objMeta.GetName())
|
|
}
|
|
|
|
replicaExternalLabelName := defaultReplicaExternalLabelName
|
|
if cpf.ReplicaExternalLabelName != nil {
|
|
replicaExternalLabelName = *cpf.ReplicaExternalLabelName
|
|
}
|
|
|
|
// Do not add the external label if the resulting value is empty.
|
|
if replicaExternalLabelName != "" {
|
|
m[replicaExternalLabelName] = fmt.Sprintf("$(%s)", operator.PodNameEnvVar)
|
|
}
|
|
|
|
for k, v := range cpf.ExternalLabels {
|
|
if _, found := m[k]; found {
|
|
cg.logger.Warn("ignoring external label because it is a reserved key", "key", k)
|
|
continue
|
|
}
|
|
m[k] = v
|
|
}
|
|
|
|
return stringMapToMapSlice(m)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addProxyConfigtoYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
proxyConfig monitoringv1.ProxyConfig,
|
|
) yaml.MapSlice {
|
|
if reflect.ValueOf(proxyConfig).IsZero() {
|
|
return cfg
|
|
}
|
|
|
|
if proxyConfig.ProxyURL != nil {
|
|
cfg = cg.AppendMapItem(cfg, "proxy_url", *proxyConfig.ProxyURL)
|
|
}
|
|
|
|
cgProxyConfig := cg.WithMinimumVersion("2.43.0")
|
|
|
|
if proxyConfig.NoProxy != nil {
|
|
cfg = cgProxyConfig.AppendMapItem(cfg, "no_proxy", *proxyConfig.NoProxy)
|
|
}
|
|
|
|
if proxyConfig.ProxyFromEnvironment != nil {
|
|
cfg = cgProxyConfig.AppendMapItem(cfg, "proxy_from_environment", *proxyConfig.ProxyFromEnvironment)
|
|
}
|
|
|
|
if proxyConfig.ProxyConnectHeader != nil {
|
|
proxyConnectHeader := make(map[string][]string, len(proxyConfig.ProxyConnectHeader))
|
|
|
|
for k, v := range proxyConfig.ProxyConnectHeader {
|
|
proxyConnectHeader[k] = []string{}
|
|
for _, s := range v {
|
|
value, _ := store.GetSecretKey(s)
|
|
proxyConnectHeader[k] = append(proxyConnectHeader[k], string(value))
|
|
}
|
|
}
|
|
|
|
cfg = cgProxyConfig.AppendMapItem(cfg, "proxy_connect_header", stringMapToMapSlice(proxyConnectHeader))
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addSafeTLStoYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
safetls *monitoringv1.SafeTLSConfig,
|
|
) yaml.MapSlice {
|
|
|
|
if safetls == nil {
|
|
return cfg
|
|
}
|
|
|
|
safetlsConfig := yaml.MapSlice{}
|
|
|
|
if safetls.InsecureSkipVerify != nil {
|
|
safetlsConfig = append(safetlsConfig, yaml.MapItem{Key: "insecure_skip_verify", Value: *safetls.InsecureSkipVerify})
|
|
}
|
|
|
|
if safetls.CA.Secret != nil || safetls.CA.ConfigMap != nil {
|
|
safetlsConfig = append(safetlsConfig, yaml.MapItem{Key: "ca_file", Value: path.Join(tlsAssetsDir, store.TLSAsset(safetls.CA))})
|
|
}
|
|
|
|
if safetls.Cert.Secret != nil || safetls.Cert.ConfigMap != nil {
|
|
safetlsConfig = append(safetlsConfig, yaml.MapItem{Key: "cert_file", Value: path.Join(tlsAssetsDir, store.TLSAsset(safetls.Cert))})
|
|
}
|
|
|
|
if safetls.KeySecret != nil {
|
|
safetlsConfig = append(safetlsConfig, yaml.MapItem{Key: "key_file", Value: path.Join(tlsAssetsDir, store.TLSAsset(safetls.KeySecret))})
|
|
}
|
|
|
|
if ptr.Deref(safetls.ServerName, "") != "" {
|
|
safetlsConfig = append(safetlsConfig, yaml.MapItem{Key: "server_name", Value: *safetls.ServerName})
|
|
}
|
|
|
|
if safetls.MinVersion != nil {
|
|
safetlsConfig = cg.WithMinimumVersion("2.35.0").AppendMapItem(safetlsConfig, "min_version", *safetls.MinVersion)
|
|
}
|
|
|
|
if safetls.MaxVersion != nil {
|
|
safetlsConfig = cg.WithMinimumVersion("2.41.0").AppendMapItem(safetlsConfig, "max_version", *safetls.MaxVersion)
|
|
}
|
|
|
|
return cg.AppendMapItem(cfg, "tls_config", safetlsConfig)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addTLStoYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
tls *monitoringv1.TLSConfig,
|
|
) yaml.MapSlice {
|
|
if tls == nil {
|
|
return cfg
|
|
}
|
|
|
|
tlsConfig := cg.addSafeTLStoYaml(yaml.MapSlice{}, store, &tls.SafeTLSConfig)[0].Value.(yaml.MapSlice)
|
|
|
|
if tls.CAFile != "" {
|
|
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: tls.CAFile})
|
|
}
|
|
|
|
if tls.CertFile != "" {
|
|
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: tls.CertFile})
|
|
}
|
|
|
|
if tls.KeyFile != "" {
|
|
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: tls.KeyFile})
|
|
}
|
|
|
|
return cg.AppendMapItem(cfg, "tls_config", tlsConfig)
|
|
}
|
|
|
|
// CompareScrapeTimeoutToScrapeInterval validates value of scrapeTimeout based on scrapeInterval.
|
|
func CompareScrapeTimeoutToScrapeInterval(scrapeTimeout, scrapeInterval monitoringv1.Duration) error {
|
|
var si, st model.Duration
|
|
var err error
|
|
|
|
if si, err = model.ParseDuration(string(scrapeInterval)); err != nil {
|
|
return fmt.Errorf("invalid scrapeInterval %q: %w", scrapeInterval, err)
|
|
}
|
|
|
|
if st, err = model.ParseDuration(string(scrapeTimeout)); err != nil {
|
|
return fmt.Errorf("invalid scrapeTimeout: %q: %w", scrapeTimeout, err)
|
|
}
|
|
|
|
if st > si {
|
|
return fmt.Errorf("scrapeTimeout %q greater than scrapeInterval %q", scrapeTimeout, scrapeInterval)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GenerateServerConfiguration creates a serialized YAML representation of a Prometheus Server configuration using the provided resources.
|
|
func (cg *ConfigGenerator) GenerateServerConfiguration(
|
|
p *monitoringv1.Prometheus,
|
|
sMons map[string]*monitoringv1.ServiceMonitor,
|
|
pMons map[string]*monitoringv1.PodMonitor,
|
|
probes map[string]*monitoringv1.Probe,
|
|
sCons map[string]*monitoringv1alpha1.ScrapeConfig,
|
|
store *assets.StoreBuilder,
|
|
additionalScrapeConfigs []byte,
|
|
additionalAlertRelabelConfigs []byte,
|
|
additionalAlertManagerConfigs []byte,
|
|
ruleConfigMapNames []string,
|
|
) ([]byte, error) {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
|
|
// validates the value of scrapeTimeout based on scrapeInterval
|
|
if cpf.ScrapeTimeout != "" {
|
|
if err := CompareScrapeTimeoutToScrapeInterval(cpf.ScrapeTimeout, cpf.ScrapeInterval); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
cfg := yaml.MapSlice{}
|
|
|
|
// Global config
|
|
globalCfg := cg.buildGlobalConfig()
|
|
globalCfg = cg.appendEvaluationInterval(globalCfg, p.Spec.EvaluationInterval)
|
|
globalCfg = cg.appendRuleQueryOffset(globalCfg, p.Spec.RuleQueryOffset)
|
|
globalCfg = cg.appendQueryLogFile(globalCfg, p.Spec.QueryLogFile)
|
|
cfg = append(cfg, yaml.MapItem{Key: "global", Value: globalCfg})
|
|
|
|
// Runtime config
|
|
cfg = cg.appendRuntime(cfg)
|
|
|
|
// Rule Files config
|
|
cfg = cg.appendRuleFiles(cfg, ruleConfigMapNames, p.Spec.RuleSelector)
|
|
|
|
// Scrape config
|
|
var (
|
|
scrapeConfigs []yaml.MapSlice
|
|
apiserverConfig = cpf.APIServerConfig
|
|
shards = shardsNumber(cg.prom)
|
|
)
|
|
|
|
scrapeConfigs = cg.appendServiceMonitorConfigs(scrapeConfigs, sMons, apiserverConfig, store, shards)
|
|
scrapeConfigs = cg.appendPodMonitorConfigs(scrapeConfigs, pMons, apiserverConfig, store, shards)
|
|
scrapeConfigs = cg.appendProbeConfigs(scrapeConfigs, probes, apiserverConfig, store, shards)
|
|
scrapeConfigs, err := cg.appendScrapeConfigs(scrapeConfigs, sCons, store, shards)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate scrape configs: %w", err)
|
|
}
|
|
|
|
scrapeConfigs, err = cg.appendAdditionalScrapeConfigs(scrapeConfigs, additionalScrapeConfigs, shards)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate additional scrape configs: %w", err)
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "scrape_configs",
|
|
Value: scrapeConfigs,
|
|
})
|
|
|
|
// Storage config
|
|
cfg, err = cg.appendStorageSettingsConfig(cfg, p.Spec.Exemplars)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generating storage_settings configuration failed: %w", err)
|
|
}
|
|
|
|
s := store.ForNamespace(cg.prom.GetObjectMeta().GetNamespace())
|
|
|
|
// Alerting config
|
|
cfg, err = cg.appendAlertingConfig(cfg, p.Spec.Alerting, additionalAlertRelabelConfigs, additionalAlertManagerConfigs, s)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generating alerting configuration failed: %w", err)
|
|
}
|
|
|
|
// Remote write config
|
|
if len(cpf.RemoteWrite) > 0 {
|
|
cfg = append(cfg, cg.generateRemoteWriteConfig(s))
|
|
}
|
|
|
|
// Remote read config
|
|
if len(p.Spec.RemoteRead) > 0 {
|
|
cfg = append(cfg, cg.generateRemoteReadConfig(p.Spec.RemoteRead, s))
|
|
}
|
|
|
|
// OTLP config
|
|
cfg, err = cg.appendOTLPConfig(cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate OTLP configuration: %w", err)
|
|
}
|
|
|
|
cfg, err = cg.appendTracingConfig(cfg, s)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate tracing configuration: %w", err)
|
|
}
|
|
|
|
return yaml.Marshal(cfg)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendStorageSettingsConfig(cfg yaml.MapSlice, exemplars *monitoringv1.Exemplars) (yaml.MapSlice, error) {
|
|
var (
|
|
storage yaml.MapSlice
|
|
cgStorage = cg.WithMinimumVersion("2.29.0")
|
|
tsdb = cg.prom.GetCommonPrometheusFields().TSDB
|
|
)
|
|
|
|
if exemplars != nil && exemplars.MaxSize != nil {
|
|
storage = cgStorage.AppendMapItem(storage, "exemplars", yaml.MapSlice{
|
|
{
|
|
Key: "max_exemplars",
|
|
Value: *exemplars.MaxSize,
|
|
},
|
|
})
|
|
}
|
|
|
|
if tsdb != nil && tsdb.OutOfOrderTimeWindow != nil {
|
|
storage = cg.WithMinimumVersion("2.39.0").AppendMapItem(storage, "tsdb", yaml.MapSlice{
|
|
{
|
|
Key: "out_of_order_time_window",
|
|
Value: *tsdb.OutOfOrderTimeWindow,
|
|
},
|
|
})
|
|
}
|
|
|
|
if len(storage) == 0 {
|
|
return cfg, nil
|
|
}
|
|
|
|
return cgStorage.AppendMapItem(cfg, "storage", storage), nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendAlertingConfig(
|
|
cfg yaml.MapSlice,
|
|
alerting *monitoringv1.AlertingSpec,
|
|
additionalAlertRelabelConfigs []byte,
|
|
additionalAlertmanagerConfigs []byte,
|
|
store assets.StoreGetter,
|
|
) (yaml.MapSlice, error) {
|
|
if alerting == nil && additionalAlertRelabelConfigs == nil && additionalAlertmanagerConfigs == nil {
|
|
return cfg, nil
|
|
}
|
|
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
|
|
alertmanagerConfigs := cg.generateAlertmanagerConfig(alerting, cpf.APIServerConfig, store)
|
|
|
|
var additionalAlertmanagerConfigsYaml []yaml.MapSlice
|
|
if err := yaml.Unmarshal(additionalAlertmanagerConfigs, &additionalAlertmanagerConfigsYaml); err != nil {
|
|
return nil, fmt.Errorf("unmarshalling additional alertmanager configs failed")
|
|
}
|
|
alertmanagerConfigs = append(alertmanagerConfigs, additionalAlertmanagerConfigsYaml...)
|
|
|
|
var alertRelabelConfigs []yaml.MapSlice
|
|
|
|
replicaExternalLabelName := defaultReplicaExternalLabelName
|
|
if cpf.ReplicaExternalLabelName != nil {
|
|
replicaExternalLabelName = *cpf.ReplicaExternalLabelName
|
|
}
|
|
|
|
if replicaExternalLabelName != "" {
|
|
// Drop the replica label to enable proper deduplication on the Alertmanager side.
|
|
alertRelabelConfigs = append(alertRelabelConfigs, yaml.MapSlice{
|
|
{Key: "action", Value: "labeldrop"},
|
|
{Key: "regex", Value: regexp.QuoteMeta(replicaExternalLabelName)},
|
|
})
|
|
}
|
|
|
|
var additionalAlertRelabelConfigsYaml []yaml.MapSlice
|
|
if err := yaml.Unmarshal(additionalAlertRelabelConfigs, &additionalAlertRelabelConfigsYaml); err != nil {
|
|
return nil, fmt.Errorf("unmarshalling additional alerting relabel configs failed: %w", err)
|
|
}
|
|
alertRelabelConfigs = append(alertRelabelConfigs, additionalAlertRelabelConfigsYaml...)
|
|
|
|
return append(cfg, yaml.MapItem{
|
|
Key: "alerting",
|
|
Value: yaml.MapSlice{
|
|
{
|
|
Key: "alert_relabel_configs",
|
|
Value: alertRelabelConfigs,
|
|
},
|
|
{
|
|
Key: "alertmanagers",
|
|
Value: alertmanagerConfigs,
|
|
},
|
|
},
|
|
}), nil
|
|
}
|
|
|
|
func initRelabelings() []yaml.MapSlice {
|
|
// Relabel prometheus job name into a meta label
|
|
return []yaml.MapSlice{
|
|
{
|
|
{Key: "source_labels", Value: []string{"job"}},
|
|
{Key: "target_label", Value: "__tmp_prometheus_job_name"},
|
|
},
|
|
}
|
|
}
|
|
|
|
// BuildCommonPrometheusArgs builds a slice of arguments that are common between Prometheus Server and Agent.
|
|
func (cg *ConfigGenerator) BuildCommonPrometheusArgs() []monitoringv1.Argument {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
promArgs := []monitoringv1.Argument{
|
|
{Name: "web.console.templates", Value: "/etc/prometheus/consoles"},
|
|
{Name: "web.console.libraries", Value: "/etc/prometheus/console_libraries"},
|
|
{Name: "config.file", Value: path.Join(ConfOutDir, ConfigEnvsubstFilename)},
|
|
}
|
|
|
|
if ptr.Deref(cpf.ReloadStrategy, monitoringv1.HTTPReloadStrategyType) == monitoringv1.HTTPReloadStrategyType {
|
|
promArgs = append(promArgs, monitoringv1.Argument{Name: "web.enable-lifecycle"})
|
|
}
|
|
|
|
if cpf.Web != nil {
|
|
if cpf.Web.PageTitle != nil {
|
|
promArgs = cg.WithMinimumVersion("2.6.0").AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "web.page-title", Value: *cpf.Web.PageTitle})
|
|
}
|
|
|
|
if cpf.Web.MaxConnections != nil {
|
|
promArgs = append(promArgs, monitoringv1.Argument{Name: "web.max-connections", Value: fmt.Sprintf("%d", *cpf.Web.MaxConnections)})
|
|
}
|
|
}
|
|
|
|
if cpf.EnableRemoteWriteReceiver {
|
|
promArgs = cg.WithMinimumVersion("2.33.0").AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "web.enable-remote-write-receiver"})
|
|
if len(cpf.RemoteWriteReceiverMessageVersions) > 0 {
|
|
versions := make([]string, 0, len(cpf.RemoteWriteReceiverMessageVersions))
|
|
for _, v := range cpf.RemoteWriteReceiverMessageVersions {
|
|
versions = append(versions, toProtobufMessageVersion(v))
|
|
}
|
|
promArgs = cg.WithMinimumVersion("2.54.0").AppendCommandlineArgument(
|
|
promArgs,
|
|
monitoringv1.Argument{
|
|
Name: "web.remote-write-receiver.accepted-protobuf-messages",
|
|
Value: strings.Join(versions, ","),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
for _, rw := range cpf.RemoteWrite {
|
|
if ptr.Deref(rw.MessageVersion, monitoringv1.RemoteWriteMessageVersion1_0) == monitoringv1.RemoteWriteMessageVersion2_0 {
|
|
promArgs = cg.WithMinimumVersion("2.54.0").AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "enable-feature", Value: "metadata-wal-records"})
|
|
}
|
|
}
|
|
|
|
// Turn on the OTLP receiver endpoint automatically if/when the OTLP config isn't empty.
|
|
if (cpf.EnableOTLPReceiver != nil && *cpf.EnableOTLPReceiver) || (cpf.EnableOTLPReceiver == nil && cpf.OTLP != nil) {
|
|
if cg.version.Major >= 3 {
|
|
promArgs = cg.AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "web.enable-otlp-receiver"})
|
|
} else {
|
|
promArgs = cg.WithMinimumVersion("2.47.0").AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "enable-feature", Value: "otlp-write-receiver"})
|
|
}
|
|
}
|
|
|
|
if len(cpf.EnableFeatures) > 0 {
|
|
efs := make([]string, len(cpf.EnableFeatures))
|
|
for i := range cpf.EnableFeatures {
|
|
efs[i] = string(cpf.EnableFeatures[i])
|
|
}
|
|
promArgs = cg.WithMinimumVersion("2.25.0").AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "enable-feature", Value: strings.Join(efs, ",")})
|
|
}
|
|
|
|
if cpf.ExternalURL != "" {
|
|
promArgs = append(promArgs, monitoringv1.Argument{Name: "web.external-url", Value: cpf.ExternalURL})
|
|
}
|
|
|
|
promArgs = append(promArgs, monitoringv1.Argument{Name: "web.route-prefix", Value: cpf.WebRoutePrefix()})
|
|
|
|
if cpf.LogLevel != "" && cpf.LogLevel != "info" {
|
|
promArgs = append(promArgs, monitoringv1.Argument{Name: "log.level", Value: cpf.LogLevel})
|
|
}
|
|
|
|
if cpf.LogFormat != "" && cpf.LogFormat != "logfmt" {
|
|
promArgs = cg.WithMinimumVersion("2.6.0").AppendCommandlineArgument(promArgs, monitoringv1.Argument{Name: "log.format", Value: cpf.LogFormat})
|
|
}
|
|
|
|
if cpf.ListenLocal {
|
|
promArgs = append(promArgs, monitoringv1.Argument{Name: "web.listen-address", Value: "127.0.0.1:9090"})
|
|
}
|
|
|
|
return promArgs
|
|
}
|
|
|
|
func (cg *ConfigGenerator) BuildPodMetadata() (map[string]string, map[string]string) {
|
|
podAnnotations := map[string]string{
|
|
"kubectl.kubernetes.io/default-container": "prometheus",
|
|
}
|
|
|
|
podLabels := map[string]string{
|
|
"app.kubernetes.io/version": cg.version.String(),
|
|
}
|
|
|
|
podMetadata := cg.prom.GetCommonPrometheusFields().PodMetadata
|
|
if podMetadata != nil {
|
|
for k, v := range podMetadata.Labels {
|
|
podLabels[k] = v
|
|
}
|
|
|
|
for k, v := range podMetadata.Annotations {
|
|
podAnnotations[k] = v
|
|
}
|
|
}
|
|
|
|
return podAnnotations, podLabels
|
|
}
|
|
|
|
// BuildProbes returns a tuple of 3 probe definitions:
|
|
// 1. startup probe
|
|
// 2. readiness probe
|
|
// 3. liveness probe
|
|
//
|
|
// The /-/ready handler returns OK only after the TSDB initialization has
|
|
// completed. The WAL replay can take a significant time for large setups
|
|
// hence we enable the startup probe with a generous failure threshold (15
|
|
// minutes) to ensure that the readiness probe only comes into effect once
|
|
// Prometheus is effectively ready.
|
|
// We don't want to use the /-/healthy handler here because it returns OK as
|
|
// soon as the web server is started (irrespective of the WAL replay).
|
|
func (cg *ConfigGenerator) BuildProbes() (*v1.Probe, *v1.Probe, *v1.Probe) {
|
|
readyProbeHandler := cg.buildProbeHandler("/-/ready")
|
|
startupPeriodSeconds, startupFailureThreshold := getStatupProbePeriodSecondsAndFailureThreshold(cg.prom.GetCommonPrometheusFields().MaximumStartupDurationSeconds)
|
|
|
|
startupProbe := &v1.Probe{
|
|
ProbeHandler: readyProbeHandler,
|
|
TimeoutSeconds: ProbeTimeoutSeconds,
|
|
PeriodSeconds: startupPeriodSeconds,
|
|
FailureThreshold: startupFailureThreshold,
|
|
}
|
|
|
|
readinessProbe := &v1.Probe{
|
|
ProbeHandler: readyProbeHandler,
|
|
TimeoutSeconds: ProbeTimeoutSeconds,
|
|
PeriodSeconds: 5,
|
|
FailureThreshold: 3,
|
|
}
|
|
|
|
livenessProbe := &v1.Probe{
|
|
ProbeHandler: cg.buildProbeHandler("/-/healthy"),
|
|
TimeoutSeconds: ProbeTimeoutSeconds,
|
|
PeriodSeconds: 5,
|
|
FailureThreshold: 6,
|
|
}
|
|
|
|
return startupProbe, readinessProbe, livenessProbe
|
|
}
|
|
|
|
func (cg *ConfigGenerator) buildProbeHandler(probePath string) v1.ProbeHandler {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
|
|
probePath = path.Clean(cpf.WebRoutePrefix() + probePath)
|
|
handler := v1.ProbeHandler{}
|
|
if cpf.ListenLocal {
|
|
probeURL := url.URL{
|
|
Scheme: "http",
|
|
Host: "localhost:9090",
|
|
Path: probePath,
|
|
}
|
|
handler.Exec = operator.ExecAction(probeURL.String())
|
|
|
|
return handler
|
|
}
|
|
|
|
handler.HTTPGet = &v1.HTTPGetAction{
|
|
Path: probePath,
|
|
Port: intstr.FromString(cpf.PortName),
|
|
}
|
|
if cpf.Web != nil && cpf.Web.TLSConfig != nil && cg.IsCompatible() {
|
|
handler.HTTPGet.Scheme = v1.URISchemeHTTPS
|
|
}
|
|
|
|
return handler
|
|
}
|
|
|
|
func getStatupProbePeriodSecondsAndFailureThreshold(maxStartupDurationSeconds *int32) (int32, int32) {
|
|
var (
|
|
startupPeriodSeconds float64 = 15
|
|
startupFailureThreshold float64 = 60
|
|
)
|
|
|
|
maximumStartupDurationSeconds := float64(ptr.Deref(maxStartupDurationSeconds, 0))
|
|
|
|
if maximumStartupDurationSeconds >= 60 {
|
|
startupFailureThreshold = math.Ceil(maximumStartupDurationSeconds / 60)
|
|
startupPeriodSeconds = math.Ceil(maximumStartupDurationSeconds / startupFailureThreshold)
|
|
}
|
|
|
|
return int32(startupPeriodSeconds), int32(startupFailureThreshold)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generatePodMonitorConfig(
|
|
m *monitoringv1.PodMonitor,
|
|
ep monitoringv1.PodMetricsEndpoint,
|
|
i int, apiserverConfig *monitoringv1.APIServerConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32,
|
|
) yaml.MapSlice {
|
|
scrapeClass := cg.getScrapeClassOrDefault(m.Spec.ScrapeClassName)
|
|
|
|
cfg := yaml.MapSlice{
|
|
{
|
|
Key: "job_name",
|
|
Value: fmt.Sprintf("podMonitor/%s/%s/%d", m.Namespace, m.Name, i),
|
|
},
|
|
}
|
|
cfg = cg.AddHonorLabels(cfg, ep.HonorLabels)
|
|
cfg = cg.AddHonorTimestamps(cfg, ep.HonorTimestamps)
|
|
cfg = cg.AddTrackTimestampsStaleness(cfg, ep.TrackTimestampsStaleness)
|
|
|
|
attachMetaConfig := mergeAttachMetadataWithScrapeClass(m.Spec.AttachMetadata, scrapeClass, "2.35.0")
|
|
|
|
s := store.ForNamespace(m.Namespace)
|
|
|
|
cfg = append(cfg, cg.generateK8SSDConfig(m.Spec.NamespaceSelector, m.Namespace, apiserverConfig, s, kubernetesSDRolePod, attachMetaConfig))
|
|
|
|
if ep.Interval != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_interval", Value: ep.Interval})
|
|
}
|
|
if ep.ScrapeTimeout != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_timeout", Value: ep.ScrapeTimeout})
|
|
}
|
|
if ep.Path != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metrics_path", Value: ep.Path})
|
|
}
|
|
if ep.ProxyURL != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "proxy_url", Value: ep.ProxyURL})
|
|
}
|
|
if ep.Params != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "params", Value: ep.Params})
|
|
}
|
|
if ep.Scheme != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scheme", Value: ep.Scheme})
|
|
}
|
|
if ep.FollowRedirects != nil {
|
|
cfg = cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "follow_redirects", *ep.FollowRedirects)
|
|
}
|
|
if ep.EnableHttp2 != nil {
|
|
cfg = cg.WithMinimumVersion("2.35.0").AppendMapItem(cfg, "enable_http2", *ep.EnableHttp2)
|
|
}
|
|
|
|
cfg = cg.addTLStoYaml(cfg, s, mergeSafeTLSConfigWithScrapeClass(ep.TLSConfig, scrapeClass))
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if ep.BearerTokenSecret.Name != "" {
|
|
cg.logger.Debug("'bearerTokenSecret' is deprecated, use 'authorization' instead.")
|
|
|
|
b, err := s.GetSecretKey(ep.BearerTokenSecret)
|
|
if err != nil {
|
|
cg.logger.Error("invalid bearer token secret reference", "err", err)
|
|
} else {
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token", Value: string(b)})
|
|
}
|
|
}
|
|
|
|
cfg = cg.addBasicAuthToYaml(cfg, s, ep.BasicAuth)
|
|
cfg = cg.addOAuth2ToYaml(cfg, s, ep.OAuth2)
|
|
|
|
cfg = cg.addSafeAuthorizationToYaml(cfg, s, ep.Authorization)
|
|
|
|
relabelings := initRelabelings()
|
|
|
|
if ep.FilterRunning == nil || *ep.FilterRunning {
|
|
relabelings = append(relabelings, generateRunningFilter())
|
|
}
|
|
|
|
// Filter targets by pods selected by the monitor.
|
|
// Exact label matches.
|
|
for _, k := range util.SortedKeys(m.Spec.Selector.MatchLabels) {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_label_" + sanitizeLabelName(k), "__meta_kubernetes_pod_labelpresent_" + sanitizeLabelName(k)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", m.Spec.Selector.MatchLabels[k])},
|
|
})
|
|
}
|
|
// Set based label matching. We have to map the valid relations
|
|
// `In`, `NotIn`, `Exists`, and `DoesNotExist`, into relabeling rules.
|
|
for _, exp := range m.Spec.Selector.MatchExpressions {
|
|
switch exp.Operator {
|
|
case metav1.LabelSelectorOpIn:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_pod_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|"))},
|
|
})
|
|
case metav1.LabelSelectorOpNotIn:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_pod_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|"))},
|
|
})
|
|
case metav1.LabelSelectorOpExists:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: "true"},
|
|
})
|
|
case metav1.LabelSelectorOpDoesNotExist:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: "true"},
|
|
})
|
|
}
|
|
}
|
|
|
|
// Filter targets based on correct port for the endpoint.
|
|
if ep.Port != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_name"}},
|
|
{Key: "regex", Value: ep.Port},
|
|
})
|
|
} else if ep.TargetPort != nil { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
cg.logger.Warn("'targetPort' is deprecated, use 'port' instead.")
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if ep.TargetPort.StrVal != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_name"}},
|
|
{Key: "regex", Value: ep.TargetPort.String()},
|
|
})
|
|
} else if ep.TargetPort.IntVal != 0 { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_number"}},
|
|
{Key: "regex", Value: ep.TargetPort.String()},
|
|
})
|
|
}
|
|
}
|
|
|
|
// Relabel namespace and pod and service labels into proper labels.
|
|
relabelings = append(relabelings, []yaml.MapSlice{
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_namespace"}},
|
|
{Key: "target_label", Value: "namespace"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_name"}},
|
|
{Key: "target_label", Value: "container"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_name"}},
|
|
{Key: "target_label", Value: "pod"},
|
|
},
|
|
}...)
|
|
|
|
// Relabel targetLabels from Pod onto target.
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
for _, l := range append(m.Spec.PodTargetLabels, cpf.PodTargetLabels...) {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_label_" + sanitizeLabelName(l)}},
|
|
{Key: "target_label", Value: sanitizeLabelName(l)},
|
|
{Key: "regex", Value: "(.+)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
})
|
|
}
|
|
|
|
// By default, generate a safe job name from the PodMonitor. We also keep
|
|
// this around if a jobLabel is set in case the targets don't actually have a
|
|
// value for it. A single pod may potentially have multiple metrics
|
|
// endpoints, therefore the endpoints labels is filled with the ports name or
|
|
// as a fallback the port number.
|
|
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "target_label", Value: "job"},
|
|
{Key: "replacement", Value: fmt.Sprintf("%s/%s", m.GetNamespace(), m.GetName())},
|
|
})
|
|
if m.Spec.JobLabel != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_label_" + sanitizeLabelName(m.Spec.JobLabel)}},
|
|
{Key: "target_label", Value: "job"},
|
|
{Key: "regex", Value: "(.+)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
})
|
|
}
|
|
|
|
if ep.Port != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "target_label", Value: "endpoint"},
|
|
{Key: "replacement", Value: ep.Port},
|
|
})
|
|
} else if ep.TargetPort != nil && ep.TargetPort.String() != "" { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "target_label", Value: "endpoint"},
|
|
{Key: "replacement", Value: ep.TargetPort.String()}, //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
})
|
|
}
|
|
|
|
// Add scrape class relabelings if there is any.
|
|
relabelings = append(relabelings, generateRelabelConfig(scrapeClass.Relabelings)...)
|
|
|
|
labeler := namespacelabeler.New(cpf.EnforcedNamespaceLabel, cpf.ExcludedFromEnforcement, false)
|
|
relabelings = append(relabelings, generateRelabelConfig(labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, ep.RelabelConfigs))...)
|
|
|
|
// DaemonSet mode doesn't support sharding.
|
|
if !cg.daemonSet {
|
|
relabelings = generateAddressShardingRelabelingRules(relabelings, shards)
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelings})
|
|
|
|
cfg = cg.AddLimitsToYAML(cfg, sampleLimitKey, m.Spec.SampleLimit, cpf.EnforcedSampleLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, targetLimitKey, m.Spec.TargetLimit, cpf.EnforcedTargetLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelLimitKey, m.Spec.LabelLimit, cpf.EnforcedLabelLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelNameLengthLimitKey, m.Spec.LabelNameLengthLimit, cpf.EnforcedLabelNameLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelValueLengthLimitKey, m.Spec.LabelValueLengthLimit, cpf.EnforcedLabelValueLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, keepDroppedTargetsKey, m.Spec.KeepDroppedTargets, cpf.EnforcedKeepDroppedTargets)
|
|
cfg = cg.addNativeHistogramConfig(cfg, m.Spec.NativeHistogramConfig)
|
|
cfg = cg.addScrapeProtocols(cfg, m.Spec.ScrapeProtocols)
|
|
cfg = cg.addScrapeFallbackProtocol(cfg, m.Spec.ScrapeFallbackProtocol)
|
|
|
|
if bodySizeLimit := getLowerByteSize(m.Spec.BodySizeLimit, &cpf); !isByteSizeEmpty(bodySizeLimit) {
|
|
cfg = cg.WithMinimumVersion("2.28.0").AppendMapItem(cfg, "body_size_limit", bodySizeLimit)
|
|
}
|
|
|
|
metricRelabelings := []monitoringv1.RelabelConfig{}
|
|
metricRelabelings = append(metricRelabelings, scrapeClass.MetricRelabelings...)
|
|
metricRelabelings = append(metricRelabelings, labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, ep.MetricRelabelConfigs)...)
|
|
|
|
if len(metricRelabelings) > 0 {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metric_relabel_configs", Value: generateRelabelConfig(metricRelabelings)})
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
// generateProbeConfig builds the prometheus configuration for a probe. This function
|
|
// assumes that it will never receive a probe with empty targets, since the
|
|
// operator filters those in the validation step in SelectProbes().
|
|
func (cg *ConfigGenerator) generateProbeConfig(
|
|
m *monitoringv1.Probe,
|
|
apiserverConfig *monitoringv1.APIServerConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32,
|
|
) yaml.MapSlice {
|
|
scrapeClass := cg.getScrapeClassOrDefault(m.Spec.ScrapeClassName)
|
|
|
|
jobName := fmt.Sprintf("probe/%s/%s", m.Namespace, m.Name)
|
|
cfg := yaml.MapSlice{
|
|
{
|
|
Key: "job_name",
|
|
Value: jobName,
|
|
},
|
|
}
|
|
|
|
hTs := true
|
|
cfg = cg.AddHonorTimestamps(cfg, &hTs)
|
|
|
|
cfg = append(cfg, yaml.MapItem{Key: "metrics_path", Value: m.Spec.ProberSpec.Path})
|
|
|
|
if m.Spec.Interval != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_interval", Value: m.Spec.Interval})
|
|
}
|
|
if m.Spec.ScrapeTimeout != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_timeout", Value: m.Spec.ScrapeTimeout})
|
|
}
|
|
if m.Spec.ProberSpec.Scheme != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scheme", Value: m.Spec.ProberSpec.Scheme})
|
|
}
|
|
if m.Spec.ProberSpec.ProxyURL != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "proxy_url", Value: m.Spec.ProberSpec.ProxyURL})
|
|
}
|
|
|
|
if m.Spec.Module != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "params", Value: yaml.MapSlice{
|
|
{Key: "module", Value: []string{m.Spec.Module}},
|
|
}})
|
|
}
|
|
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
cfg = cg.AddLimitsToYAML(cfg, sampleLimitKey, m.Spec.SampleLimit, cpf.EnforcedSampleLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, targetLimitKey, m.Spec.TargetLimit, cpf.EnforcedTargetLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelLimitKey, m.Spec.LabelLimit, cpf.EnforcedLabelLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelNameLengthLimitKey, m.Spec.LabelNameLengthLimit, cpf.EnforcedLabelNameLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelValueLengthLimitKey, m.Spec.LabelValueLengthLimit, cpf.EnforcedLabelValueLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, keepDroppedTargetsKey, m.Spec.KeepDroppedTargets, cpf.EnforcedKeepDroppedTargets)
|
|
cfg = cg.addNativeHistogramConfig(cfg, m.Spec.NativeHistogramConfig)
|
|
cfg = cg.addScrapeProtocols(cfg, m.Spec.ScrapeProtocols)
|
|
cfg = cg.addScrapeFallbackProtocol(cfg, m.Spec.ScrapeFallbackProtocol)
|
|
|
|
if cpf.EnforcedBodySizeLimit != "" {
|
|
cfg = cg.WithMinimumVersion("2.28.0").AppendMapItem(cfg, "body_size_limit", cpf.EnforcedBodySizeLimit)
|
|
}
|
|
|
|
relabelings := initRelabelings()
|
|
|
|
if m.Spec.JobName != "" {
|
|
relabelings = append(relabelings, []yaml.MapSlice{
|
|
{
|
|
{Key: "target_label", Value: "job"},
|
|
{Key: "replacement", Value: m.Spec.JobName},
|
|
},
|
|
}...)
|
|
}
|
|
labeler := namespacelabeler.New(cpf.EnforcedNamespaceLabel, cpf.ExcludedFromEnforcement, false)
|
|
|
|
s := store.ForNamespace(m.Namespace)
|
|
|
|
// As stated in the CRD documentation, if both StaticConfig and Ingress are
|
|
// defined, the former takes precedence which is why the first case statement
|
|
// checks for m.Spec.Targets.StaticConfig.
|
|
switch {
|
|
case m.Spec.Targets.StaticConfig != nil:
|
|
// Generate static_config section.
|
|
staticConfig := yaml.MapSlice{
|
|
{Key: "targets", Value: m.Spec.Targets.StaticConfig.Targets},
|
|
}
|
|
|
|
if m.Spec.Targets.StaticConfig.Labels != nil {
|
|
if _, ok := m.Spec.Targets.StaticConfig.Labels["namespace"]; !ok {
|
|
m.Spec.Targets.StaticConfig.Labels["namespace"] = m.Namespace
|
|
}
|
|
} else {
|
|
m.Spec.Targets.StaticConfig.Labels = map[string]string{"namespace": m.Namespace}
|
|
}
|
|
|
|
staticConfig = append(staticConfig, yaml.MapSlice{
|
|
{Key: "labels", Value: m.Spec.Targets.StaticConfig.Labels},
|
|
}...)
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "static_configs",
|
|
Value: []yaml.MapSlice{staticConfig},
|
|
})
|
|
|
|
// Relabelings for prober.
|
|
relabelings = append(relabelings, []yaml.MapSlice{
|
|
{
|
|
{Key: "source_labels", Value: []string{"__address__"}},
|
|
{Key: "target_label", Value: "__param_target"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__param_target"}},
|
|
{Key: "target_label", Value: "instance"},
|
|
},
|
|
{
|
|
{Key: "target_label", Value: "__address__"},
|
|
{Key: "replacement", Value: m.Spec.ProberSpec.URL},
|
|
},
|
|
}...)
|
|
|
|
// Add scrape class relabelings if there is any.
|
|
relabelings = append(relabelings, generateRelabelConfig(scrapeClass.Relabelings)...)
|
|
|
|
// Add configured relabelings.
|
|
xc := labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, m.Spec.Targets.StaticConfig.RelabelConfigs)
|
|
relabelings = append(relabelings, generateRelabelConfig(xc)...)
|
|
|
|
case m.Spec.Targets.Ingress != nil:
|
|
// Generate kubernetes_sd_config section for the ingress resources.
|
|
// Filter targets by ingresses selected by the monitor.
|
|
// Exact label matches.
|
|
for _, k := range util.SortedKeys(m.Spec.Targets.Ingress.Selector.MatchLabels) {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_label_" + sanitizeLabelName(k), "__meta_kubernetes_ingress_labelpresent_" + sanitizeLabelName(k)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", m.Spec.Targets.Ingress.Selector.MatchLabels[k])},
|
|
})
|
|
}
|
|
|
|
// Set based label matching. We have to map the valid relations
|
|
// `In`, `NotIn`, `Exists`, and `DoesNotExist`, into relabeling rules.
|
|
for _, exp := range m.Spec.Targets.Ingress.Selector.MatchExpressions {
|
|
switch exp.Operator {
|
|
case metav1.LabelSelectorOpIn:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_ingress_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|"))},
|
|
})
|
|
case metav1.LabelSelectorOpNotIn:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_ingress_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|"))},
|
|
})
|
|
case metav1.LabelSelectorOpExists:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: "true"},
|
|
})
|
|
case metav1.LabelSelectorOpDoesNotExist:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: "true"},
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, cg.generateK8SSDConfig(m.Spec.Targets.Ingress.NamespaceSelector, m.Namespace, apiserverConfig, s, kubernetesSDRoleIngress, nil))
|
|
|
|
// Relabelings for ingress SD.
|
|
relabelings = append(relabelings, []yaml.MapSlice{
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_scheme", "__address__", "__meta_kubernetes_ingress_path"}},
|
|
{Key: "separator", Value: ";"},
|
|
{Key: "regex", Value: "(.+);(.+);(.+)"},
|
|
{Key: "target_label", Value: "__param_target"},
|
|
{Key: "replacement", Value: "${1}://${2}${3}"},
|
|
{Key: "action", Value: "replace"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_namespace"}},
|
|
{Key: "target_label", Value: "namespace"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_ingress_name"}},
|
|
{Key: "target_label", Value: "ingress"},
|
|
},
|
|
}...)
|
|
|
|
// Relabelings for prober.
|
|
relabelings = append(relabelings, []yaml.MapSlice{
|
|
{
|
|
{Key: "source_labels", Value: []string{"__address__"}},
|
|
{Key: "separator", Value: ";"},
|
|
{Key: "regex", Value: "(.*)"},
|
|
{Key: "target_label", Value: "__tmp_ingress_address"},
|
|
{Key: "replacement", Value: "$1"},
|
|
{Key: "action", Value: "replace"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__param_target"}},
|
|
{Key: "target_label", Value: "instance"},
|
|
},
|
|
{
|
|
{Key: "target_label", Value: "__address__"},
|
|
{Key: "replacement", Value: m.Spec.ProberSpec.URL},
|
|
},
|
|
}...)
|
|
|
|
// Add scrape class relabelings if there is any.
|
|
relabelings = append(relabelings, generateRelabelConfig(scrapeClass.Relabelings)...)
|
|
|
|
// Add configured relabelings.
|
|
relabelings = append(relabelings, generateRelabelConfig(labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, m.Spec.Targets.Ingress.RelabelConfigs))...)
|
|
}
|
|
|
|
relabelings = generateAddressShardingRelabelingRulesForProbes(relabelings, shards)
|
|
cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelings})
|
|
|
|
cfg = cg.addTLStoYaml(cfg, s, mergeSafeTLSConfigWithScrapeClass(m.Spec.TLSConfig, scrapeClass))
|
|
|
|
if m.Spec.BearerTokenSecret.Name != "" {
|
|
b, err := s.GetSecretKey(m.Spec.BearerTokenSecret)
|
|
if err != nil {
|
|
cg.logger.Error("invalid bearer token reference", "err", err)
|
|
} else {
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token", Value: string(b)})
|
|
}
|
|
}
|
|
|
|
cfg = cg.addBasicAuthToYaml(cfg, s, m.Spec.BasicAuth)
|
|
cfg = cg.addOAuth2ToYaml(cfg, s, m.Spec.OAuth2)
|
|
|
|
cfg = cg.addSafeAuthorizationToYaml(cfg, s, m.Spec.Authorization)
|
|
|
|
metricRelabelings := []monitoringv1.RelabelConfig{}
|
|
metricRelabelings = append(metricRelabelings, scrapeClass.MetricRelabelings...)
|
|
metricRelabelings = append(metricRelabelings, labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, m.Spec.MetricRelabelConfigs)...)
|
|
|
|
if len(metricRelabelings) > 0 {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metric_relabel_configs", Value: generateRelabelConfig(metricRelabelings)})
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateServiceMonitorConfig(
|
|
m *monitoringv1.ServiceMonitor,
|
|
ep monitoringv1.Endpoint,
|
|
i int,
|
|
apiserverConfig *monitoringv1.APIServerConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32,
|
|
) yaml.MapSlice {
|
|
scrapeClass := cg.getScrapeClassOrDefault(m.Spec.ScrapeClassName)
|
|
|
|
cfg := yaml.MapSlice{
|
|
{
|
|
Key: "job_name",
|
|
Value: fmt.Sprintf("serviceMonitor/%s/%s/%d", m.Namespace, m.Name, i),
|
|
},
|
|
}
|
|
cfg = cg.AddHonorLabels(cfg, ep.HonorLabels)
|
|
cfg = cg.AddHonorTimestamps(cfg, ep.HonorTimestamps)
|
|
cfg = cg.AddTrackTimestampsStaleness(cfg, ep.TrackTimestampsStaleness)
|
|
|
|
attachMetaConfig := mergeAttachMetadataWithScrapeClass(m.Spec.AttachMetadata, scrapeClass, "2.37.0")
|
|
|
|
s := store.ForNamespace(m.Namespace)
|
|
|
|
role := cg.endpointRoleFlavor()
|
|
cfg = append(cfg, cg.generateK8SSDConfig(m.Spec.NamespaceSelector, m.Namespace, apiserverConfig, s, role, attachMetaConfig))
|
|
|
|
if ep.Interval != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_interval", Value: ep.Interval})
|
|
}
|
|
if ep.ScrapeTimeout != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_timeout", Value: ep.ScrapeTimeout})
|
|
}
|
|
if ep.Path != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metrics_path", Value: ep.Path})
|
|
}
|
|
if ep.ProxyURL != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "proxy_url", Value: ep.ProxyURL})
|
|
}
|
|
if ep.Params != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "params", Value: ep.Params})
|
|
}
|
|
if ep.Scheme != "" {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scheme", Value: ep.Scheme})
|
|
}
|
|
if ep.FollowRedirects != nil {
|
|
cfg = cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "follow_redirects", *ep.FollowRedirects)
|
|
}
|
|
if ep.EnableHttp2 != nil {
|
|
cfg = cg.WithMinimumVersion("2.35.0").AppendMapItem(cfg, "enable_http2", *ep.EnableHttp2)
|
|
}
|
|
|
|
cfg = cg.addOAuth2ToYaml(cfg, s, ep.OAuth2)
|
|
|
|
cfg = cg.addTLStoYaml(cfg, s, mergeTLSConfigWithScrapeClass(ep.TLSConfig, scrapeClass))
|
|
|
|
if ep.BearerTokenFile != "" { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
cg.logger.Debug("'bearerTokenFile' is deprecated, use 'authorization' instead.")
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: ep.BearerTokenFile}) //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
}
|
|
|
|
if ep.BearerTokenSecret != nil && ep.BearerTokenSecret.Name != "" { //nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
cg.logger.Debug("'bearerTokenSecret' is deprecated, use 'authorization' instead.")
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
b, err := s.GetSecretKey(*ep.BearerTokenSecret)
|
|
if err != nil {
|
|
cg.logger.Error("invalid bearer token reference", "err", err)
|
|
} else {
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token", Value: string(b)})
|
|
}
|
|
}
|
|
|
|
cfg = cg.addBasicAuthToYaml(cfg, store.ForNamespace(m.Namespace), ep.BasicAuth)
|
|
|
|
cfg = cg.addSafeAuthorizationToYaml(cfg, s, ep.Authorization)
|
|
|
|
relabelings := initRelabelings()
|
|
|
|
// Filter targets by services selected by the monitor.
|
|
// Exact label matches.
|
|
for _, k := range util.SortedKeys(m.Spec.Selector.MatchLabels) {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(k), "__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(k)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", m.Spec.Selector.MatchLabels[k])},
|
|
})
|
|
}
|
|
// Set based label matching. We have to map the valid relations
|
|
// `In`, `NotIn`, `Exists`, and `DoesNotExist`, into relabeling rules.
|
|
for _, exp := range m.Spec.Selector.MatchExpressions {
|
|
switch exp.Operator {
|
|
case metav1.LabelSelectorOpIn:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|"))},
|
|
})
|
|
case metav1.LabelSelectorOpNotIn:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(exp.Key), "__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: fmt.Sprintf("(%s);true", strings.Join(exp.Values, "|"))},
|
|
})
|
|
case metav1.LabelSelectorOpExists:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: "true"},
|
|
})
|
|
case metav1.LabelSelectorOpDoesNotExist:
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_labelpresent_" + sanitizeLabelName(exp.Key)}},
|
|
{Key: "regex", Value: "true"},
|
|
})
|
|
}
|
|
}
|
|
|
|
// Filter targets based on correct port for the endpoint.
|
|
if ep.Port != "" {
|
|
sourceLabels := []string{"__meta_kubernetes_endpoint_port_name"}
|
|
if role == kubernetesSDRoleEndpointSlice {
|
|
sourceLabels = []string{"__meta_kubernetes_endpointslice_port_name"}
|
|
}
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
yaml.MapItem{Key: "source_labels", Value: sourceLabels},
|
|
{Key: "regex", Value: ep.Port},
|
|
})
|
|
} else if ep.TargetPort != nil {
|
|
if ep.TargetPort.StrVal != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_name"}},
|
|
{Key: "regex", Value: ep.TargetPort.String()},
|
|
})
|
|
} else if ep.TargetPort.IntVal != 0 {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_number"}},
|
|
{Key: "regex", Value: ep.TargetPort.String()},
|
|
})
|
|
}
|
|
}
|
|
|
|
sourceLabels := []string{"__meta_kubernetes_endpoint_address_target_kind", "__meta_kubernetes_endpoint_address_target_name"}
|
|
if role == kubernetesSDRoleEndpointSlice {
|
|
sourceLabels = []string{"__meta_kubernetes_endpointslice_address_target_kind", "__meta_kubernetes_endpointslice_address_target_name"}
|
|
}
|
|
|
|
// Relabel namespace and pod and service labels into proper labels.
|
|
relabelings = append(relabelings, []yaml.MapSlice{
|
|
{ // Relabel node labels with meta labels available with Prometheus >= v2.3.
|
|
yaml.MapItem{Key: "source_labels", Value: sourceLabels},
|
|
{Key: "separator", Value: ";"},
|
|
{Key: "regex", Value: "Node;(.*)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
{Key: "target_label", Value: "node"},
|
|
},
|
|
{ // Relabel pod labels for >=v2.3 meta labels
|
|
yaml.MapItem{Key: "source_labels", Value: sourceLabels},
|
|
{Key: "separator", Value: ";"},
|
|
{Key: "regex", Value: "Pod;(.*)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
{Key: "target_label", Value: "pod"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_namespace"}},
|
|
{Key: "target_label", Value: "namespace"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_name"}},
|
|
{Key: "target_label", Value: "service"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_name"}},
|
|
{Key: "target_label", Value: "pod"},
|
|
},
|
|
{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_name"}},
|
|
{Key: "target_label", Value: "container"},
|
|
},
|
|
}...)
|
|
|
|
if ptr.Deref(ep.FilterRunning, true) {
|
|
relabelings = append(relabelings, generateRunningFilter())
|
|
}
|
|
|
|
// Relabel targetLabels from Service onto target.
|
|
for _, l := range m.Spec.TargetLabels {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(l)}},
|
|
{Key: "target_label", Value: sanitizeLabelName(l)},
|
|
{Key: "regex", Value: "(.+)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
})
|
|
}
|
|
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
for _, l := range append(m.Spec.PodTargetLabels, cpf.PodTargetLabels...) {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_label_" + sanitizeLabelName(l)}},
|
|
{Key: "target_label", Value: sanitizeLabelName(l)},
|
|
{Key: "regex", Value: "(.+)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
})
|
|
}
|
|
|
|
// By default, generate a safe job name from the service name. We also keep
|
|
// this around if a jobLabel is set in case the targets don't actually have a
|
|
// value for it.
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_name"}},
|
|
{Key: "target_label", Value: "job"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
})
|
|
if m.Spec.JobLabel != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_label_" + sanitizeLabelName(m.Spec.JobLabel)}},
|
|
{Key: "target_label", Value: "job"},
|
|
{Key: "regex", Value: "(.+)"},
|
|
{Key: "replacement", Value: "${1}"},
|
|
})
|
|
}
|
|
|
|
// A single service may potentially have multiple metrics
|
|
// endpoints, therefore the endpoints labels is filled with the ports name or
|
|
// as a fallback the port number.
|
|
if ep.Port != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "target_label", Value: "endpoint"},
|
|
{Key: "replacement", Value: ep.Port},
|
|
})
|
|
} else if ep.TargetPort != nil && ep.TargetPort.String() != "" {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "target_label", Value: "endpoint"},
|
|
{Key: "replacement", Value: ep.TargetPort.String()},
|
|
})
|
|
}
|
|
|
|
// Add scrape class relabelings if there is any.
|
|
relabelings = append(relabelings, generateRelabelConfig(scrapeClass.Relabelings)...)
|
|
|
|
labeler := namespacelabeler.New(cpf.EnforcedNamespaceLabel, cpf.ExcludedFromEnforcement, false)
|
|
relabelings = append(relabelings, generateRelabelConfig(labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, ep.RelabelConfigs))...)
|
|
|
|
relabelings = generateAddressShardingRelabelingRules(relabelings, shards)
|
|
cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelings})
|
|
|
|
cfg = cg.AddLimitsToYAML(cfg, sampleLimitKey, m.Spec.SampleLimit, cpf.EnforcedSampleLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, targetLimitKey, m.Spec.TargetLimit, cpf.EnforcedTargetLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelLimitKey, m.Spec.LabelLimit, cpf.EnforcedLabelLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelNameLengthLimitKey, m.Spec.LabelNameLengthLimit, cpf.EnforcedLabelNameLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelValueLengthLimitKey, m.Spec.LabelValueLengthLimit, cpf.EnforcedLabelValueLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, keepDroppedTargetsKey, m.Spec.KeepDroppedTargets, cpf.EnforcedKeepDroppedTargets)
|
|
cfg = cg.addNativeHistogramConfig(cfg, m.Spec.NativeHistogramConfig)
|
|
cfg = cg.addScrapeProtocols(cfg, m.Spec.ScrapeProtocols)
|
|
cfg = cg.addScrapeFallbackProtocol(cfg, m.Spec.ScrapeFallbackProtocol)
|
|
|
|
if bodySizeLimit := getLowerByteSize(m.Spec.BodySizeLimit, &cpf); !isByteSizeEmpty(bodySizeLimit) {
|
|
cfg = cg.WithMinimumVersion("2.28.0").AppendMapItem(cfg, "body_size_limit", bodySizeLimit)
|
|
}
|
|
|
|
metricRelabelings := []monitoringv1.RelabelConfig{}
|
|
metricRelabelings = append(metricRelabelings, scrapeClass.MetricRelabelings...)
|
|
metricRelabelings = append(metricRelabelings, labeler.GetRelabelingConfigs(m.TypeMeta, m.ObjectMeta, ep.MetricRelabelConfigs)...)
|
|
|
|
if len(metricRelabelings) > 0 {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metric_relabel_configs", Value: generateRelabelConfig(metricRelabelings)})
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
func generateRunningFilter() yaml.MapSlice {
|
|
return yaml.MapSlice{
|
|
{Key: "action", Value: "drop"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_phase"}},
|
|
{Key: "regex", Value: "(Failed|Succeeded)"},
|
|
}
|
|
}
|
|
|
|
func (cg *ConfigGenerator) getLimit(user *uint64, enforced *uint64) *uint64 {
|
|
if ptr.Deref(enforced, 0) == 0 {
|
|
return user
|
|
}
|
|
|
|
if ptr.Deref(user, 0) == 0 {
|
|
// With Prometheus >= 2.45.0, the limit value in the global section will always apply, hence there's no need to set the value explicitly.
|
|
if cg.version.GTE(semver.MustParse("2.45.0")) {
|
|
return nil
|
|
}
|
|
return enforced
|
|
}
|
|
|
|
if ptr.Deref(enforced, 0) > ptr.Deref(user, 0) {
|
|
return user
|
|
}
|
|
|
|
return enforced
|
|
}
|
|
|
|
func generateAddressShardingRelabelingRules(relabelings []yaml.MapSlice, shards int32) []yaml.MapSlice {
|
|
return generateAddressShardingRelabelingRulesWithSourceLabel(relabelings, shards, "__address__")
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateAddressShardingRelabelingRulesIfMissing(relabelings []yaml.MapSlice, shards int32) []yaml.MapSlice {
|
|
for i, relabeling := range relabelings {
|
|
for _, relabelItem := range relabeling {
|
|
if relabelItem.Key == "action" && relabelItem.Value == "hashmod" {
|
|
cg.logger.Debug("found existing hashmod relabeling rule, skipping", "idx", i)
|
|
return relabelings
|
|
}
|
|
}
|
|
}
|
|
return generateAddressShardingRelabelingRules(relabelings, shards)
|
|
}
|
|
|
|
func generateAddressShardingRelabelingRulesForProbes(relabelings []yaml.MapSlice, shards int32) []yaml.MapSlice {
|
|
return generateAddressShardingRelabelingRulesWithSourceLabel(relabelings, shards, "__param_target")
|
|
}
|
|
|
|
func generateAddressShardingRelabelingRulesWithSourceLabel(relabelings []yaml.MapSlice, shards int32, shardLabel string) []yaml.MapSlice {
|
|
return append(relabelings, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{shardLabel}},
|
|
{Key: "target_label", Value: "__tmp_hash"},
|
|
{Key: "modulus", Value: shards},
|
|
{Key: "action", Value: "hashmod"},
|
|
}, yaml.MapSlice{
|
|
{Key: "source_labels", Value: []string{"__tmp_hash"}},
|
|
{Key: "regex", Value: fmt.Sprintf("$(%s)", operator.ShardEnvVar)},
|
|
{Key: "action", Value: "keep"},
|
|
})
|
|
}
|
|
|
|
func generateRelabelConfig(rc []monitoringv1.RelabelConfig) []yaml.MapSlice {
|
|
var cfg []yaml.MapSlice
|
|
|
|
for _, c := range rc {
|
|
relabeling := yaml.MapSlice{}
|
|
|
|
if len(c.SourceLabels) > 0 {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "source_labels", Value: c.SourceLabels})
|
|
}
|
|
|
|
if c.Separator != nil {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "separator", Value: *c.Separator})
|
|
}
|
|
|
|
if c.TargetLabel != "" {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "target_label", Value: c.TargetLabel})
|
|
}
|
|
|
|
if c.Regex != "" {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "regex", Value: c.Regex})
|
|
}
|
|
|
|
if c.Modulus != uint64(0) {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "modulus", Value: c.Modulus})
|
|
}
|
|
|
|
if c.Replacement != nil {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "replacement", Value: *c.Replacement})
|
|
}
|
|
|
|
if c.Action != "" {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "action", Value: strings.ToLower(c.Action)})
|
|
}
|
|
|
|
cfg = append(cfg, relabeling)
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// GetNamespacesFromNamespaceSelector gets a list of namespaces to select based on
|
|
// the given namespace selector, the given default namespace, and whether to ignore namespace selectors.
|
|
func (cg *ConfigGenerator) getNamespacesFromNamespaceSelector(nsel monitoringv1.NamespaceSelector, namespace string) []string {
|
|
if cg.prom.GetCommonPrometheusFields().IgnoreNamespaceSelectors {
|
|
return []string{namespace}
|
|
} else if nsel.Any {
|
|
return []string{}
|
|
} else if len(nsel.MatchNames) == 0 {
|
|
return []string{namespace}
|
|
}
|
|
return nsel.MatchNames
|
|
}
|
|
|
|
type attachMetadataConfig struct {
|
|
MinimumVersion string
|
|
attachMetadata *monitoringv1.AttachMetadata
|
|
}
|
|
|
|
func (a *attachMetadataConfig) node() bool {
|
|
return ptr.Deref(a.attachMetadata.Node, false)
|
|
}
|
|
|
|
// generateK8SSDConfig generates a kubernetes_sd_configs entry.
|
|
func (cg *ConfigGenerator) generateK8SSDConfig(
|
|
namespaceSelector monitoringv1.NamespaceSelector,
|
|
namespace string,
|
|
apiserverConfig *monitoringv1.APIServerConfig,
|
|
store assets.StoreGetter,
|
|
role string,
|
|
attachMetadataConfig *attachMetadataConfig,
|
|
) yaml.MapItem {
|
|
k8sSDConfig := yaml.MapSlice{
|
|
{
|
|
Key: "role",
|
|
Value: role,
|
|
},
|
|
}
|
|
|
|
namespaces := cg.getNamespacesFromNamespaceSelector(namespaceSelector, namespace)
|
|
if len(namespaces) != 0 {
|
|
k8sSDConfig = append(k8sSDConfig, yaml.MapItem{
|
|
Key: "namespaces",
|
|
Value: yaml.MapSlice{
|
|
{
|
|
Key: "names",
|
|
Value: namespaces,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
if apiserverConfig != nil {
|
|
k8sSDConfig = append(k8sSDConfig, yaml.MapItem{
|
|
Key: "api_server", Value: apiserverConfig.Host,
|
|
})
|
|
|
|
k8sSDConfig = cg.addBasicAuthToYaml(k8sSDConfig, store, apiserverConfig.BasicAuth)
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if apiserverConfig.BearerToken != "" {
|
|
cg.logger.Warn("'bearerToken' is deprecated, use 'authorization' instead.")
|
|
k8sSDConfig = append(k8sSDConfig, yaml.MapItem{Key: "bearer_token", Value: apiserverConfig.BearerToken})
|
|
}
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if apiserverConfig.BearerTokenFile != "" {
|
|
cg.logger.Debug("'bearerTokenFile' is deprecated, use 'authorization' instead.")
|
|
k8sSDConfig = append(k8sSDConfig, yaml.MapItem{Key: "bearer_token_file", Value: apiserverConfig.BearerTokenFile})
|
|
}
|
|
|
|
k8sSDConfig = cg.addAuthorizationToYaml(k8sSDConfig, store, apiserverConfig.Authorization)
|
|
|
|
k8sSDConfig = cg.addTLStoYaml(k8sSDConfig, store, apiserverConfig.TLSConfig)
|
|
}
|
|
|
|
if attachMetadataConfig != nil {
|
|
k8sSDConfig = cg.WithMinimumVersion(attachMetadataConfig.MinimumVersion).AppendMapItem(
|
|
k8sSDConfig,
|
|
"attach_metadata",
|
|
yaml.MapSlice{
|
|
{Key: "node", Value: attachMetadataConfig.node()},
|
|
})
|
|
}
|
|
|
|
// Specific configuration generated for DaemonSet mode.
|
|
if cg.daemonSet {
|
|
k8sSDConfig = cg.AppendMapItem(k8sSDConfig, "selectors", []yaml.MapSlice{
|
|
{
|
|
{
|
|
Key: "role",
|
|
Value: "pod",
|
|
},
|
|
{
|
|
Key: "field",
|
|
Value: "spec.nodeName=$(NODE_NAME)",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
return yaml.MapItem{
|
|
Key: "kubernetes_sd_configs",
|
|
Value: []yaml.MapSlice{
|
|
k8sSDConfig,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateAlertmanagerConfig(alerting *monitoringv1.AlertingSpec, apiserverConfig *monitoringv1.APIServerConfig, store assets.StoreGetter) []yaml.MapSlice {
|
|
if alerting == nil || len(alerting.Alertmanagers) == 0 {
|
|
return nil
|
|
}
|
|
|
|
alertmanagerConfigs := make([]yaml.MapSlice, 0, len(alerting.Alertmanagers))
|
|
for i, am := range alerting.Alertmanagers {
|
|
if am.Scheme == "" {
|
|
am.Scheme = "http"
|
|
}
|
|
|
|
if am.PathPrefix == "" {
|
|
am.PathPrefix = "/"
|
|
}
|
|
|
|
cfg := yaml.MapSlice{
|
|
{Key: "path_prefix", Value: am.PathPrefix},
|
|
{Key: "scheme", Value: am.Scheme},
|
|
}
|
|
|
|
if am.Timeout != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "timeout", Value: am.Timeout})
|
|
}
|
|
|
|
if am.EnableHttp2 != nil {
|
|
cfg = cg.WithMinimumVersion("2.35.0").AppendMapItem(cfg, "enable_http2", *am.EnableHttp2)
|
|
}
|
|
|
|
cfg = cg.addTLStoYaml(cfg, store, am.TLSConfig)
|
|
|
|
ns := ptr.Deref(am.Namespace, cg.prom.GetObjectMeta().GetNamespace())
|
|
cfg = append(cfg, cg.generateK8SSDConfig(monitoringv1.NamespaceSelector{}, ns, apiserverConfig, store, cg.endpointRoleFlavor(), nil))
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if am.BearerTokenFile != "" {
|
|
cg.logger.Debug("'bearerTokenFile' is deprecated, use 'authorization' instead.")
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: am.BearerTokenFile})
|
|
}
|
|
|
|
cfg = cg.WithMinimumVersion("2.26.0").addBasicAuthToYaml(cfg, store, am.BasicAuth)
|
|
|
|
cfg = cg.addSafeAuthorizationToYaml(cfg, store, am.Authorization)
|
|
|
|
cfg = cg.WithMinimumVersion("2.48.0").addSigv4ToYaml(cfg, fmt.Sprintf("alertmanager/auth/%d", i), store, am.Sigv4)
|
|
|
|
apiVersionCg := cg.WithMinimumVersion("2.11.0")
|
|
switch am.APIVersion {
|
|
case "v1":
|
|
// API v1 isn't supported anymore by Prometheus v3.
|
|
cfg = apiVersionCg.WithMaximumVersion("2.999.0").AppendMapItem(cfg, "api_version", am.APIVersion)
|
|
case "v2":
|
|
cfg = apiVersionCg.AppendMapItem(cfg, "api_version", am.APIVersion)
|
|
}
|
|
|
|
var relabelings []yaml.MapSlice
|
|
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_service_name"}},
|
|
{Key: "regex", Value: am.Name},
|
|
})
|
|
|
|
if am.Port.StrVal != "" {
|
|
sourceLabels := []string{"__meta_kubernetes_endpoint_port_name"}
|
|
if cg.endpointRoleFlavor() == kubernetesSDRoleEndpointSlice {
|
|
sourceLabels = []string{"__meta_kubernetes_endpointslice_port_name"}
|
|
}
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: sourceLabels},
|
|
{Key: "regex", Value: am.Port.String()},
|
|
})
|
|
} else if am.Port.IntVal != 0 {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "action", Value: "keep"},
|
|
{Key: "source_labels", Value: []string{"__meta_kubernetes_pod_container_port_number"}},
|
|
{Key: "regex", Value: am.Port.String()},
|
|
})
|
|
}
|
|
|
|
if len(am.RelabelConfigs) != 0 {
|
|
relabelings = append(relabelings, generateRelabelConfig(am.RelabelConfigs)...)
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelings})
|
|
|
|
// Append alert_relabel_configs, if any, to the config
|
|
if len(am.AlertRelabelConfigs) > 0 {
|
|
cfg = cg.WithMinimumVersion("2.51.0").AppendMapItem(cfg, "alert_relabel_configs", generateRelabelConfig(am.AlertRelabelConfigs))
|
|
}
|
|
alertmanagerConfigs = append(alertmanagerConfigs, cfg)
|
|
}
|
|
|
|
return alertmanagerConfigs
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateAdditionalScrapeConfigs(
|
|
additionalScrapeConfigs []byte,
|
|
shards int32,
|
|
) ([]yaml.MapSlice, error) {
|
|
var additionalScrapeConfigsYaml []yaml.MapSlice
|
|
err := yaml.Unmarshal(additionalScrapeConfigs, &additionalScrapeConfigsYaml)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unmarshalling additional scrape configs failed: %w", err)
|
|
}
|
|
|
|
// DaemonSet mode doesn't support sharding.
|
|
if cg.daemonSet || shards == 1 {
|
|
return additionalScrapeConfigsYaml, nil
|
|
}
|
|
|
|
var addlScrapeConfigs []yaml.MapSlice
|
|
for _, mapSlice := range additionalScrapeConfigsYaml {
|
|
var addlScrapeConfig yaml.MapSlice
|
|
var relabelings []yaml.MapSlice
|
|
var otherConfigItems []yaml.MapItem
|
|
|
|
for _, mapItem := range mapSlice {
|
|
if mapItem.Key != "relabel_configs" {
|
|
otherConfigItems = append(otherConfigItems, mapItem)
|
|
continue
|
|
}
|
|
values, ok := mapItem.Value.([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("error parsing relabel configs: %w", err)
|
|
}
|
|
for _, value := range values {
|
|
relabeling, ok := value.(yaml.MapSlice)
|
|
if !ok {
|
|
return nil, fmt.Errorf("error parsing relabel config: %w", err)
|
|
}
|
|
relabelings = append(relabelings, relabeling)
|
|
}
|
|
}
|
|
// DaemonSet mode doesn't support sharding.
|
|
if !cg.daemonSet {
|
|
relabelings = cg.generateAddressShardingRelabelingRulesIfMissing(relabelings, shards)
|
|
}
|
|
|
|
addlScrapeConfig = append(addlScrapeConfig, otherConfigItems...)
|
|
addlScrapeConfig = append(addlScrapeConfig, yaml.MapItem{Key: "relabel_configs", Value: relabelings})
|
|
addlScrapeConfigs = append(addlScrapeConfigs, addlScrapeConfig)
|
|
}
|
|
return addlScrapeConfigs, nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateRemoteReadConfig(remoteRead []monitoringv1.RemoteReadSpec, s assets.StoreGetter) yaml.MapItem {
|
|
cfgs := []yaml.MapSlice{}
|
|
|
|
for _, spec := range remoteRead {
|
|
cfg := yaml.MapSlice{
|
|
{Key: "url", Value: spec.URL},
|
|
}
|
|
|
|
if spec.RemoteTimeout != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "remote_timeout", Value: *spec.RemoteTimeout})
|
|
}
|
|
|
|
if len(spec.Headers) > 0 {
|
|
cfg = cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "headers", stringMapToMapSlice(spec.Headers))
|
|
}
|
|
|
|
if spec.Name != "" {
|
|
cfg = cg.WithMinimumVersion("2.15.0").AppendMapItem(cfg, "name", spec.Name)
|
|
}
|
|
|
|
if len(spec.RequiredMatchers) > 0 {
|
|
cfg = append(cfg, yaml.MapItem{Key: "required_matchers", Value: stringMapToMapSlice(spec.RequiredMatchers)})
|
|
}
|
|
|
|
if spec.ReadRecent {
|
|
cfg = append(cfg, yaml.MapItem{Key: "read_recent", Value: spec.ReadRecent})
|
|
}
|
|
|
|
cfg = cg.addBasicAuthToYaml(cfg, s, spec.BasicAuth)
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if spec.BearerToken != "" {
|
|
cg.logger.Warn("'bearerToken' is deprecated, use 'authorization' instead.")
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token", Value: spec.BearerToken})
|
|
}
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if spec.BearerTokenFile != "" {
|
|
cg.logger.Debug("'bearerTokenFile' is deprecated, use 'authorization' instead.")
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: spec.BearerTokenFile})
|
|
}
|
|
|
|
cfg = cg.addOAuth2ToYaml(cfg, s, spec.OAuth2)
|
|
|
|
cfg = cg.addTLStoYaml(cfg, s, spec.TLSConfig)
|
|
|
|
cfg = cg.addAuthorizationToYaml(cfg, s, spec.Authorization)
|
|
|
|
cfg = cg.addProxyConfigtoYaml(cfg, s, spec.ProxyConfig)
|
|
|
|
if spec.FollowRedirects != nil {
|
|
cfg = cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "follow_redirects", spec.FollowRedirects)
|
|
}
|
|
|
|
if spec.FilterExternalLabels != nil {
|
|
cfg = cg.WithMinimumVersion("2.34.0").AppendMapItem(cfg, "filter_external_labels", spec.FilterExternalLabels)
|
|
}
|
|
|
|
cfgs = append(cfgs, cfg)
|
|
}
|
|
|
|
return yaml.MapItem{
|
|
Key: "remote_read",
|
|
Value: cfgs,
|
|
}
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addOAuth2ToYaml(
|
|
cfg yaml.MapSlice,
|
|
store assets.StoreGetter,
|
|
oauth2 *monitoringv1.OAuth2,
|
|
) yaml.MapSlice {
|
|
if oauth2 == nil {
|
|
return cfg
|
|
}
|
|
|
|
clientID, err := store.GetSecretOrConfigMapKey(oauth2.ClientID)
|
|
if err != nil {
|
|
cg.logger.Error("invalid OAuth2 client ID reference", "err", err)
|
|
return cfg
|
|
}
|
|
|
|
clientSecret, err := store.GetSecretKey(oauth2.ClientSecret)
|
|
if err != nil {
|
|
cg.logger.Error("invalid OAuth2 client secret reference", "err", err)
|
|
return cfg
|
|
}
|
|
|
|
oauth2Cfg := yaml.MapSlice{}
|
|
oauth2Cfg = append(oauth2Cfg,
|
|
yaml.MapItem{Key: "client_id", Value: clientID},
|
|
yaml.MapItem{Key: "client_secret", Value: string(clientSecret)},
|
|
yaml.MapItem{Key: "token_url", Value: oauth2.TokenURL},
|
|
)
|
|
|
|
if len(oauth2.Scopes) > 0 {
|
|
oauth2Cfg = append(oauth2Cfg, yaml.MapItem{Key: "scopes", Value: oauth2.Scopes})
|
|
}
|
|
|
|
if len(oauth2.EndpointParams) > 0 {
|
|
oauth2Cfg = append(oauth2Cfg, yaml.MapItem{Key: "endpoint_params", Value: oauth2.EndpointParams})
|
|
}
|
|
|
|
oauth2Cfg = cg.WithMinimumVersion("2.43.0").addProxyConfigtoYaml(oauth2Cfg, store, oauth2.ProxyConfig)
|
|
oauth2Cfg = cg.WithMinimumVersion("2.43.0").addSafeTLStoYaml(oauth2Cfg, store, oauth2.TLSConfig)
|
|
|
|
return cg.WithMinimumVersion("2.27.0").AppendMapItem(cfg, "oauth2", oauth2Cfg)
|
|
}
|
|
|
|
func toProtobufMessageVersion(mv monitoringv1.RemoteWriteMessageVersion) string {
|
|
switch mv {
|
|
case monitoringv1.RemoteWriteMessageVersion1_0:
|
|
return "prometheus.WriteRequest"
|
|
case monitoringv1.RemoteWriteMessageVersion2_0:
|
|
return "io.prometheus.write.v2.Request"
|
|
}
|
|
|
|
// The API should allow only the values listed in the switch/case
|
|
// statement but in case something goes wrong, let's return remote
|
|
// write v1.
|
|
return "prometheus.WriteRequest"
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateRemoteWriteConfig(s assets.StoreGetter) yaml.MapItem {
|
|
var (
|
|
cfgs = []yaml.MapSlice{}
|
|
cpf = cg.prom.GetCommonPrometheusFields()
|
|
)
|
|
|
|
for i, spec := range cpf.RemoteWrite {
|
|
cfg := yaml.MapSlice{
|
|
{Key: "url", Value: spec.URL},
|
|
}
|
|
|
|
if spec.RemoteTimeout != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "remote_timeout", Value: *spec.RemoteTimeout})
|
|
}
|
|
|
|
if len(spec.Headers) > 0 {
|
|
cfg = cg.WithMinimumVersion("2.15.0").AppendMapItem(cfg, "headers", stringMapToMapSlice(spec.Headers))
|
|
}
|
|
|
|
if ptr.Deref(spec.Name, "") != "" {
|
|
cfg = cg.WithMinimumVersion("2.15.0").AppendMapItem(cfg, "name", *spec.Name)
|
|
}
|
|
|
|
if spec.MessageVersion != nil {
|
|
cfg = cg.WithMinimumVersion("2.54.0").AppendMapItem(cfg, "protobuf_message", toProtobufMessageVersion(*spec.MessageVersion))
|
|
}
|
|
|
|
if spec.SendExemplars != nil {
|
|
cfg = cg.WithMinimumVersion("2.27.0").AppendMapItem(cfg, "send_exemplars", spec.SendExemplars)
|
|
}
|
|
|
|
if spec.SendNativeHistograms != nil {
|
|
cfg = cg.WithMinimumVersion("2.40.0").AppendMapItem(cfg, "send_native_histograms", spec.SendNativeHistograms)
|
|
}
|
|
|
|
if spec.WriteRelabelConfigs != nil {
|
|
relabelings := []yaml.MapSlice{}
|
|
for _, c := range spec.WriteRelabelConfigs {
|
|
relabeling := yaml.MapSlice{}
|
|
|
|
if len(c.SourceLabels) > 0 {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "source_labels", Value: c.SourceLabels})
|
|
}
|
|
|
|
if c.Separator != nil {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "separator", Value: *c.Separator})
|
|
}
|
|
|
|
if c.TargetLabel != "" {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "target_label", Value: c.TargetLabel})
|
|
}
|
|
|
|
if c.Regex != "" {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "regex", Value: c.Regex})
|
|
}
|
|
|
|
if c.Modulus != uint64(0) {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "modulus", Value: c.Modulus})
|
|
}
|
|
|
|
if c.Replacement != nil {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "replacement", Value: *c.Replacement})
|
|
}
|
|
|
|
if c.Action != "" {
|
|
relabeling = append(relabeling, yaml.MapItem{Key: "action", Value: strings.ToLower(c.Action)})
|
|
}
|
|
relabelings = append(relabelings, relabeling)
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{Key: "write_relabel_configs", Value: relabelings})
|
|
|
|
}
|
|
|
|
cfg = cg.addBasicAuthToYaml(cfg, s, spec.BasicAuth)
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if spec.BearerToken != "" {
|
|
cg.logger.Warn("'bearerToken' is deprecated, use 'authorization' instead.")
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token", Value: spec.BearerToken})
|
|
}
|
|
|
|
//nolint:staticcheck // Ignore SA1019 this field is marked as deprecated.
|
|
if spec.BearerTokenFile != "" {
|
|
cg.logger.Debug("'bearerTokenFile' is deprecated, use 'authorization' instead.")
|
|
cfg = append(cfg, yaml.MapItem{Key: "bearer_token_file", Value: spec.BearerTokenFile})
|
|
}
|
|
|
|
cfg = cg.addOAuth2ToYaml(cfg, s, spec.OAuth2)
|
|
|
|
cfg = cg.addTLStoYaml(cfg, s, spec.TLSConfig)
|
|
|
|
cfg = cg.addAuthorizationToYaml(cfg, s, spec.Authorization)
|
|
|
|
cfg = cg.addProxyConfigtoYaml(cfg, s, spec.ProxyConfig)
|
|
|
|
cfg = cg.WithMinimumVersion("2.26.0").addSigv4ToYaml(cfg, fmt.Sprintf("remoteWrite/%d", i), s, spec.Sigv4)
|
|
|
|
if spec.AzureAD != nil {
|
|
azureAd := yaml.MapSlice{}
|
|
|
|
if spec.AzureAD.ManagedIdentity != nil {
|
|
azureAd = append(azureAd,
|
|
yaml.MapItem{Key: "managed_identity", Value: yaml.MapSlice{
|
|
{Key: "client_id", Value: spec.AzureAD.ManagedIdentity.ClientID},
|
|
}},
|
|
)
|
|
}
|
|
|
|
if spec.AzureAD.OAuth != nil {
|
|
b, err := s.GetSecretKey(spec.AzureAD.OAuth.ClientSecret)
|
|
if err != nil {
|
|
cg.logger.Error("invalid Azure OAuth client secret", "err", err)
|
|
} else {
|
|
azureAd = cg.WithMinimumVersion("2.48.0").AppendMapItem(azureAd, "oauth", yaml.MapSlice{
|
|
{Key: "client_id", Value: spec.AzureAD.OAuth.ClientID},
|
|
{Key: "client_secret", Value: string(b)},
|
|
{Key: "tenant_id", Value: spec.AzureAD.OAuth.TenantID},
|
|
})
|
|
}
|
|
}
|
|
|
|
if spec.AzureAD.SDK != nil {
|
|
azureAd = cg.WithMinimumVersion("2.52.0").AppendMapItem(azureAd, "sdk", yaml.MapSlice{
|
|
{Key: "tenant_id", Value: ptr.Deref(spec.AzureAD.SDK.TenantID, "")},
|
|
})
|
|
}
|
|
|
|
if spec.AzureAD.Cloud != nil {
|
|
azureAd = append(azureAd, yaml.MapItem{Key: "cloud", Value: spec.AzureAD.Cloud})
|
|
}
|
|
|
|
cfg = cg.WithMinimumVersion("2.45.0").AppendMapItem(cfg, "azuread", azureAd)
|
|
}
|
|
|
|
if spec.FollowRedirects != nil {
|
|
cfg = cg.WithMinimumVersion("2.26.0").AppendMapItem(cfg, "follow_redirects", spec.FollowRedirects)
|
|
}
|
|
|
|
if spec.EnableHttp2 != nil {
|
|
cfg = cg.WithMinimumVersion("2.35.0").AppendMapItem(cfg, "enable_http2", *spec.EnableHttp2)
|
|
}
|
|
|
|
if spec.QueueConfig != nil {
|
|
queueConfig := yaml.MapSlice{}
|
|
|
|
if spec.QueueConfig.Capacity != int(0) {
|
|
queueConfig = append(queueConfig, yaml.MapItem{Key: "capacity", Value: spec.QueueConfig.Capacity})
|
|
}
|
|
|
|
if spec.QueueConfig.MinShards != int(0) {
|
|
queueConfig = cg.WithMinimumVersion("2.6.0").AppendMapItem(queueConfig, "min_shards", spec.QueueConfig.MinShards)
|
|
}
|
|
|
|
if spec.QueueConfig.MaxShards != int(0) {
|
|
queueConfig = append(queueConfig, yaml.MapItem{Key: "max_shards", Value: spec.QueueConfig.MaxShards})
|
|
}
|
|
|
|
if spec.QueueConfig.MaxSamplesPerSend != int(0) {
|
|
queueConfig = append(queueConfig, yaml.MapItem{Key: "max_samples_per_send", Value: spec.QueueConfig.MaxSamplesPerSend})
|
|
}
|
|
|
|
if spec.QueueConfig.BatchSendDeadline != nil {
|
|
queueConfig = append(queueConfig, yaml.MapItem{Key: "batch_send_deadline", Value: string(*spec.QueueConfig.BatchSendDeadline)})
|
|
}
|
|
|
|
if spec.QueueConfig.MaxRetries != int(0) {
|
|
queueConfig = cg.WithMaximumVersion("2.11.0").AppendMapItem(queueConfig, "max_retries", spec.QueueConfig.MaxRetries)
|
|
}
|
|
|
|
if spec.QueueConfig.MinBackoff != nil {
|
|
queueConfig = append(queueConfig, yaml.MapItem{Key: "min_backoff", Value: string(*spec.QueueConfig.MinBackoff)})
|
|
}
|
|
|
|
if spec.QueueConfig.MaxBackoff != nil {
|
|
queueConfig = append(queueConfig, yaml.MapItem{Key: "max_backoff", Value: string(*spec.QueueConfig.MaxBackoff)})
|
|
}
|
|
|
|
if spec.QueueConfig.RetryOnRateLimit {
|
|
queueConfig = cg.WithMinimumVersion("2.26.0").AppendMapItem(queueConfig, "retry_on_http_429", spec.QueueConfig.RetryOnRateLimit)
|
|
}
|
|
|
|
if spec.QueueConfig.SampleAgeLimit != nil {
|
|
queueConfig = cg.WithMinimumVersion("2.50.0").AppendMapItem(queueConfig, "sample_age_limit", string(*spec.QueueConfig.SampleAgeLimit))
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{Key: "queue_config", Value: queueConfig})
|
|
}
|
|
|
|
if spec.MetadataConfig != nil {
|
|
metadataConfig := append(yaml.MapSlice{}, yaml.MapItem{Key: "send", Value: spec.MetadataConfig.Send})
|
|
if spec.MetadataConfig.SendInterval != "" {
|
|
metadataConfig = append(metadataConfig, yaml.MapItem{Key: "send_interval", Value: spec.MetadataConfig.SendInterval})
|
|
}
|
|
|
|
cfg = cg.WithMinimumVersion("2.23.0").AppendMapItem(cfg, "metadata_config", metadataConfig)
|
|
}
|
|
|
|
cfgs = append(cfgs, cfg)
|
|
}
|
|
|
|
return yaml.MapItem{
|
|
Key: "remote_write",
|
|
Value: cfgs,
|
|
}
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendScrapeIntervals(slice yaml.MapSlice) yaml.MapSlice {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
slice = append(slice, yaml.MapItem{Key: "scrape_interval", Value: cpf.ScrapeInterval})
|
|
|
|
if cpf.ScrapeTimeout != "" {
|
|
slice = append(slice, yaml.MapItem{
|
|
Key: "scrape_timeout", Value: cpf.ScrapeTimeout,
|
|
})
|
|
}
|
|
|
|
return slice
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendRuntime(slice yaml.MapSlice) yaml.MapSlice {
|
|
runtime := cg.prom.GetCommonPrometheusFields().Runtime
|
|
if runtime == nil {
|
|
return slice
|
|
}
|
|
if !cg.WithMinimumVersion("2.53.0").IsCompatible() {
|
|
cg.Warn("runtime")
|
|
return slice
|
|
}
|
|
|
|
var runtimeSlice yaml.MapSlice
|
|
if runtime.GoGC != nil {
|
|
runtimeSlice = append(runtimeSlice, yaml.MapItem{Key: "gogc", Value: *runtime.GoGC})
|
|
}
|
|
|
|
return cg.AppendMapItem(slice, "runtime", runtimeSlice)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendEvaluationInterval(slice yaml.MapSlice, evaluationInterval monitoringv1.Duration) yaml.MapSlice {
|
|
return append(slice, yaml.MapItem{Key: "evaluation_interval", Value: evaluationInterval})
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendGlobalLimits(slice yaml.MapSlice, limitKey string, limit *uint64, enforcedLimit *uint64) yaml.MapSlice {
|
|
if ptr.Deref(limit, 0) > 0 {
|
|
if ptr.Deref(enforcedLimit, 0) > 0 && *limit > *enforcedLimit {
|
|
cg.logger.Warn(fmt.Sprintf("%q is greater than the enforced limit, using enforced limit", limitKey), "limit", *limit, "enforced_limit", *enforcedLimit)
|
|
return cg.AppendMapItem(slice, limitKey, *enforcedLimit)
|
|
}
|
|
return cg.AppendMapItem(slice, limitKey, *limit)
|
|
}
|
|
|
|
// Use the enforced limit if no global limit is defined to ensure that scrape jobs without an explicit limit inherit the enforced limit value.
|
|
if ptr.Deref(enforcedLimit, 0) > 0 {
|
|
return cg.AppendMapItem(slice, limitKey, *enforcedLimit)
|
|
}
|
|
|
|
return slice
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendScrapeLimits(slice yaml.MapSlice) yaml.MapSlice {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
|
|
if cpf.BodySizeLimit != nil {
|
|
slice = cg.WithMinimumVersion("2.45.0").AppendMapItem(slice, "body_size_limit", cpf.BodySizeLimit)
|
|
} else if cpf.EnforcedBodySizeLimit != "" {
|
|
slice = cg.WithMinimumVersion("2.45.0").AppendMapItem(slice, "body_size_limit", cpf.EnforcedBodySizeLimit)
|
|
}
|
|
|
|
slice = cg.WithMinimumVersion("2.45.0").appendGlobalLimits(slice, "sample_limit", cpf.SampleLimit, cpf.EnforcedSampleLimit)
|
|
slice = cg.WithMinimumVersion("2.45.0").appendGlobalLimits(slice, "target_limit", cpf.TargetLimit, cpf.EnforcedTargetLimit)
|
|
slice = cg.WithMinimumVersion("2.45.0").appendGlobalLimits(slice, "label_limit", cpf.LabelLimit, cpf.EnforcedLabelLimit)
|
|
slice = cg.WithMinimumVersion("2.45.0").appendGlobalLimits(slice, "label_name_length_limit", cpf.LabelNameLengthLimit, cpf.EnforcedLabelNameLengthLimit)
|
|
slice = cg.WithMinimumVersion("2.45.0").appendGlobalLimits(slice, "label_value_length_limit", cpf.LabelValueLengthLimit, cpf.EnforcedLabelValueLengthLimit)
|
|
slice = cg.WithMinimumVersion("2.47.0").appendGlobalLimits(slice, "keep_dropped_targets", cpf.KeepDroppedTargets, cpf.EnforcedKeepDroppedTargets)
|
|
return slice
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendExternalLabels(slice yaml.MapSlice) yaml.MapSlice {
|
|
slice = append(slice, yaml.MapItem{
|
|
Key: "external_labels",
|
|
Value: cg.buildExternalLabels(),
|
|
})
|
|
|
|
return slice
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendRuleQueryOffset(slice yaml.MapSlice, ruleQueryOffset *monitoringv1.Duration) yaml.MapSlice {
|
|
if ruleQueryOffset == nil {
|
|
return slice
|
|
}
|
|
return cg.WithMinimumVersion("2.53.0").AppendMapItem(slice, "rule_query_offset", ruleQueryOffset)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendQueryLogFile(slice yaml.MapSlice, queryLogFile string) yaml.MapSlice {
|
|
if queryLogFile == "" {
|
|
return slice
|
|
}
|
|
|
|
return cg.WithMinimumVersion("2.16.0").AppendMapItem(slice, "query_log_file", queryLogFilePath(queryLogFile))
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendRuleFiles(slice yaml.MapSlice, ruleFiles []string, ruleSelector *metav1.LabelSelector) yaml.MapSlice {
|
|
if ruleSelector != nil {
|
|
ruleFilePaths := []string{}
|
|
for _, name := range ruleFiles {
|
|
ruleFilePaths = append(ruleFilePaths, RulesDir+"/"+name+"/*.yaml")
|
|
}
|
|
slice = append(slice, yaml.MapItem{
|
|
Key: "rule_files",
|
|
Value: ruleFilePaths,
|
|
})
|
|
}
|
|
|
|
return slice
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendServiceMonitorConfigs(
|
|
slices []yaml.MapSlice,
|
|
serviceMonitors map[string]*monitoringv1.ServiceMonitor,
|
|
apiserverConfig *monitoringv1.APIServerConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32) []yaml.MapSlice {
|
|
|
|
for _, identifier := range util.SortedKeys(serviceMonitors) {
|
|
for i, ep := range serviceMonitors[identifier].Spec.Endpoints {
|
|
slices = append(slices,
|
|
cg.WithKeyVals("service_monitor", identifier).generateServiceMonitorConfig(
|
|
serviceMonitors[identifier],
|
|
ep, i,
|
|
apiserverConfig,
|
|
store,
|
|
shards,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
return slices
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendPodMonitorConfigs(
|
|
slices []yaml.MapSlice,
|
|
podMonitors map[string]*monitoringv1.PodMonitor,
|
|
apiserverConfig *monitoringv1.APIServerConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32) []yaml.MapSlice {
|
|
|
|
for _, identifier := range util.SortedKeys(podMonitors) {
|
|
for i, ep := range podMonitors[identifier].Spec.PodMetricsEndpoints {
|
|
slices = append(slices,
|
|
cg.WithKeyVals("pod_monitor", identifier).generatePodMonitorConfig(
|
|
podMonitors[identifier], ep, i,
|
|
apiserverConfig,
|
|
store,
|
|
shards,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
return slices
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendProbeConfigs(
|
|
slices []yaml.MapSlice,
|
|
probes map[string]*monitoringv1.Probe,
|
|
apiserverConfig *monitoringv1.APIServerConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32) []yaml.MapSlice {
|
|
|
|
for _, identifier := range util.SortedKeys(probes) {
|
|
slices = append(slices,
|
|
cg.WithKeyVals("probe", identifier).generateProbeConfig(
|
|
probes[identifier],
|
|
apiserverConfig,
|
|
store,
|
|
shards,
|
|
),
|
|
)
|
|
}
|
|
|
|
return slices
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendAdditionalScrapeConfigs(scrapeConfigs []yaml.MapSlice, additionalScrapeConfigs []byte, shards int32) ([]yaml.MapSlice, error) {
|
|
addlScrapeConfigs, err := cg.generateAdditionalScrapeConfigs(additionalScrapeConfigs, shards)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return append(scrapeConfigs, addlScrapeConfigs...), nil
|
|
}
|
|
|
|
// GenerateAgentConfiguration creates a serialized YAML representation of a Prometheus Agent configuration using the provided resources.
|
|
func (cg *ConfigGenerator) GenerateAgentConfiguration(
|
|
sMons map[string]*monitoringv1.ServiceMonitor,
|
|
pMons map[string]*monitoringv1.PodMonitor,
|
|
probes map[string]*monitoringv1.Probe,
|
|
sCons map[string]*monitoringv1alpha1.ScrapeConfig,
|
|
store *assets.StoreBuilder,
|
|
additionalScrapeConfigs []byte,
|
|
) ([]byte, error) {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
|
|
// validates the value of scrapeTimeout based on scrapeInterval
|
|
if cpf.ScrapeTimeout != "" {
|
|
if err := CompareScrapeTimeoutToScrapeInterval(cpf.ScrapeTimeout, cpf.ScrapeInterval); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
cfg := yaml.MapSlice{}
|
|
|
|
// Global config
|
|
cfg = append(cfg, yaml.MapItem{Key: "global", Value: cg.buildGlobalConfig()})
|
|
|
|
// Runtime config
|
|
cfg = cg.appendRuntime(cfg)
|
|
|
|
// Scrape config
|
|
var (
|
|
scrapeConfigs []yaml.MapSlice
|
|
apiserverConfig = cpf.APIServerConfig
|
|
shards = shardsNumber(cg.prom)
|
|
)
|
|
|
|
scrapeConfigs = cg.appendPodMonitorConfigs(scrapeConfigs, pMons, apiserverConfig, store, shards)
|
|
scrapeConfigs, err := cg.appendAdditionalScrapeConfigs(scrapeConfigs, additionalScrapeConfigs, shards)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate additional scrape configs: %w", err)
|
|
}
|
|
|
|
// Currently, DaemonSet mode doesn't support these.
|
|
if !cg.daemonSet {
|
|
scrapeConfigs = cg.appendServiceMonitorConfigs(scrapeConfigs, sMons, apiserverConfig, store, shards)
|
|
scrapeConfigs = cg.appendProbeConfigs(scrapeConfigs, probes, apiserverConfig, store, shards)
|
|
scrapeConfigs, err = cg.appendScrapeConfigs(scrapeConfigs, sCons, store, shards)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate scrape configs: %w", err)
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "scrape_configs",
|
|
Value: scrapeConfigs,
|
|
})
|
|
|
|
// TSDB
|
|
tsdb := cpf.TSDB
|
|
if tsdb != nil && tsdb.OutOfOrderTimeWindow != nil {
|
|
var storage yaml.MapSlice
|
|
storage = cg.AppendMapItem(storage, "tsdb", yaml.MapSlice{
|
|
{
|
|
Key: "out_of_order_time_window",
|
|
Value: *tsdb.OutOfOrderTimeWindow,
|
|
},
|
|
})
|
|
cfg = cg.WithMinimumVersion("2.54.0").AppendMapItem(cfg, "storage", storage)
|
|
}
|
|
|
|
// Remote write config
|
|
s := store.ForNamespace(cg.prom.GetObjectMeta().GetNamespace())
|
|
if len(cpf.RemoteWrite) > 0 {
|
|
cfg = append(cfg, cg.generateRemoteWriteConfig(s))
|
|
}
|
|
|
|
// OTLP config
|
|
cfg, err = cg.appendOTLPConfig(cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate OTLP configuration: %w", err)
|
|
}
|
|
|
|
cfg, err = cg.appendTracingConfig(cfg, s)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate tracing configuration: %w", err)
|
|
}
|
|
|
|
return yaml.Marshal(cfg)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendScrapeConfigs(
|
|
slices []yaml.MapSlice,
|
|
scrapeConfigs map[string]*monitoringv1alpha1.ScrapeConfig,
|
|
store *assets.StoreBuilder,
|
|
shards int32) ([]yaml.MapSlice, error) {
|
|
|
|
for _, identifier := range util.SortedKeys(scrapeConfigs) {
|
|
cfgGenerator := cg.WithKeyVals("scrapeconfig", identifier)
|
|
scrapeConfig, err := cfgGenerator.generateScrapeConfig(scrapeConfigs[identifier], store.ForNamespace(scrapeConfigs[identifier].GetNamespace()), shards)
|
|
|
|
if err != nil {
|
|
return slices, err
|
|
}
|
|
|
|
slices = append(slices, scrapeConfig)
|
|
}
|
|
|
|
return slices, nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) generateScrapeConfig(
|
|
sc *monitoringv1alpha1.ScrapeConfig,
|
|
s assets.StoreGetter,
|
|
shards int32,
|
|
) (yaml.MapSlice, error) {
|
|
scrapeClass := cg.getScrapeClassOrDefault(sc.Spec.ScrapeClassName)
|
|
|
|
jobName := fmt.Sprintf("scrapeConfig/%s/%s", sc.Namespace, sc.Name)
|
|
|
|
cfg := yaml.MapSlice{
|
|
{
|
|
Key: "job_name",
|
|
Value: jobName,
|
|
},
|
|
}
|
|
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
relabelings := initRelabelings()
|
|
// Add scrape class relabelings if there is any.
|
|
relabelings = append(relabelings, generateRelabelConfig(scrapeClass.Relabelings)...)
|
|
labeler := namespacelabeler.New(cpf.EnforcedNamespaceLabel, cpf.ExcludedFromEnforcement, false)
|
|
|
|
if sc.Spec.JobName != nil {
|
|
relabelings = append(relabelings, yaml.MapSlice{
|
|
{Key: "target_label", Value: "job"},
|
|
{Key: "action", Value: "replace"},
|
|
{Key: "replacement", Value: sc.Spec.JobName},
|
|
})
|
|
}
|
|
|
|
if sc.Spec.HonorTimestamps != nil {
|
|
cfg = cg.AddHonorTimestamps(cfg, sc.Spec.HonorTimestamps)
|
|
}
|
|
|
|
if sc.Spec.TrackTimestampsStaleness != nil {
|
|
cfg = cg.AddTrackTimestampsStaleness(cfg, sc.Spec.TrackTimestampsStaleness)
|
|
}
|
|
|
|
if sc.Spec.HonorLabels != nil {
|
|
cfg = cg.AddHonorLabels(cfg, *sc.Spec.HonorLabels)
|
|
}
|
|
|
|
if sc.Spec.MetricsPath != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metrics_path", Value: *sc.Spec.MetricsPath})
|
|
}
|
|
|
|
if len(sc.Spec.Params) > 0 {
|
|
cfg = append(cfg, yaml.MapItem{Key: "params", Value: stringMapToMapSlice(sc.Spec.Params)})
|
|
}
|
|
|
|
if sc.Spec.EnableCompression != nil {
|
|
cfg = cg.WithMinimumVersion("2.49.0").AppendMapItem(cfg, "enable_compression", *sc.Spec.EnableCompression)
|
|
}
|
|
|
|
if sc.Spec.EnableHTTP2 != nil {
|
|
cfg = cg.WithMinimumVersion("2.35.0").AppendMapItem(cfg, "enable_http2", *sc.Spec.EnableHTTP2)
|
|
}
|
|
|
|
if sc.Spec.ScrapeInterval != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_interval", Value: *sc.Spec.ScrapeInterval})
|
|
}
|
|
|
|
if sc.Spec.ScrapeTimeout != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scrape_timeout", Value: *sc.Spec.ScrapeTimeout})
|
|
}
|
|
|
|
cfg = cg.addScrapeProtocols(cfg, sc.Spec.ScrapeProtocols)
|
|
cfg = cg.addScrapeFallbackProtocol(cfg, sc.Spec.ScrapeFallbackProtocol)
|
|
|
|
if sc.Spec.Scheme != nil {
|
|
cfg = append(cfg, yaml.MapItem{Key: "scheme", Value: strings.ToLower(*sc.Spec.Scheme)})
|
|
}
|
|
|
|
cfg = cg.addProxyConfigtoYaml(cfg, s, sc.Spec.ProxyConfig)
|
|
|
|
cfg = cg.addBasicAuthToYaml(cfg, s, sc.Spec.BasicAuth)
|
|
|
|
cfg = cg.addSafeAuthorizationToYaml(cfg, s, sc.Spec.Authorization)
|
|
|
|
cfg = cg.addOAuth2ToYaml(cfg, s, sc.Spec.OAuth2)
|
|
|
|
cfg = cg.addTLStoYaml(cfg, s, mergeSafeTLSConfigWithScrapeClass(sc.Spec.TLSConfig, scrapeClass))
|
|
|
|
cfg = cg.AddLimitsToYAML(cfg, sampleLimitKey, sc.Spec.SampleLimit, cpf.EnforcedSampleLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, targetLimitKey, sc.Spec.TargetLimit, cpf.EnforcedTargetLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelLimitKey, sc.Spec.LabelLimit, cpf.EnforcedLabelLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelNameLengthLimitKey, sc.Spec.LabelNameLengthLimit, cpf.EnforcedLabelNameLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, labelValueLengthLimitKey, sc.Spec.LabelValueLengthLimit, cpf.EnforcedLabelValueLengthLimit)
|
|
cfg = cg.AddLimitsToYAML(cfg, keepDroppedTargetsKey, sc.Spec.KeepDroppedTargets, cpf.EnforcedKeepDroppedTargets)
|
|
cfg = cg.addNativeHistogramConfig(cfg, sc.Spec.NativeHistogramConfig)
|
|
|
|
if cpf.EnforcedBodySizeLimit != "" {
|
|
cfg = cg.WithMinimumVersion("2.28.0").AppendMapItem(cfg, "body_size_limit", cpf.EnforcedBodySizeLimit)
|
|
}
|
|
|
|
// StaticConfig
|
|
if len(sc.Spec.StaticConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.StaticConfigs))
|
|
for i, config := range sc.Spec.StaticConfigs {
|
|
configs[i] = []yaml.MapItem{
|
|
{
|
|
Key: "targets",
|
|
Value: config.Targets,
|
|
},
|
|
{
|
|
Key: "labels",
|
|
Value: config.Labels,
|
|
},
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "static_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// FileSDConfig
|
|
if len(sc.Spec.FileSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.FileSDConfigs))
|
|
for i, config := range sc.Spec.FileSDConfigs {
|
|
configs[i] = []yaml.MapItem{
|
|
{
|
|
Key: "files",
|
|
Value: config.Files,
|
|
},
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "file_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// HTTPSDConfig
|
|
if len(sc.Spec.HTTPSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.HTTPSDConfigs))
|
|
for i, config := range sc.Spec.HTTPSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "url",
|
|
Value: config.URL,
|
|
})
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "http_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// KubernetesSDConfig
|
|
if len(sc.Spec.KubernetesSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.KubernetesSDConfigs))
|
|
for i, config := range sc.Spec.KubernetesSDConfigs {
|
|
if config.APIServer != nil {
|
|
configs[i] = []yaml.MapItem{
|
|
{
|
|
Key: "api_server",
|
|
Value: config.APIServer,
|
|
},
|
|
}
|
|
}
|
|
|
|
switch config.Role {
|
|
case monitoringv1alpha1.KubernetesRoleEndpointSlice:
|
|
configs[i] = cg.WithMinimumVersion("2.21.0").AppendMapItem(configs[i], "role", strings.ToLower(string(config.Role)))
|
|
default:
|
|
configs[i] = cg.AppendMapItem(configs[i], "role", strings.ToLower(string(config.Role)))
|
|
}
|
|
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.Namespaces != nil {
|
|
namespaces := []yaml.MapItem{
|
|
{
|
|
Key: "names",
|
|
Value: config.Namespaces.Names,
|
|
},
|
|
}
|
|
|
|
if config.Namespaces.IncludeOwnNamespace != nil {
|
|
namespaces = append(namespaces, yaml.MapItem{
|
|
Key: "own_namespace",
|
|
Value: config.Namespaces.IncludeOwnNamespace,
|
|
})
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "namespaces",
|
|
Value: namespaces,
|
|
})
|
|
}
|
|
|
|
if len(config.Selectors) > 0 {
|
|
selectors := make([][]yaml.MapItem, len(config.Selectors))
|
|
for i, s := range config.Selectors {
|
|
selectors[i] = cg.AppendMapItem(selectors[i], "role", strings.ToLower(string(s.Role)))
|
|
|
|
if s.Label != nil {
|
|
selectors[i] = cg.AppendMapItem(selectors[i], "label", *s.Label)
|
|
}
|
|
|
|
if s.Field != nil {
|
|
selectors[i] = cg.AppendMapItem(selectors[i], "field", *s.Field)
|
|
}
|
|
}
|
|
|
|
configs[i] = cg.WithMinimumVersion("2.17.0").AppendMapItem(configs[i], "selectors", selectors)
|
|
}
|
|
|
|
if config.AttachMetadata != nil {
|
|
switch strings.ToLower(string(config.Role)) {
|
|
case "pod":
|
|
configs[i] = cg.WithMinimumVersion("2.35.0").AppendMapItem(configs[i], "attach_metadata", config.AttachMetadata)
|
|
case "endpoints", "endpointslice":
|
|
configs[i] = cg.WithMinimumVersion("2.37.0").AppendMapItem(configs[i], "attach_metadata", config.AttachMetadata)
|
|
default:
|
|
cg.logger.Warn(fmt.Sprintf("ignoring attachMetadata not supported by Prometheus for role: %s", config.Role))
|
|
}
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "kubernetes_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
//ConsulSDConfig
|
|
if len(sc.Spec.ConsulSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.ConsulSDConfigs))
|
|
for i, config := range sc.Spec.ConsulSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "server",
|
|
Value: config.Server,
|
|
})
|
|
|
|
if config.PathPrefix != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "path_prefix",
|
|
Value: config.PathPrefix,
|
|
})
|
|
}
|
|
|
|
if config.TokenRef != nil {
|
|
value, err := s.GetSecretKey(*config.TokenRef)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to read %s secret %s: %w", config.TokenRef.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "token",
|
|
Value: string(value),
|
|
})
|
|
}
|
|
|
|
if config.Datacenter != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "datacenter",
|
|
Value: config.Datacenter,
|
|
})
|
|
}
|
|
|
|
if config.Namespace != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "namespace",
|
|
Value: config.Namespace,
|
|
})
|
|
}
|
|
|
|
if config.Partition != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "partition",
|
|
Value: config.Partition,
|
|
})
|
|
}
|
|
|
|
if config.Scheme != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "scheme",
|
|
Value: strings.ToLower(*config.Scheme),
|
|
})
|
|
}
|
|
|
|
if len(config.Services) > 0 {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "services",
|
|
Value: config.Services,
|
|
})
|
|
}
|
|
|
|
if len(config.Tags) > 0 {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tags",
|
|
Value: config.Tags,
|
|
})
|
|
}
|
|
|
|
if config.TagSeparator != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tag_separator",
|
|
Value: config.TagSeparator,
|
|
})
|
|
}
|
|
|
|
if len(config.NodeMeta) > 0 {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "node_meta",
|
|
Value: stringMapToMapSlice(config.NodeMeta),
|
|
})
|
|
}
|
|
|
|
if config.AllowStale != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "allow_stale",
|
|
Value: config.AllowStale,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHttp2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHttp2,
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "consul_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// DNSSDConfig
|
|
if len(sc.Spec.DNSSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.DNSSDConfigs))
|
|
|
|
compatibilityMatrix := map[monitoringv1alpha1.DNSRecordType]string{
|
|
monitoringv1alpha1.DNSRecordTypeNS: "2.49.0",
|
|
monitoringv1alpha1.DNSRecordTypeMX: "2.38.0",
|
|
}
|
|
|
|
for i, config := range sc.Spec.DNSSDConfigs {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "names",
|
|
Value: config.Names,
|
|
})
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Type != nil {
|
|
typecg := cg
|
|
|
|
if minVersion, found := compatibilityMatrix[*config.Type]; found {
|
|
typecg = typecg.WithMinimumVersion(minVersion)
|
|
}
|
|
|
|
configs[i] = typecg.AppendMapItem(configs[i], "type", config.Type)
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "dns_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// EC2SDConfig
|
|
if len(sc.Spec.EC2SDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.EC2SDConfigs))
|
|
for i, config := range sc.Spec.EC2SDConfigs {
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
if config.Region != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "region",
|
|
Value: config.Region,
|
|
})
|
|
}
|
|
|
|
if config.AccessKey != nil && config.SecretKey != nil {
|
|
|
|
value, err := s.GetSecretKey(*config.AccessKey)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to get %s access key %s: %w", config.AccessKey.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "access_key",
|
|
Value: string(value),
|
|
})
|
|
|
|
value, err = s.GetSecretKey(*config.SecretKey)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to get %s access key %s: %w", config.SecretKey.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "secret_key",
|
|
Value: string(value),
|
|
})
|
|
}
|
|
|
|
if config.RoleARN != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "role_arn",
|
|
Value: config.RoleARN,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.WithMinimumVersion("2.3.0").addFiltersToYaml(configs[i], config.Filters)
|
|
|
|
cgForHTTPClientConfig := cg.WithMinimumVersion("2.41.0")
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = cgForHTTPClientConfig.AppendMapItem(configs[i], "follow_redirects", config.FollowRedirects)
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = cgForHTTPClientConfig.AppendMapItem(configs[i], "enable_http2", config.EnableHTTP2)
|
|
}
|
|
|
|
if config.TLSConfig != nil {
|
|
configs[i] = cgForHTTPClientConfig.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "ec2_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// AzureSDConfig
|
|
if len(sc.Spec.AzureSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.AzureSDConfigs))
|
|
for i, config := range sc.Spec.AzureSDConfigs {
|
|
if config.Environment != nil {
|
|
configs[i] = []yaml.MapItem{
|
|
{
|
|
Key: "environment",
|
|
Value: config.Environment,
|
|
},
|
|
}
|
|
}
|
|
|
|
if config.AuthenticationMethod != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "authentication_method",
|
|
Value: config.AuthenticationMethod,
|
|
})
|
|
}
|
|
|
|
if config.SubscriptionID != "" {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "subscription_id",
|
|
Value: config.SubscriptionID,
|
|
})
|
|
}
|
|
|
|
if config.TenantID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tenant_id",
|
|
Value: config.TenantID,
|
|
})
|
|
}
|
|
|
|
if config.ClientID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "client_id",
|
|
Value: config.ClientID,
|
|
})
|
|
}
|
|
|
|
if config.ClientSecret != nil {
|
|
value, err := s.GetSecretKey(*config.ClientSecret)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to get %s client secret %s: %w", config.ClientSecret.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "client_secret",
|
|
Value: string(value),
|
|
})
|
|
}
|
|
|
|
if config.ResourceGroup != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
|
|
Key: "resource_group",
|
|
Value: config.ResourceGroup,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "azure_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// GCESDConfig
|
|
if len(sc.Spec.GCESDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.GCESDConfigs))
|
|
for i, config := range sc.Spec.GCESDConfigs {
|
|
configs[i] = []yaml.MapItem{
|
|
{
|
|
Key: "project",
|
|
Value: config.Project,
|
|
},
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "zone",
|
|
Value: config.Zone,
|
|
})
|
|
|
|
if config.Filter != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "filter",
|
|
Value: config.Filter,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.TagSeparator != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tag_separator",
|
|
Value: config.TagSeparator,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "gce_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// OpenStackSDConfig
|
|
if len(sc.Spec.OpenStackSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.OpenStackSDConfigs))
|
|
for i, config := range sc.Spec.OpenStackSDConfigs {
|
|
configs[i] = []yaml.MapItem{
|
|
{
|
|
Key: "role",
|
|
Value: strings.ToLower(config.Role),
|
|
},
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "region",
|
|
Value: config.Region,
|
|
})
|
|
|
|
if config.IdentityEndpoint != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "identity_endpoint",
|
|
Value: config.IdentityEndpoint,
|
|
})
|
|
}
|
|
|
|
if config.Username != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "username",
|
|
Value: config.Username,
|
|
})
|
|
}
|
|
|
|
if config.UserID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "userid",
|
|
Value: config.UserID,
|
|
})
|
|
}
|
|
|
|
if config.Password != nil {
|
|
password, err := s.GetSecretKey(*config.Password)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to read %s secret %s: %w", config.Password.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "password",
|
|
Value: string(password),
|
|
})
|
|
}
|
|
|
|
if config.DomainName != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "domain_name",
|
|
Value: config.DomainName,
|
|
})
|
|
}
|
|
|
|
if config.DomainID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "domain_id",
|
|
Value: config.DomainID,
|
|
})
|
|
}
|
|
|
|
if config.ProjectName != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "project_name",
|
|
Value: config.ProjectName,
|
|
})
|
|
}
|
|
|
|
if config.ProjectID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "project_id",
|
|
Value: config.ProjectID,
|
|
})
|
|
}
|
|
|
|
if config.ApplicationCredentialName != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "application_credential_name",
|
|
Value: config.ApplicationCredentialName,
|
|
})
|
|
}
|
|
|
|
if config.ApplicationCredentialID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "application_credential_id",
|
|
Value: config.ApplicationCredentialID,
|
|
})
|
|
}
|
|
|
|
if config.ApplicationCredentialSecret != nil {
|
|
secret, err := s.GetSecretKey(*config.ApplicationCredentialSecret)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to read %s secret %s: %w", config.ApplicationCredentialSecret.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "application_credential_secret",
|
|
Value: string(secret),
|
|
})
|
|
}
|
|
|
|
if config.AllTenants != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "all_tenants",
|
|
Value: config.AllTenants,
|
|
})
|
|
}
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.Availability != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "availability",
|
|
Value: config.Availability,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "openstack_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// DigitalOceanSDConfig
|
|
if len(sc.Spec.DigitalOceanSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.DigitalOceanSDConfigs))
|
|
for i, config := range sc.Spec.DigitalOceanSDConfigs {
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "digitalocean_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// KumaSDConfig
|
|
if len(sc.Spec.KumaSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.KumaSDConfigs))
|
|
for i, config := range sc.Spec.KumaSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "server",
|
|
Value: config.Server,
|
|
})
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FetchTimeout != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "fetch_timeout",
|
|
Value: config.FetchTimeout,
|
|
})
|
|
}
|
|
|
|
if config.ClientID != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "client_id",
|
|
Value: config.ClientID,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "kuma_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// EurekaSDConfig
|
|
if len(sc.Spec.EurekaSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.EurekaSDConfigs))
|
|
for i, config := range sc.Spec.EurekaSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Server != "" {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "server",
|
|
Value: config.Server,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "eureka_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// DockerSDConfig
|
|
if len(sc.Spec.DockerSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.DockerSDConfigs))
|
|
|
|
for i, config := range sc.Spec.DockerSDConfigs {
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addFiltersToYaml(configs[i], config.Filters)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "host",
|
|
Value: config.Host,
|
|
})
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.HostNetworkingHost != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "host_networking_host",
|
|
Value: config.HostNetworkingHost})
|
|
}
|
|
|
|
if config.MatchFirstNetwork != nil {
|
|
// ref: https://github.com/prometheus/prometheus/pull/14654
|
|
configs[i] = cg.WithMinimumVersion("2.54.1").AppendMapItem(configs[i],
|
|
"match_first_network",
|
|
config.MatchFirstNetwork)
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "docker_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// LinodeSDConfig
|
|
if len(sc.Spec.LinodeSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.LinodeSDConfigs))
|
|
|
|
for i, config := range sc.Spec.LinodeSDConfigs {
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.Region != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "region",
|
|
Value: config.Region,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
if config.TagSeparator != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tag_separator",
|
|
Value: config.TagSeparator,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "linode_sd_configs",
|
|
Value: configs,
|
|
})
|
|
|
|
}
|
|
|
|
// HetznerSDConfig
|
|
if len(sc.Spec.HetznerSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.HetznerSDConfigs))
|
|
for i, config := range sc.Spec.HetznerSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "role",
|
|
Value: strings.ToLower(config.Role),
|
|
})
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "hetzner_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// NomadSDConfig
|
|
if len(sc.Spec.NomadSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.NomadSDConfigs))
|
|
for i, config := range sc.Spec.NomadSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "server",
|
|
Value: config.Server,
|
|
})
|
|
|
|
if config.AllowStale != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "allow_stale",
|
|
Value: config.AllowStale,
|
|
})
|
|
}
|
|
|
|
if config.Namespace != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "namespace",
|
|
Value: config.Namespace,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.Region != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "region",
|
|
Value: config.Region,
|
|
})
|
|
}
|
|
|
|
if config.TagSeparator != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tag_separator",
|
|
Value: config.TagSeparator,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "nomad_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// DockerswarmSDConfig
|
|
if len(sc.Spec.DockerSwarmSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.DockerSwarmSDConfigs))
|
|
for i, config := range sc.Spec.DockerSwarmSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
configs[i] = cg.addFiltersToYaml(configs[i], config.Filters)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "host",
|
|
Value: config.Host,
|
|
})
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "role",
|
|
Value: strings.ToLower(config.Role),
|
|
})
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "dockerswarm_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// PuppetDBSDConfig
|
|
if len(sc.Spec.PuppetDBSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.PuppetDBSDConfigs))
|
|
for i, config := range sc.Spec.PuppetDBSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "url",
|
|
Value: config.URL,
|
|
})
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "query",
|
|
Value: config.Query,
|
|
})
|
|
|
|
if config.IncludeParameters != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "include_parameters",
|
|
Value: config.IncludeParameters,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "puppetdb_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
if len(sc.Spec.LightSailSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.LightSailSDConfigs))
|
|
for i, config := range sc.Spec.LightSailSDConfigs {
|
|
configs[i] = cg.addBasicAuthToYaml(configs[i], s, config.BasicAuth)
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, config.Authorization)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
configs[i] = cg.addOAuth2ToYaml(configs[i], s, config.OAuth2)
|
|
|
|
if config.Region != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "region",
|
|
Value: config.Region,
|
|
})
|
|
}
|
|
|
|
if config.Endpoint != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "endpoint",
|
|
Value: config.Endpoint,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
if config.AccessKey != nil && config.SecretKey != nil {
|
|
|
|
value, err := s.GetSecretKey(*config.AccessKey)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to get %s access key %s: %w", config.AccessKey.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "access_key",
|
|
Value: string(value),
|
|
})
|
|
|
|
value, err = s.GetSecretKey(*config.SecretKey)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("failed to get %s access key %s: %w", config.SecretKey.Name, jobName, err)
|
|
}
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "secret_key",
|
|
Value: string(value),
|
|
})
|
|
}
|
|
|
|
if config.RoleARN != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "role_arn",
|
|
Value: config.RoleARN,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
}
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "lightsail_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// OVHCloudSDConfigs
|
|
if len(sc.Spec.OVHCloudSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.OVHCloudSDConfigs))
|
|
for i, config := range sc.Spec.OVHCloudSDConfigs {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "application_key",
|
|
Value: config.ApplicationKey,
|
|
})
|
|
|
|
value, _ := s.GetSecretKey(config.ApplicationSecret)
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "application_secret",
|
|
Value: string(value),
|
|
})
|
|
|
|
key, _ := s.GetSecretKey(config.ConsumerKey)
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "consumer_key",
|
|
Value: string(key),
|
|
})
|
|
|
|
switch config.Service {
|
|
case monitoringv1alpha1.VPS:
|
|
configs[i] = append(configs[i], yaml.MapItem{Key: "service", Value: "vps"})
|
|
case monitoringv1alpha1.DedicatedServer:
|
|
configs[i] = append(configs[i], yaml.MapItem{Key: "service", Value: "dedicated_server"})
|
|
default:
|
|
cg.logger.Warn(fmt.Sprintf("ignoring service not supported by Prometheus: %s", string(config.Service)))
|
|
}
|
|
|
|
if config.Endpoint != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "endpoint",
|
|
Value: *config.Endpoint,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "ovhcloud_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// ScalewaySDConfig
|
|
if len(sc.Spec.ScalewaySDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.ScalewaySDConfigs))
|
|
for i, config := range sc.Spec.ScalewaySDConfigs {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "access_key",
|
|
Value: config.AccessKey,
|
|
})
|
|
|
|
value, _ := s.GetSecretKey(config.SecretKey)
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "secret_key",
|
|
Value: string(value),
|
|
})
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "project_id",
|
|
Value: config.ProjectID,
|
|
})
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "role",
|
|
Value: strings.ToLower(string(config.Role)),
|
|
})
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.ApiURL != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "api_url",
|
|
Value: *config.ApiURL,
|
|
})
|
|
}
|
|
|
|
if config.Zone != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "zone",
|
|
Value: config.Zone,
|
|
})
|
|
}
|
|
|
|
if config.NameFilter != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "name_filter",
|
|
Value: config.NameFilter,
|
|
})
|
|
}
|
|
|
|
if len(config.TagsFilter) > 0 {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "tags_filter",
|
|
Value: config.TagsFilter,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "scaleway_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
// IonosSDConfig
|
|
if len(sc.Spec.IonosSDConfigs) > 0 {
|
|
configs := make([][]yaml.MapItem, len(sc.Spec.IonosSDConfigs))
|
|
for i, config := range sc.Spec.IonosSDConfigs {
|
|
configs[i] = cg.addSafeAuthorizationToYaml(configs[i], s, &config.Authorization)
|
|
configs[i] = cg.addProxyConfigtoYaml(configs[i], s, config.ProxyConfig)
|
|
configs[i] = cg.addSafeTLStoYaml(configs[i], s, config.TLSConfig)
|
|
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "datacenter_id",
|
|
Value: config.DataCenterID,
|
|
})
|
|
|
|
if config.FollowRedirects != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "follow_redirects",
|
|
Value: config.FollowRedirects,
|
|
})
|
|
}
|
|
|
|
if config.EnableHTTP2 != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "enable_http2",
|
|
Value: config.EnableHTTP2,
|
|
})
|
|
}
|
|
|
|
if config.Port != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "port",
|
|
Value: config.Port,
|
|
})
|
|
}
|
|
|
|
if config.RefreshInterval != nil {
|
|
configs[i] = append(configs[i], yaml.MapItem{
|
|
Key: "refresh_interval",
|
|
Value: config.RefreshInterval,
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg = append(cfg, yaml.MapItem{
|
|
Key: "ionos_sd_configs",
|
|
Value: configs,
|
|
})
|
|
}
|
|
|
|
if len(sc.Spec.RelabelConfigs) > 0 {
|
|
relabelings = append(relabelings, generateRelabelConfig(labeler.GetRelabelingConfigs(sc.TypeMeta, sc.ObjectMeta, sc.Spec.RelabelConfigs))...)
|
|
}
|
|
|
|
if shards != 1 {
|
|
relabelings = cg.generateAddressShardingRelabelingRulesIfMissing(relabelings, shards)
|
|
}
|
|
|
|
// No need to check for the length because relabelings should always have
|
|
// at least one item.
|
|
cfg = append(cfg, yaml.MapItem{Key: "relabel_configs", Value: relabelings})
|
|
|
|
metricRelabelings := []monitoringv1.RelabelConfig{}
|
|
metricRelabelings = append(metricRelabelings, scrapeClass.MetricRelabelings...)
|
|
metricRelabelings = append(metricRelabelings, labeler.GetRelabelingConfigs(sc.TypeMeta, sc.ObjectMeta, sc.Spec.MetricRelabelConfigs)...)
|
|
|
|
if len(metricRelabelings) > 0 {
|
|
cfg = append(cfg, yaml.MapItem{Key: "metric_relabel_configs", Value: generateRelabelConfig(metricRelabelings)})
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendOTLPConfig(cfg yaml.MapSlice) (yaml.MapSlice, error) {
|
|
otlpConfig := cg.prom.GetCommonPrometheusFields().OTLP
|
|
nameValidationScheme := cg.prom.GetCommonPrometheusFields().NameValidationScheme
|
|
|
|
if otlpConfig == nil {
|
|
return cfg, nil
|
|
}
|
|
|
|
if cg.version.LT(semver.MustParse("2.55.0")) {
|
|
return cfg, fmt.Errorf("OTLP configuration is only supported from Prometheus version 2.55.0")
|
|
}
|
|
|
|
if ptr.Deref(otlpConfig.TranslationStrategy, "") == monitoringv1.NoUTF8EscapingWithSuffixes && ptr.Deref(nameValidationScheme, "") == monitoringv1.LegacyNameValidationScheme {
|
|
return cfg, fmt.Errorf("nameValidationScheme %q is not compatible with OTLP translation strategy %q", monitoringv1.LegacyNameValidationScheme, monitoringv1.NoUTF8EscapingWithSuffixes)
|
|
}
|
|
|
|
otlp := yaml.MapSlice{}
|
|
|
|
if len(otlpConfig.PromoteResourceAttributes) > 0 {
|
|
otlp = cg.WithMinimumVersion("2.55.0").AppendMapItem(otlp,
|
|
"promote_resource_attributes",
|
|
otlpConfig.PromoteResourceAttributes)
|
|
}
|
|
|
|
if otlpConfig.TranslationStrategy != nil {
|
|
otlp = cg.WithMinimumVersion("3.0.0").AppendMapItem(otlp,
|
|
"translation_strategy",
|
|
otlpConfig.TranslationStrategy)
|
|
}
|
|
|
|
if len(otlp) == 0 {
|
|
return cfg, nil
|
|
}
|
|
|
|
return cg.AppendMapItem(cfg, "otlp", otlp), nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) appendTracingConfig(cfg yaml.MapSlice, s assets.StoreGetter) (yaml.MapSlice, error) {
|
|
tracingConfig := cg.prom.GetCommonPrometheusFields().TracingConfig
|
|
if tracingConfig == nil {
|
|
return cfg, nil
|
|
}
|
|
|
|
var tracing yaml.MapSlice
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "endpoint",
|
|
Value: tracingConfig.Endpoint,
|
|
})
|
|
|
|
if tracingConfig.ClientType != nil {
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "client_type",
|
|
Value: tracingConfig.ClientType,
|
|
})
|
|
}
|
|
|
|
if tracingConfig.SamplingFraction != nil {
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "sampling_fraction",
|
|
Value: tracingConfig.SamplingFraction.AsApproximateFloat64(),
|
|
})
|
|
}
|
|
|
|
if tracingConfig.Insecure != nil {
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "insecure",
|
|
Value: tracingConfig.Insecure,
|
|
})
|
|
}
|
|
|
|
if len(tracingConfig.Headers) > 0 {
|
|
headers := yaml.MapSlice{}
|
|
for key, value := range tracingConfig.Headers {
|
|
headers = append(headers, yaml.MapItem{
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "headers",
|
|
Value: headers,
|
|
})
|
|
}
|
|
|
|
if tracingConfig.Compression != nil {
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "compression",
|
|
Value: tracingConfig.Compression,
|
|
})
|
|
}
|
|
|
|
if tracingConfig.Timeout != nil {
|
|
tracing = append(tracing, yaml.MapItem{
|
|
Key: "timeout",
|
|
Value: tracingConfig.Timeout,
|
|
})
|
|
}
|
|
|
|
tracing = cg.addTLStoYaml(tracing, s, tracingConfig.TLSConfig)
|
|
|
|
return append(
|
|
cfg,
|
|
yaml.MapItem{
|
|
Key: "tracing",
|
|
Value: tracing,
|
|
}), nil
|
|
}
|
|
|
|
func (cg *ConfigGenerator) getScrapeClassOrDefault(name *string) monitoringv1.ScrapeClass {
|
|
if name != nil {
|
|
if scrapeClass, found := cg.scrapeClasses[*name]; found {
|
|
return scrapeClass
|
|
}
|
|
}
|
|
|
|
if cg.defaultScrapeClassName != "" {
|
|
if scrapeClass, found := cg.scrapeClasses[cg.defaultScrapeClassName]; found {
|
|
return scrapeClass
|
|
}
|
|
}
|
|
|
|
return monitoringv1.ScrapeClass{}
|
|
}
|
|
|
|
func getLowerByteSize(v *monitoringv1.ByteSize, cpf *monitoringv1.CommonPrometheusFields) *monitoringv1.ByteSize {
|
|
if isByteSizeEmpty(&cpf.EnforcedBodySizeLimit) {
|
|
return v
|
|
}
|
|
|
|
if isByteSizeEmpty(v) {
|
|
return &cpf.EnforcedBodySizeLimit
|
|
}
|
|
|
|
vBytes, _ := units.ParseBase2Bytes(string(*v))
|
|
pBytes, _ := units.ParseBase2Bytes(string(cpf.EnforcedBodySizeLimit))
|
|
|
|
if vBytes > pBytes {
|
|
return &cpf.EnforcedBodySizeLimit
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
func isByteSizeEmpty(v *monitoringv1.ByteSize) bool {
|
|
return v == nil || *v == ""
|
|
}
|
|
|
|
func (cg *ConfigGenerator) addFiltersToYaml(cfg yaml.MapSlice, filters []monitoringv1alpha1.Filter) yaml.MapSlice {
|
|
if len(filters) == 0 {
|
|
return cfg
|
|
}
|
|
|
|
// Sort the filters by name to generate deterministic config.
|
|
slices.SortStableFunc(filters, func(a, b monitoringv1alpha1.Filter) int {
|
|
return cmp.Compare(a.Name, b.Name)
|
|
})
|
|
|
|
filtersYamlMap := []yaml.MapSlice{}
|
|
for _, filter := range filters {
|
|
filtersYamlMap = append(filtersYamlMap, yaml.MapSlice{
|
|
{
|
|
Key: "name",
|
|
Value: filter.Name,
|
|
},
|
|
{
|
|
Key: "values",
|
|
Value: filter.Values,
|
|
}})
|
|
}
|
|
|
|
return cg.AppendMapItem(cfg, "filters", filtersYamlMap)
|
|
}
|
|
|
|
func (cg *ConfigGenerator) buildGlobalConfig() yaml.MapSlice {
|
|
cpf := cg.prom.GetCommonPrometheusFields()
|
|
cfg := yaml.MapSlice{}
|
|
cfg = cg.appendScrapeIntervals(cfg)
|
|
cfg = cg.addScrapeProtocols(cfg, cg.prom.GetCommonPrometheusFields().ScrapeProtocols)
|
|
cfg = cg.addScrapeFallbackProtocol(cfg, cg.prom.GetCommonPrometheusFields().ScrapeFallbackProtocol)
|
|
cfg = cg.appendExternalLabels(cfg)
|
|
cfg = cg.appendScrapeLimits(cfg)
|
|
if cpf.NameValidationScheme != nil {
|
|
cg.WithMinimumVersion("3.0.0").AppendMapItem(cfg, "metric_name_validation_scheme", *cpf.NameValidationScheme)
|
|
}
|
|
|
|
return cfg
|
|
}
|