Skip to content

Commit 1e83c8d

Browse files
committed
make config imports more testable
1 parent 7ee0aad commit 1e83c8d

File tree

5 files changed

+210
-204
lines changed

5 files changed

+210
-204
lines changed

config/v3/configV3.go

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,17 @@ const (
3636
)
3737

3838
func Unmarshal(config []byte) (*Config, error) {
39+
return unmarshal(config, NewFileLoader())
40+
}
41+
42+
// For testing, allow injection of mock file loader.
43+
func unmarshal(config []byte, fileLoader FileLoader) (*Config, error) {
3944
cfg := &Config{}
4045
err := yaml.Unmarshal(config, cfg)
4146
if err != nil {
42-
return nil, fmt.Errorf("invalid configuration: %v. make sure to use 'single quotes' around strings with special characters (like match patterns or label templates), and make sure to use '-' only for lists (metrics) but not for maps (labels).", err.Error())
47+
return nil, fmt.Errorf("invalid configuration: %v. make sure to use 'single quotes' around strings with special characters (like match patterns or label templates), and make sure to use '-' only for lists (metrics) but not for maps (labels)", err.Error())
4348
}
44-
importedMetrics, err := importMetrics(cfg.Imports)
49+
importedMetrics, err := importMetrics(cfg.Imports, fileLoader)
4550
if err != nil {
4651
return nil, err
4752
}
@@ -154,6 +159,68 @@ type ServerConfig struct {
154159
Key string `yaml:",omitempty"`
155160
}
156161

162+
func importMetrics(importsConfig ImportsConfig, fileLoader FileLoader) (MetricsConfig, error) {
163+
var (
164+
importConfig ImportConfig
165+
result MetricsConfig
166+
err error
167+
files []*ConfigFile
168+
)
169+
for _, importConfig = range importsConfig {
170+
if importConfig.Type != importMetricsType {
171+
continue
172+
}
173+
err = importConfig.validate()
174+
if err != nil {
175+
return nil, err
176+
}
177+
if len(importConfig.Dir) > 0 {
178+
files, err = fileLoader.LoadDir(importConfig.Dir)
179+
} else {
180+
files, err = fileLoader.LoadGlob(importConfig.File)
181+
}
182+
if err != nil {
183+
return nil, err
184+
}
185+
for _, file := range files {
186+
var metricsConfig MetricsConfig
187+
err := yaml.Unmarshal([]byte(file.Contents), &metricsConfig)
188+
if err != nil {
189+
return nil, fmt.Errorf("%v: %v", file.Path, err)
190+
}
191+
for i := range metricsConfig {
192+
applyImportDefaults(&metricsConfig[i], importConfig.Defaults)
193+
result = append(result, metricsConfig[i])
194+
}
195+
}
196+
}
197+
return result, nil
198+
}
199+
200+
func applyImportDefaults(metricConfig *MetricConfig, defaults DefaultConfig) {
201+
for key, value := range defaults.Labels {
202+
if _, exists := metricConfig.Labels[key]; !exists {
203+
if metricConfig.Labels == nil {
204+
metricConfig.Labels = make(map[string]string)
205+
}
206+
metricConfig.Labels[key] = value
207+
}
208+
}
209+
if len(metricConfig.Quantiles) == 0 {
210+
metricConfig.Quantiles = defaults.Quantiles
211+
}
212+
if len(metricConfig.Buckets) == 0 {
213+
metricConfig.Buckets = defaults.Buckets
214+
}
215+
if metricConfig.Retention == 0 {
216+
metricConfig.Retention = defaults.Retention
217+
}
218+
if len(metricConfig.Path) == 0 && len(metricConfig.Paths) == 0 {
219+
metricConfig.Path = defaults.Path
220+
metricConfig.Paths = defaults.Paths
221+
}
222+
}
223+
157224
func (cfg *Config) addDefaults() {
158225
cfg.Global.addDefaults()
159226
cfg.Input.addDefaults()

config/v3/configV3_test.go

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,18 @@ server:
176176
port: 9144
177177
`
178178

179-
const additional_config_file = `
179+
const config_with_imports = `
180180
global:
181181
config_version: 3
182182
input:
183183
type: stdin
184+
imports:
185+
- type: metrics
186+
file: /etc/grok/metrics.d/*.yaml
187+
defaults:
188+
path: /var/log/syslog/*
189+
labels:
190+
logfile: '{{base .logfile}}'
184191
grok_patterns:
185192
- WARN WARN
186193
- ERROR ERROR
@@ -189,22 +196,36 @@ metrics:
189196
name: errors_total
190197
help: Dummy help message.
191198
match: ERROR
192-
imports:
193-
- type: metrics
194-
file: /etc/grok/metrics.d/*.yaml
195-
defaults:
196-
path: /var/log/syslog/*
197-
labels:
198-
logfile: '{{base .logfile}}'
199199
server:
200200
protocol: http
201201
port: 9144
202202
`
203203

204-
//func TestAdditionalConfigFiles(t *testing.T) {
205-
// cfg := loadOrFail(t, additional_config_file)
206-
// fmt.Printf("cfg: %#v\n", cfg.Imports)
207-
//}
204+
const import_1 = `
205+
- type: counter
206+
name: errors_total_1
207+
help: Dummy help message.
208+
match: ERROR
209+
`
210+
211+
const import_2 = `
212+
- type: counter
213+
name: errors_total_2
214+
help: Dummy help message.
215+
match: ERROR
216+
`
217+
218+
type mockLoader struct {
219+
files []*ConfigFile
220+
}
221+
222+
func (f *mockLoader) LoadDir(dir string) ([]*ConfigFile, error) {
223+
return f.files, nil
224+
}
225+
226+
func (f *mockLoader) LoadGlob(globString string) ([]*ConfigFile, error) {
227+
return f.files, nil
228+
}
208229

209230
func TestCounterValidConfig(t *testing.T) {
210231
loadOrFail(t, counter_config)
@@ -365,6 +386,26 @@ func TestEmptyGrokSection(t *testing.T) {
365386
loadOrFail(t, empty_grok_section)
366387
}
367388

389+
func TestConfigWithImports(t *testing.T) {
390+
fileLoader := &mockLoader{
391+
files: []*ConfigFile{
392+
{Path: "file1.yaml", Contents: import_1},
393+
{Path: "file2.yaml", Contents: import_2},
394+
},
395+
}
396+
cfg, err := unmarshal([]byte(config_with_imports), fileLoader)
397+
if err != nil {
398+
t.Fatalf("Failed to read config: %v", err.Error())
399+
}
400+
err = equalsIgnoreIndentation(cfg.String(), config_with_imports)
401+
if err != nil {
402+
t.Fatalf("Expected:\n%v\nActual:\n%v\n%v", config_with_imports, cfg, err)
403+
}
404+
if len(cfg.AllMetrics) != 3 {
405+
t.Fatalf("expected 3 metrics, but found %v", len(cfg.AllMetrics))
406+
}
407+
}
408+
368409
func loadOrFail(t *testing.T, cfgString string) *Config {
369410
cfg, err := Unmarshal([]byte(cfgString))
370411
if err != nil {

config/v3/fileLoader.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2020 The grok_exporter Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v3
16+
17+
import (
18+
"fmt"
19+
"github.com/fstab/grok_exporter/tailer/glob"
20+
"io/ioutil"
21+
"path"
22+
)
23+
24+
type ConfigFile struct {
25+
Path string
26+
Contents string
27+
}
28+
29+
type FileLoader interface {
30+
LoadDir(dir string) ([]*ConfigFile, error)
31+
LoadGlob(globString string) ([]*ConfigFile, error)
32+
}
33+
34+
type fileLoader struct{}
35+
36+
func NewFileLoader() FileLoader {
37+
return &fileLoader{}
38+
}
39+
40+
func (f *fileLoader) LoadDir(dir string) ([]*ConfigFile, error) {
41+
return f.LoadGlob(path.Join(dir, "*"))
42+
}
43+
44+
func (f *fileLoader) LoadGlob(globString string) ([]*ConfigFile, error) {
45+
result := make([]*ConfigFile, 0, 0)
46+
g, err := glob.Parse(globString)
47+
if err != nil {
48+
return nil, err
49+
}
50+
fileInfos, err := ioutil.ReadDir(g.Dir())
51+
if err != nil {
52+
return nil, err
53+
}
54+
for _, fileInfo := range fileInfos {
55+
filePath := path.Join(g.Dir(), fileInfo.Name())
56+
if g.Match(filePath) {
57+
contents, err := ioutil.ReadFile(filePath)
58+
if err != nil {
59+
return nil, err
60+
}
61+
result = append(result, &ConfigFile{
62+
Path: filePath,
63+
Contents: string(contents),
64+
})
65+
}
66+
}
67+
if len(result) == 0 {
68+
return nil, fmt.Errorf("%v: file(s) not found", globString)
69+
}
70+
return result, nil
71+
}

0 commit comments

Comments
 (0)