prometheus

Форк
0
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

14
package legacymanager
15

16
import (
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

31
const (
32
	configFieldPrefix      = "AUTO_DISCOVERY_"
33
	staticConfigsKey       = "static_configs"
34
	staticConfigsFieldName = configFieldPrefix + staticConfigsKey
35
)
36

37
var (
38
	configNames      = make(map[string]discovery.Config)
39
	configFieldNames = make(map[reflect.Type]string)
40
	configFields     []reflect.StructField
41

42
	configTypesMu sync.Mutex
43
	configTypes   = make(map[reflect.Type]reflect.Type)
44

45
	emptyStructType = reflect.TypeOf(struct{}{})
46
	configsType     = reflect.TypeOf(discovery.Configs{})
47
)
48

49
// RegisterConfig registers the given Config type for YAML marshaling and unmarshaling.
50
func RegisterConfig(config discovery.Config) {
51
	registerConfig(config.Name()+"_sd_configs", reflect.TypeOf(config), config)
52
}
53

54
func 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.
57
	elemTyp := reflect.TypeOf(&targetgroup.Group{})
58
	registerConfig(staticConfigsKey, elemTyp, discovery.StaticConfig{})
59
}
60

61
func registerConfig(yamlKey string, elemType reflect.Type, config discovery.Config) {
62
	name := config.Name()
63
	if _, ok := configNames[name]; ok {
64
		panic(fmt.Sprintf("discovery: Config named %q is already registered", name))
65
	}
66
	configNames[name] = config
67

68
	fieldName := configFieldPrefix + yamlKey // Field must be exported.
69
	configFieldNames[elemType] = fieldName
70

71
	// Insert fields in sorted order.
72
	i := sort.Search(len(configFields), func(k int) bool {
73
		return fieldName < configFields[k].Name
74
	})
75
	configFields = append(configFields, reflect.StructField{}) // Add empty field at end.
76
	copy(configFields[i+1:], configFields[i:])                 // Shift fields to the right.
77
	configFields[i] = reflect.StructField{                     // Write new field in place.
78
		Name: fieldName,
79
		Type: reflect.SliceOf(elemType),
80
		Tag:  reflect.StructTag(`yaml:"` + yamlKey + `,omitempty"`),
81
	}
82
}
83

84
func getConfigType(out reflect.Type) reflect.Type {
85
	configTypesMu.Lock()
86
	defer configTypesMu.Unlock()
87
	if typ, ok := configTypes[out]; ok {
88
		return typ
89
	}
90
	// Initial exported fields map one-to-one.
91
	var fields []reflect.StructField
92
	for i, n := 0, out.NumField(); i < n; i++ {
93
		switch field := out.Field(i); {
94
		case field.PkgPath == "" && field.Type != configsType:
95
			fields = append(fields, field)
96
		default:
97
			fields = append(fields, reflect.StructField{
98
				Name:    "_" + field.Name, // Field must be unexported.
99
				PkgPath: out.PkgPath(),
100
				Type:    emptyStructType,
101
			})
102
		}
103
	}
104
	// Append extra config fields on the end.
105
	fields = append(fields, configFields...)
106
	typ := reflect.StructOf(fields)
107
	configTypes[out] = typ
108
	return typ
109
}
110

111
// UnmarshalYAMLWithInlineConfigs helps implement yaml.Unmarshal for structs
112
// that have a Configs field that should be inlined.
113
func UnmarshalYAMLWithInlineConfigs(out interface{}, unmarshal func(interface{}) error) error {
114
	outVal := reflect.ValueOf(out)
115
	if outVal.Kind() != reflect.Ptr {
116
		return fmt.Errorf("discovery: can only unmarshal into a struct pointer: %T", out)
117
	}
118
	outVal = outVal.Elem()
119
	if outVal.Kind() != reflect.Struct {
120
		return fmt.Errorf("discovery: can only unmarshal into a struct pointer: %T", out)
121
	}
122
	outTyp := outVal.Type()
123

124
	cfgTyp := getConfigType(outTyp)
125
	cfgPtr := reflect.New(cfgTyp)
126
	cfgVal := cfgPtr.Elem()
127

128
	// Copy shared fields (defaults) to dynamic value.
129
	var configs *discovery.Configs
130
	for i, n := 0, outVal.NumField(); i < n; i++ {
131
		if outTyp.Field(i).Type == configsType {
132
			configs = outVal.Field(i).Addr().Interface().(*discovery.Configs)
133
			continue
134
		}
135
		if cfgTyp.Field(i).PkgPath != "" {
136
			continue // Field is unexported: ignore.
137
		}
138
		cfgVal.Field(i).Set(outVal.Field(i))
139
	}
140
	if configs == nil {
141
		return fmt.Errorf("discovery: Configs field not found in type: %T", out)
142
	}
143

144
	// Unmarshal into dynamic value.
145
	if err := unmarshal(cfgPtr.Interface()); err != nil {
146
		return replaceYAMLTypeError(err, cfgTyp, outTyp)
147
	}
148

149
	// Copy shared fields from dynamic value.
150
	for i, n := 0, outVal.NumField(); i < n; i++ {
151
		if cfgTyp.Field(i).PkgPath != "" {
152
			continue // Field is unexported: ignore.
153
		}
154
		outVal.Field(i).Set(cfgVal.Field(i))
155
	}
156

157
	var err error
158
	*configs, err = readConfigs(cfgVal, outVal.NumField())
159
	return err
160
}
161

162
func readConfigs(structVal reflect.Value, startField int) (discovery.Configs, error) {
163
	var (
164
		configs discovery.Configs
165
		targets []*targetgroup.Group
166
	)
167
	for i, n := startField, structVal.NumField(); i < n; i++ {
168
		field := structVal.Field(i)
169
		if field.Kind() != reflect.Slice {
170
			panic("discovery: internal error: field is not a slice")
171
		}
172
		for k := 0; k < field.Len(); k++ {
173
			val := field.Index(k)
174
			if val.IsZero() || (val.Kind() == reflect.Ptr && val.Elem().IsZero()) {
175
				key := configFieldNames[field.Type().Elem()]
176
				key = strings.TrimPrefix(key, configFieldPrefix)
177
				return nil, fmt.Errorf("empty or null section in %s", key)
178
			}
179
			switch c := val.Interface().(type) {
180
			case *targetgroup.Group:
181
				// Add index to the static config target groups for unique identification
182
				// within scrape pool.
183
				c.Source = strconv.Itoa(len(targets))
184
				// Coalesce multiple static configs into a single static config.
185
				targets = append(targets, c)
186
			case discovery.Config:
187
				configs = append(configs, c)
188
			default:
189
				panic("discovery: internal error: slice element is not a Config")
190
			}
191
		}
192
	}
193
	if len(targets) > 0 {
194
		configs = append(configs, discovery.StaticConfig(targets))
195
	}
196
	return configs, nil
197
}
198

199
// MarshalYAMLWithInlineConfigs helps implement yaml.Marshal for structs
200
// that have a Configs field that should be inlined.
201
func MarshalYAMLWithInlineConfigs(in interface{}) (interface{}, error) {
202
	inVal := reflect.ValueOf(in)
203
	for inVal.Kind() == reflect.Ptr {
204
		inVal = inVal.Elem()
205
	}
206
	inTyp := inVal.Type()
207

208
	cfgTyp := getConfigType(inTyp)
209
	cfgPtr := reflect.New(cfgTyp)
210
	cfgVal := cfgPtr.Elem()
211

212
	// Copy shared fields to dynamic value.
213
	var configs *discovery.Configs
214
	for i, n := 0, inTyp.NumField(); i < n; i++ {
215
		if inTyp.Field(i).Type == configsType {
216
			configs = inVal.Field(i).Addr().Interface().(*discovery.Configs)
217
		}
218
		if cfgTyp.Field(i).PkgPath != "" {
219
			continue // Field is unexported: ignore.
220
		}
221
		cfgVal.Field(i).Set(inVal.Field(i))
222
	}
223
	if configs == nil {
224
		return nil, fmt.Errorf("discovery: Configs field not found in type: %T", in)
225
	}
226

227
	if err := writeConfigs(cfgVal, *configs); err != nil {
228
		return nil, err
229
	}
230

231
	return cfgPtr.Interface(), nil
232
}
233

234
func writeConfigs(structVal reflect.Value, configs discovery.Configs) error {
235
	targets := structVal.FieldByName(staticConfigsFieldName).Addr().Interface().(*[]*targetgroup.Group)
236
	for _, c := range configs {
237
		if sc, ok := c.(discovery.StaticConfig); ok {
238
			*targets = append(*targets, sc...)
239
			continue
240
		}
241
		fieldName, ok := configFieldNames[reflect.TypeOf(c)]
242
		if !ok {
243
			return fmt.Errorf("discovery: cannot marshal unregistered Config type: %T", c)
244
		}
245
		field := structVal.FieldByName(fieldName)
246
		field.Set(reflect.Append(field, reflect.ValueOf(c)))
247
	}
248
	return nil
249
}
250

251
func replaceYAMLTypeError(err error, oldTyp, newTyp reflect.Type) error {
252
	var e *yaml.TypeError
253
	if errors.As(err, &e) {
254
		oldStr := oldTyp.String()
255
		newStr := newTyp.String()
256
		for i, s := range e.Errors {
257
			e.Errors[i] = strings.ReplaceAll(s, oldStr, newStr)
258
		}
259
	}
260
	return err
261
}
262

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.