mirror of
				https://github.com/prometheus/prometheus.git
				synced 2025-11-04 02:11:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			262 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2020 The Prometheus 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 legacymanager
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"gopkg.in/yaml.v2"
 | 
						|
 | 
						|
	"github.com/prometheus/prometheus/discovery"
 | 
						|
	"github.com/prometheus/prometheus/discovery/targetgroup"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	configFieldPrefix      = "AUTO_DISCOVERY_"
 | 
						|
	staticConfigsKey       = "static_configs"
 | 
						|
	staticConfigsFieldName = configFieldPrefix + staticConfigsKey
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	configNames      = make(map[string]discovery.Config)
 | 
						|
	configFieldNames = make(map[reflect.Type]string)
 | 
						|
	configFields     []reflect.StructField
 | 
						|
 | 
						|
	configTypesMu sync.Mutex
 | 
						|
	configTypes   = make(map[reflect.Type]reflect.Type)
 | 
						|
 | 
						|
	emptyStructType = reflect.TypeOf(struct{}{})
 | 
						|
	configsType     = reflect.TypeOf(discovery.Configs{})
 | 
						|
)
 | 
						|
 | 
						|
// RegisterConfig registers the given Config type for YAML marshaling and unmarshaling.
 | 
						|
func RegisterConfig(config discovery.Config) {
 | 
						|
	registerConfig(config.Name()+"_sd_configs", reflect.TypeOf(config), config)
 | 
						|
}
 | 
						|
 | 
						|
func init() {
 | 
						|
	// N.B.: static_configs is the only Config type implemented by default.
 | 
						|
	// All other types are registered at init by their implementing packages.
 | 
						|
	elemTyp := reflect.TypeOf(&targetgroup.Group{})
 | 
						|
	registerConfig(staticConfigsKey, elemTyp, discovery.StaticConfig{})
 | 
						|
}
 | 
						|
 | 
						|
func registerConfig(yamlKey string, elemType reflect.Type, config discovery.Config) {
 | 
						|
	name := config.Name()
 | 
						|
	if _, ok := configNames[name]; ok {
 | 
						|
		panic(fmt.Sprintf("discovery: Config named %q is already registered", name))
 | 
						|
	}
 | 
						|
	configNames[name] = config
 | 
						|
 | 
						|
	fieldName := configFieldPrefix + yamlKey // Field must be exported.
 | 
						|
	configFieldNames[elemType] = fieldName
 | 
						|
 | 
						|
	// Insert fields in sorted order.
 | 
						|
	i := sort.Search(len(configFields), func(k int) bool {
 | 
						|
		return fieldName < configFields[k].Name
 | 
						|
	})
 | 
						|
	configFields = append(configFields, reflect.StructField{}) // Add empty field at end.
 | 
						|
	copy(configFields[i+1:], configFields[i:])                 // Shift fields to the right.
 | 
						|
	configFields[i] = reflect.StructField{                     // Write new field in place.
 | 
						|
		Name: fieldName,
 | 
						|
		Type: reflect.SliceOf(elemType),
 | 
						|
		Tag:  reflect.StructTag(`yaml:"` + yamlKey + `,omitempty"`),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func getConfigType(out reflect.Type) reflect.Type {
 | 
						|
	configTypesMu.Lock()
 | 
						|
	defer configTypesMu.Unlock()
 | 
						|
	if typ, ok := configTypes[out]; ok {
 | 
						|
		return typ
 | 
						|
	}
 | 
						|
	// Initial exported fields map one-to-one.
 | 
						|
	var fields []reflect.StructField
 | 
						|
	for i, n := 0, out.NumField(); i < n; i++ {
 | 
						|
		switch field := out.Field(i); {
 | 
						|
		case field.PkgPath == "" && field.Type != configsType:
 | 
						|
			fields = append(fields, field)
 | 
						|
		default:
 | 
						|
			fields = append(fields, reflect.StructField{
 | 
						|
				Name:    "_" + field.Name, // Field must be unexported.
 | 
						|
				PkgPath: out.PkgPath(),
 | 
						|
				Type:    emptyStructType,
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// Append extra config fields on the end.
 | 
						|
	fields = append(fields, configFields...)
 | 
						|
	typ := reflect.StructOf(fields)
 | 
						|
	configTypes[out] = typ
 | 
						|
	return typ
 | 
						|
}
 | 
						|
 | 
						|
// UnmarshalYAMLWithInlineConfigs helps implement yaml.Unmarshal for structs
 | 
						|
// that have a Configs field that should be inlined.
 | 
						|
func UnmarshalYAMLWithInlineConfigs(out interface{}, unmarshal func(interface{}) error) error {
 | 
						|
	outVal := reflect.ValueOf(out)
 | 
						|
	if outVal.Kind() != reflect.Ptr {
 | 
						|
		return fmt.Errorf("discovery: can only unmarshal into a struct pointer: %T", out)
 | 
						|
	}
 | 
						|
	outVal = outVal.Elem()
 | 
						|
	if outVal.Kind() != reflect.Struct {
 | 
						|
		return fmt.Errorf("discovery: can only unmarshal into a struct pointer: %T", out)
 | 
						|
	}
 | 
						|
	outTyp := outVal.Type()
 | 
						|
 | 
						|
	cfgTyp := getConfigType(outTyp)
 | 
						|
	cfgPtr := reflect.New(cfgTyp)
 | 
						|
	cfgVal := cfgPtr.Elem()
 | 
						|
 | 
						|
	// Copy shared fields (defaults) to dynamic value.
 | 
						|
	var configs *discovery.Configs
 | 
						|
	for i, n := 0, outVal.NumField(); i < n; i++ {
 | 
						|
		if outTyp.Field(i).Type == configsType {
 | 
						|
			configs = outVal.Field(i).Addr().Interface().(*discovery.Configs)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if cfgTyp.Field(i).PkgPath != "" {
 | 
						|
			continue // Field is unexported: ignore.
 | 
						|
		}
 | 
						|
		cfgVal.Field(i).Set(outVal.Field(i))
 | 
						|
	}
 | 
						|
	if configs == nil {
 | 
						|
		return fmt.Errorf("discovery: Configs field not found in type: %T", out)
 | 
						|
	}
 | 
						|
 | 
						|
	// Unmarshal into dynamic value.
 | 
						|
	if err := unmarshal(cfgPtr.Interface()); err != nil {
 | 
						|
		return replaceYAMLTypeError(err, cfgTyp, outTyp)
 | 
						|
	}
 | 
						|
 | 
						|
	// Copy shared fields from dynamic value.
 | 
						|
	for i, n := 0, outVal.NumField(); i < n; i++ {
 | 
						|
		if cfgTyp.Field(i).PkgPath != "" {
 | 
						|
			continue // Field is unexported: ignore.
 | 
						|
		}
 | 
						|
		outVal.Field(i).Set(cfgVal.Field(i))
 | 
						|
	}
 | 
						|
 | 
						|
	var err error
 | 
						|
	*configs, err = readConfigs(cfgVal, outVal.NumField())
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func readConfigs(structVal reflect.Value, startField int) (discovery.Configs, error) {
 | 
						|
	var (
 | 
						|
		configs discovery.Configs
 | 
						|
		targets []*targetgroup.Group
 | 
						|
	)
 | 
						|
	for i, n := startField, structVal.NumField(); i < n; i++ {
 | 
						|
		field := structVal.Field(i)
 | 
						|
		if field.Kind() != reflect.Slice {
 | 
						|
			panic("discovery: internal error: field is not a slice")
 | 
						|
		}
 | 
						|
		for k := 0; k < field.Len(); k++ {
 | 
						|
			val := field.Index(k)
 | 
						|
			if val.IsZero() || (val.Kind() == reflect.Ptr && val.Elem().IsZero()) {
 | 
						|
				key := configFieldNames[field.Type().Elem()]
 | 
						|
				key = strings.TrimPrefix(key, configFieldPrefix)
 | 
						|
				return nil, fmt.Errorf("empty or null section in %s", key)
 | 
						|
			}
 | 
						|
			switch c := val.Interface().(type) {
 | 
						|
			case *targetgroup.Group:
 | 
						|
				// Add index to the static config target groups for unique identification
 | 
						|
				// within scrape pool.
 | 
						|
				c.Source = strconv.Itoa(len(targets))
 | 
						|
				// Coalesce multiple static configs into a single static config.
 | 
						|
				targets = append(targets, c)
 | 
						|
			case discovery.Config:
 | 
						|
				configs = append(configs, c)
 | 
						|
			default:
 | 
						|
				panic("discovery: internal error: slice element is not a Config")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if len(targets) > 0 {
 | 
						|
		configs = append(configs, discovery.StaticConfig(targets))
 | 
						|
	}
 | 
						|
	return configs, nil
 | 
						|
}
 | 
						|
 | 
						|
// MarshalYAMLWithInlineConfigs helps implement yaml.Marshal for structs
 | 
						|
// that have a Configs field that should be inlined.
 | 
						|
func MarshalYAMLWithInlineConfigs(in interface{}) (interface{}, error) {
 | 
						|
	inVal := reflect.ValueOf(in)
 | 
						|
	for inVal.Kind() == reflect.Ptr {
 | 
						|
		inVal = inVal.Elem()
 | 
						|
	}
 | 
						|
	inTyp := inVal.Type()
 | 
						|
 | 
						|
	cfgTyp := getConfigType(inTyp)
 | 
						|
	cfgPtr := reflect.New(cfgTyp)
 | 
						|
	cfgVal := cfgPtr.Elem()
 | 
						|
 | 
						|
	// Copy shared fields to dynamic value.
 | 
						|
	var configs *discovery.Configs
 | 
						|
	for i, n := 0, inTyp.NumField(); i < n; i++ {
 | 
						|
		if inTyp.Field(i).Type == configsType {
 | 
						|
			configs = inVal.Field(i).Addr().Interface().(*discovery.Configs)
 | 
						|
		}
 | 
						|
		if cfgTyp.Field(i).PkgPath != "" {
 | 
						|
			continue // Field is unexported: ignore.
 | 
						|
		}
 | 
						|
		cfgVal.Field(i).Set(inVal.Field(i))
 | 
						|
	}
 | 
						|
	if configs == nil {
 | 
						|
		return nil, fmt.Errorf("discovery: Configs field not found in type: %T", in)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := writeConfigs(cfgVal, *configs); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return cfgPtr.Interface(), nil
 | 
						|
}
 | 
						|
 | 
						|
func writeConfigs(structVal reflect.Value, configs discovery.Configs) error {
 | 
						|
	targets := structVal.FieldByName(staticConfigsFieldName).Addr().Interface().(*[]*targetgroup.Group)
 | 
						|
	for _, c := range configs {
 | 
						|
		if sc, ok := c.(discovery.StaticConfig); ok {
 | 
						|
			*targets = append(*targets, sc...)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		fieldName, ok := configFieldNames[reflect.TypeOf(c)]
 | 
						|
		if !ok {
 | 
						|
			return fmt.Errorf("discovery: cannot marshal unregistered Config type: %T", c)
 | 
						|
		}
 | 
						|
		field := structVal.FieldByName(fieldName)
 | 
						|
		field.Set(reflect.Append(field, reflect.ValueOf(c)))
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func replaceYAMLTypeError(err error, oldTyp, newTyp reflect.Type) error {
 | 
						|
	var e *yaml.TypeError
 | 
						|
	if errors.As(err, &e) {
 | 
						|
		oldStr := oldTyp.String()
 | 
						|
		newStr := newTyp.String()
 | 
						|
		for i, s := range e.Errors {
 | 
						|
			e.Errors[i] = strings.ReplaceAll(s, oldStr, newStr)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 |