prometheus
261 строка · 7.9 Кб
1// Copyright 2020 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package legacymanager15
16import (17"errors"18"fmt"19"reflect"20"sort"21"strconv"22"strings"23"sync"24
25"gopkg.in/yaml.v2"26
27"github.com/prometheus/prometheus/discovery"28"github.com/prometheus/prometheus/discovery/targetgroup"29)
30
31const (32configFieldPrefix = "AUTO_DISCOVERY_"33staticConfigsKey = "static_configs"34staticConfigsFieldName = configFieldPrefix + staticConfigsKey35)
36
37var (38configNames = make(map[string]discovery.Config)39configFieldNames = make(map[reflect.Type]string)40configFields []reflect.StructField41
42configTypesMu sync.Mutex43configTypes = make(map[reflect.Type]reflect.Type)44
45emptyStructType = reflect.TypeOf(struct{}{})46configsType = reflect.TypeOf(discovery.Configs{})47)
48
49// RegisterConfig registers the given Config type for YAML marshaling and unmarshaling.
50func RegisterConfig(config discovery.Config) {51registerConfig(config.Name()+"_sd_configs", reflect.TypeOf(config), config)52}
53
54func init() {55// N.B.: static_configs is the only Config type implemented by default.56// All other types are registered at init by their implementing packages.57elemTyp := reflect.TypeOf(&targetgroup.Group{})58registerConfig(staticConfigsKey, elemTyp, discovery.StaticConfig{})59}
60
61func registerConfig(yamlKey string, elemType reflect.Type, config discovery.Config) {62name := config.Name()63if _, ok := configNames[name]; ok {64panic(fmt.Sprintf("discovery: Config named %q is already registered", name))65}66configNames[name] = config67
68fieldName := configFieldPrefix + yamlKey // Field must be exported.69configFieldNames[elemType] = fieldName70
71// Insert fields in sorted order.72i := sort.Search(len(configFields), func(k int) bool {73return fieldName < configFields[k].Name74})75configFields = append(configFields, reflect.StructField{}) // Add empty field at end.76copy(configFields[i+1:], configFields[i:]) // Shift fields to the right.77configFields[i] = reflect.StructField{ // Write new field in place.78Name: fieldName,79Type: reflect.SliceOf(elemType),80Tag: reflect.StructTag(`yaml:"` + yamlKey + `,omitempty"`),81}82}
83
84func getConfigType(out reflect.Type) reflect.Type {85configTypesMu.Lock()86defer configTypesMu.Unlock()87if typ, ok := configTypes[out]; ok {88return typ89}90// Initial exported fields map one-to-one.91var fields []reflect.StructField92for i, n := 0, out.NumField(); i < n; i++ {93switch field := out.Field(i); {94case field.PkgPath == "" && field.Type != configsType:95fields = append(fields, field)96default:97fields = append(fields, reflect.StructField{98Name: "_" + field.Name, // Field must be unexported.99PkgPath: out.PkgPath(),100Type: emptyStructType,101})102}103}104// Append extra config fields on the end.105fields = append(fields, configFields...)106typ := reflect.StructOf(fields)107configTypes[out] = typ108return typ109}
110
111// UnmarshalYAMLWithInlineConfigs helps implement yaml.Unmarshal for structs
112// that have a Configs field that should be inlined.
113func UnmarshalYAMLWithInlineConfigs(out interface{}, unmarshal func(interface{}) error) error {114outVal := reflect.ValueOf(out)115if outVal.Kind() != reflect.Ptr {116return fmt.Errorf("discovery: can only unmarshal into a struct pointer: %T", out)117}118outVal = outVal.Elem()119if outVal.Kind() != reflect.Struct {120return fmt.Errorf("discovery: can only unmarshal into a struct pointer: %T", out)121}122outTyp := outVal.Type()123
124cfgTyp := getConfigType(outTyp)125cfgPtr := reflect.New(cfgTyp)126cfgVal := cfgPtr.Elem()127
128// Copy shared fields (defaults) to dynamic value.129var configs *discovery.Configs130for i, n := 0, outVal.NumField(); i < n; i++ {131if outTyp.Field(i).Type == configsType {132configs = outVal.Field(i).Addr().Interface().(*discovery.Configs)133continue134}135if cfgTyp.Field(i).PkgPath != "" {136continue // Field is unexported: ignore.137}138cfgVal.Field(i).Set(outVal.Field(i))139}140if configs == nil {141return fmt.Errorf("discovery: Configs field not found in type: %T", out)142}143
144// Unmarshal into dynamic value.145if err := unmarshal(cfgPtr.Interface()); err != nil {146return replaceYAMLTypeError(err, cfgTyp, outTyp)147}148
149// Copy shared fields from dynamic value.150for i, n := 0, outVal.NumField(); i < n; i++ {151if cfgTyp.Field(i).PkgPath != "" {152continue // Field is unexported: ignore.153}154outVal.Field(i).Set(cfgVal.Field(i))155}156
157var err error158*configs, err = readConfigs(cfgVal, outVal.NumField())159return err160}
161
162func readConfigs(structVal reflect.Value, startField int) (discovery.Configs, error) {163var (164configs discovery.Configs165targets []*targetgroup.Group166)167for i, n := startField, structVal.NumField(); i < n; i++ {168field := structVal.Field(i)169if field.Kind() != reflect.Slice {170panic("discovery: internal error: field is not a slice")171}172for k := 0; k < field.Len(); k++ {173val := field.Index(k)174if val.IsZero() || (val.Kind() == reflect.Ptr && val.Elem().IsZero()) {175key := configFieldNames[field.Type().Elem()]176key = strings.TrimPrefix(key, configFieldPrefix)177return nil, fmt.Errorf("empty or null section in %s", key)178}179switch c := val.Interface().(type) {180case *targetgroup.Group:181// Add index to the static config target groups for unique identification182// within scrape pool.183c.Source = strconv.Itoa(len(targets))184// Coalesce multiple static configs into a single static config.185targets = append(targets, c)186case discovery.Config:187configs = append(configs, c)188default:189panic("discovery: internal error: slice element is not a Config")190}191}192}193if len(targets) > 0 {194configs = append(configs, discovery.StaticConfig(targets))195}196return configs, nil197}
198
199// MarshalYAMLWithInlineConfigs helps implement yaml.Marshal for structs
200// that have a Configs field that should be inlined.
201func MarshalYAMLWithInlineConfigs(in interface{}) (interface{}, error) {202inVal := reflect.ValueOf(in)203for inVal.Kind() == reflect.Ptr {204inVal = inVal.Elem()205}206inTyp := inVal.Type()207
208cfgTyp := getConfigType(inTyp)209cfgPtr := reflect.New(cfgTyp)210cfgVal := cfgPtr.Elem()211
212// Copy shared fields to dynamic value.213var configs *discovery.Configs214for i, n := 0, inTyp.NumField(); i < n; i++ {215if inTyp.Field(i).Type == configsType {216configs = inVal.Field(i).Addr().Interface().(*discovery.Configs)217}218if cfgTyp.Field(i).PkgPath != "" {219continue // Field is unexported: ignore.220}221cfgVal.Field(i).Set(inVal.Field(i))222}223if configs == nil {224return nil, fmt.Errorf("discovery: Configs field not found in type: %T", in)225}226
227if err := writeConfigs(cfgVal, *configs); err != nil {228return nil, err229}230
231return cfgPtr.Interface(), nil232}
233
234func writeConfigs(structVal reflect.Value, configs discovery.Configs) error {235targets := structVal.FieldByName(staticConfigsFieldName).Addr().Interface().(*[]*targetgroup.Group)236for _, c := range configs {237if sc, ok := c.(discovery.StaticConfig); ok {238*targets = append(*targets, sc...)239continue240}241fieldName, ok := configFieldNames[reflect.TypeOf(c)]242if !ok {243return fmt.Errorf("discovery: cannot marshal unregistered Config type: %T", c)244}245field := structVal.FieldByName(fieldName)246field.Set(reflect.Append(field, reflect.ValueOf(c)))247}248return nil249}
250
251func replaceYAMLTypeError(err error, oldTyp, newTyp reflect.Type) error {252var e *yaml.TypeError253if errors.As(err, &e) {254oldStr := oldTyp.String()255newStr := newTyp.String()256for i, s := range e.Errors {257e.Errors[i] = strings.ReplaceAll(s, oldStr, newStr)258}259}260return err261}
262