diff --git a/cfg/config.go b/cfg/config.go index cd21e1ef0..655a2b8cb 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -259,14 +259,13 @@ func Parse(ptr any, values map[string]int) error { return nil } - childs := make([]reflect.Value, 0) + nestedConfigs := make([]reflect.Value, 0) for i := 0; i < t.NumField(); i++ { vField := v.Field(i) tField := t.Field(i) - childTag := tField.Tag.Get("child") - if childTag == trueValue { - childs = append(childs, vField) + if vField.Kind() == reflect.Struct { + nestedConfigs = append(nestedConfigs, vField) continue } @@ -284,8 +283,8 @@ func Parse(ptr any, values map[string]int) error { } } - for _, child := range childs { - if err := ParseChild(v, child, values); err != nil { + for _, nestedConfig := range nestedConfigs { + if err := Parse(nestedConfig.Addr().Interface(), values); err != nil { return err } } @@ -293,39 +292,8 @@ func Parse(ptr any, values map[string]int) error { return nil } -// it isn't just a recursion -// it also captures values with the same name from parent -// i.e. take this config: -// -// { -// "T": 10, -// "Child": { // has `child:true` in a tag -// "T": null -// } -// } -// -// this function will set `config.Child.T = config.T` -// see file.d/cfg/config_test.go:TestHierarchy for an example -func ParseChild(parent reflect.Value, v reflect.Value, values map[string]int) error { - if v.CanAddr() { - for i := 0; i < v.NumField(); i++ { - name := v.Type().Field(i).Name - val := parent.FieldByName(name) - if val.CanAddr() { - v.Field(i).Set(val) - } - } - - err := Parse(v.Addr().Interface(), values) - if err != nil { - return err - } - } - return nil -} - // ParseSlice recursively parses elements of an slice -// calls Parse, not ParseChild (!) +// calls Parse func ParseSlice(v reflect.Value, values map[string]int) error { for i := 0; i < v.Len(); i++ { if err := Parse(v.Index(i).Addr().Interface(), values); err != nil { diff --git a/cfg/config_test.go b/cfg/config_test.go index 90ff8912e..4872f84cd 100644 --- a/cfg/config_test.go +++ b/cfg/config_test.go @@ -61,15 +61,6 @@ type strDataUnit struct { T_ uint } -type hierarchyChild struct { - T string `required:"true"` -} - -type hierarchy struct { - T string `default:"sync"` - Child hierarchyChild `child:"true"` -} - type sliceChild struct { Value string `default:"child"` } @@ -346,15 +337,6 @@ func TestParseFieldSelectorEnding(t *testing.T) { assert.Equal(t, "c.", path[2], "wrong field") } -func TestHierarchy(t *testing.T) { - s := &hierarchy{T: "10"} - err := Parse(s, map[string]int{}) - - assert.Nil(t, err, "shouldn't be an error") - assert.Equal(t, "10", s.T, "wrong value") - assert.Equal(t, "10", s.Child.T, "wrong value") -} - func TestSlice(t *testing.T) { s := &sliceStruct{Value: "parent_value", Childs: []sliceChild{{"child_1"}, {}}} SetDefaultValues(s) @@ -643,3 +625,23 @@ func TestExpression_UnmarshalJSON(t *testing.T) { require.Equal(t, Expression("2"), val.E2) require.Equal(t, Expression("2+2"), val.E3) } + +type parentCfg struct { + Field childCfg +} + +type childCfg struct { + T Duration `default:"5s" parse:"duration"` + T_ time.Duration +} + +func TestParseNested(t *testing.T) { + s := &parentCfg{Field: childCfg{T: "20s"}} + err := SetDefaultValues(s) + require.NoError(t, err) + + err = Parse(s, nil) + require.NoError(t, err, "error not expected") + + require.Equal(t, 20*time.Second, s.Field.T_, "wrong value") +} diff --git a/plugin/README.md b/plugin/README.md index e31c8496d..d780c204b 100755 --- a/plugin/README.md +++ b/plugin/README.md @@ -129,10 +129,11 @@ pipelines: example_k8s_pipeline: input: type: k8s - offsets_file: /data/offsets.yaml - file_config: // customize file plugin + file_config: # customize file plugin persistence_mode: sync read_buffer_size: 2048 + offsets_file: /data/offsets.yaml + watching_dir: /var/log/containers ``` [More details...](plugin/input/k8s/README.md) diff --git a/plugin/action/throttle/throttle.go b/plugin/action/throttle/throttle.go index 381eadc8a..76c95c57d 100644 --- a/plugin/action/throttle/throttle.go +++ b/plugin/action/throttle/throttle.go @@ -92,7 +92,7 @@ type Config struct { // > @3@4@5@6 // > // > It contains redis settings - RedisBackendCfg RedisBackendConfig `json:"redis_backend_config" child:"true"` // * + RedisBackendCfg RedisBackendConfig `json:"redis_backend_config"` // * // > @3@4@5@6 // > diff --git a/plugin/input/README.md b/plugin/input/README.md index 0df508aaa..c84fce069 100755 --- a/plugin/input/README.md +++ b/plugin/input/README.md @@ -128,10 +128,11 @@ pipelines: example_k8s_pipeline: input: type: k8s - offsets_file: /data/offsets.yaml - file_config: // customize file plugin + file_config: # customize file plugin persistence_mode: sync read_buffer_size: 2048 + offsets_file: /data/offsets.yaml + watching_dir: /var/log/containers ``` [More details...](plugin/input/k8s/README.md) diff --git a/plugin/input/http/http.go b/plugin/input/http/http.go index be2459045..cf733ff2c 100644 --- a/plugin/input/http/http.go +++ b/plugin/input/http/http.go @@ -149,7 +149,7 @@ type Config struct { // > Disabled by default. // > See AuthConfig for details. // > You can use 'warn' log level for logging authorizations. - Auth AuthConfig `json:"auth" child:"true"` // * + Auth AuthConfig `json:"auth"` // * // > @3@4@5@6 // > @@ -165,7 +165,7 @@ type Config struct { // > // > CORS config. // > See CORSConfig for details. - CORS CORSConfig `json:"cors" child:"true"` // * + CORS CORSConfig `json:"cors"` // * } type AuthStrategy byte diff --git a/plugin/input/k8s/README.md b/plugin/input/k8s/README.md index 3239b56a6..f3e69d9e3 100755 --- a/plugin/input/k8s/README.md +++ b/plugin/input/k8s/README.md @@ -21,10 +21,11 @@ pipelines: example_k8s_pipeline: input: type: k8s - offsets_file: /data/offsets.yaml - file_config: // customize file plugin + file_config: # customize file plugin persistence_mode: sync read_buffer_size: 2048 + offsets_file: /data/offsets.yaml + watching_dir: /var/log/containers ``` ### Config params @@ -55,12 +56,16 @@ Skips retrieving Kubernetes meta information using Kubernetes API and adds only **`watching_dir`** *`string`* *`default=/var/log/containers`* +DEPRECATED: you must fill `file_config.watching_dir` instead! + Kubernetes dir with container logs. It's like `watching_dir` parameter from [file plugin](/plugin/input/file/README.md) config.
**`offsets_file`** *`string`* *`required`* +DEPRECATED: you must fill `file_config.offsets_file` instead! + The filename to store offsets of processed files. It's like `offsets_file` parameter from [file plugin](/plugin/input/file/README.md) config.
diff --git a/plugin/input/k8s/k8s.go b/plugin/input/k8s/k8s.go index d890c40df..c78e3af04 100644 --- a/plugin/input/k8s/k8s.go +++ b/plugin/input/k8s/k8s.go @@ -35,10 +35,11 @@ pipelines: example_k8s_pipeline: input: type: k8s - offsets_file: /data/offsets.yaml - file_config: // customize file plugin + file_config: # customize file plugin persistence_mode: sync read_buffer_size: 2048 + offsets_file: /data/offsets.yaml + watching_dir: /var/log/containers ``` }*/ @@ -79,19 +80,23 @@ type Config struct { // > @3@4@5@6 // > + // > DEPRECATED: you must fill `file_config.watching_dir` instead! + // > // > Kubernetes dir with container logs. It's like `watching_dir` parameter from [file plugin](/plugin/input/file/README.md) config. WatchingDir string `json:"watching_dir" default:"/var/log/containers"` // * WatchingDir_ string // > @3@4@5@6 // > + // > DEPRECATED: you must fill `file_config.offsets_file` instead! + // > // > The filename to store offsets of processed files. It's like `offsets_file` parameter from [file plugin](/plugin/input/file/README.md) config. OffsetsFile string `json:"offsets_file" required:"true"` // * // > @3@4@5@6 // > // > Under the hood this plugin uses [file plugin](/plugin/input/file/README.md) to collect logs from files. So you can change any [file plugin](/plugin/input/file/README.md) config parameter using `file_config` section. Check out an example. - FileConfig file.Config `json:"file_config" child:"true"` // * + FileConfig file.Config `json:"file_config"` // * } var startCounter atomic.Int32 @@ -127,6 +132,17 @@ func (p *Plugin) Start(config pipeline.AnyConfig, params *pipeline.InputPluginPa p.params = params p.config = config.(*Config) + if p.config.OffsetsFile != "" { + p.logger.Error("Field 'offsets_file' DEPRECATED and will be deleted soon! You must fill 'file_config.offsets_file' instead") + } + if p.config.WatchingDir_ != "" { + p.logger.Error("Field 'watching_dir' DEPRECATED and will be deleted soon! You must fill 'file_config.watching_dir' instead") + } + + // for backward compatibility; will be removed + p.config.FileConfig.OffsetsFile = p.config.OffsetsFile + p.config.FileConfig.WatchingDir = p.config.WatchingDir_ + startCounter := startCounter.Inc() if startCounter == 1 { diff --git a/plugin/input/k8s/k8s_test.go b/plugin/input/k8s/k8s_test.go index 9d6b42e78..3143811d7 100644 --- a/plugin/input/k8s/k8s_test.go +++ b/plugin/input/k8s/k8s_test.go @@ -10,6 +10,7 @@ import ( "github.com/ozontech/file.d/logger" "github.com/ozontech/file.d/pipeline" + "github.com/ozontech/file.d/plugin/input/file" "github.com/ozontech/file.d/test" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -53,7 +54,13 @@ func getPodInfo(item *metaItem, isWhite bool) *corev1.Pod { } func config() *Config { - config := &Config{AllowedPodLabels: []string{"allowed_label"}, OffsetsFile: "offsets.yaml"} + config := &Config{ + AllowedPodLabels: []string{"allowed_label"}, OffsetsFile: "offsets.yaml", + FileConfig: file.Config{ + WatchingDir: "/var/log/containers", + OffsetsFile: "offsets.yaml", + }, + } test.NewConfig(config, map[string]int{"gomaxprocs": 1}) return config } diff --git a/plugin/output/s3/s3.go b/plugin/output/s3/s3.go index 84148db68..ece3913c5 100644 --- a/plugin/output/s3/s3.go +++ b/plugin/output/s3/s3.go @@ -179,7 +179,7 @@ type Config struct { // > @3@4@5@6 // > // > Under the hood this plugin uses /plugin/output/file/ to collect logs. - FileConfig file.Config `json:"file_config" child:"true"` // * + FileConfig file.Config `json:"file_config"` // * // > @3@4@5@6 // >