Repository: banzaicloud/logging-operator
Pull request #647 (2020-12-02 16:25:35)

Xmas refactor 🎄
| Q               | A
| --------------- | ---
| Bug fix?        | no
| New feature?    | no
| API breaks?     | no
| Deprecations?   | no
| Related tickets | -
| License         | Apache 2.0


### What's in this PR?
Small refactorings and simplifications.


### Why?
Make codebase more maintainable.




Patch:
diff --git a/pkg/resources/fluentd/configsecret.go b/pkg/resources/fluentd/configsecret.go
index 4df6ce1a..00a8f9fc 100644
--- a/pkg/resources/fluentd/configsecret.go
+++ b/pkg/resources/fluentd/configsecret.go
@@ -48,13 +48,14 @@ func generateConfig(input fluentdConfig) (string, error) {
 	if err != nil {
 		return "", errors.Wrap(err, "failed to execute template")
 	}
-	outputString := fmt.Sprint(output.String())
-	return outputString, nil
+	return output.String(), nil
 }
 
 func (r *Reconciler) secretConfig() (runtime.Object, reconciler.DesiredState, error) {
 	input := fluentdConfig{
-		RootDir: r.Logging.Spec.FluentdSpec.RootDir,
+		IgnoreSameLogInterval:     r.Logging.Spec.FluentdSpec.IgnoreSameLogInterval,
+		IgnoreRepeatedLogInterval: r.Logging.Spec.FluentdSpec.IgnoreRepeatedLogInterval,
+		RootDir:                   r.Logging.Spec.FluentdSpec.RootDir,
 	}
 
 	if r.Logging.Spec.FluentdSpec.Metrics != nil {
@@ -62,23 +63,16 @@ func (r *Reconciler) secretConfig() (runtime.Object, reconciler.DesiredState, er
 		input.Monitor.Port = r.Logging.Spec.FluentdSpec.Metrics.Port
 		input.Monitor.Path = r.Logging.Spec.FluentdSpec.Metrics.Path
 	}
-	if r.Logging.Spec.FluentdSpec.LogLevel != "" {
-		input.LogLevel = r.Logging.Spec.FluentdSpec.LogLevel
-	} else {
+
+	input.LogLevel = r.Logging.Spec.FluentdSpec.LogLevel
+	if input.LogLevel == "" {
 		input.LogLevel = "info"
 	}
 
-	if r.Logging.Spec.FluentdSpec.Workers > 0 {
-		input.Workers = r.Logging.Spec.FluentdSpec.Workers
-	} else {
+	input.Workers = r.Logging.Spec.FluentdSpec.Workers
+	if input.Workers <= 0 {
 		input.Workers = 1
 	}
-	//if r.Logging.Spec.FluentdSpec.IgnoreSameLogInterval != nil {
-	input.IgnoreSameLogInterval = r.Logging.Spec.FluentdSpec.IgnoreSameLogInterval
-	//}
-	//if r.Logging.Spec.FluentdSpec.IgnoreRepeatedLogInterval != nil {
-	input.IgnoreRepeatedLogInterval = r.Logging.Spec.FluentdSpec.IgnoreRepeatedLogInterval
-	//}
 
 	inputConfig, err := generateConfig(input)
 	if err != nil {
diff --git a/pkg/resources/model/system.go b/pkg/resources/model/system.go
index e9603cfa..daf4c357 100644
--- a/pkg/resources/model/system.go
+++ b/pkg/resources/model/system.go
@@ -19,14 +19,15 @@ import (
 	"strconv"
 
 	"emperror.dev/errors"
+	"github.com/banzaicloud/operator-tools/pkg/secret"
+	"github.com/banzaicloud/operator-tools/pkg/utils"
+	"github.com/go-logr/logr"
+
 	"github.com/banzaicloud/logging-operator/pkg/sdk/api/v1beta1"
 	"github.com/banzaicloud/logging-operator/pkg/sdk/model/common"
 	"github.com/banzaicloud/logging-operator/pkg/sdk/model/input"
 	"github.com/banzaicloud/logging-operator/pkg/sdk/model/types"
 	"github.com/banzaicloud/logging-operator/pkg/sdk/plugins"
-	"github.com/banzaicloud/operator-tools/pkg/secret"
-	"github.com/banzaicloud/operator-tools/pkg/utils"
-	"github.com/go-logr/logr"
 )
 
 func CreateSystem(resources LoggingResources, secrets SecretLoaderFactory, logger logr.Logger) (*types.System, error) {
@@ -58,7 +59,7 @@ func CreateSystem(resources LoggingResources, secrets SecretLoaderFactory, logge
 		return nil, errors.WrapIf(err, "creating root input")
 	}
 
-	router := types.NewRouter("main", map[string]string{
+	router := types.NewRouter("main", types.Params{
 		"metrics": strconv.FormatBool(logging.Spec.FluentdSpec.Metrics != nil),
 	})
 
diff --git a/pkg/sdk/maps/mapstrstr/helpers.go b/pkg/sdk/maps/mapstrstr/helpers.go
new file mode 100644
index 00000000..da0bbfa4
--- /dev/null
+++ b/pkg/sdk/maps/mapstrstr/helpers.go
@@ -0,0 +1,36 @@
+// Copyright © 2020 Banzai Cloud
+//
+// 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 mapstrstr
+
+// Keys returns the keys of a map[string]string
+func Keys(m map[string]string) []string {
+	keys := make([]string, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	return keys
+}
+
+func MergeInto(dst, src map[string]string) map[string]string {
+	if len(src) > 0 {
+		if dst == nil {
+			dst = make(map[string]string)
+		}
+		for k, v := range src {
+			dst[k] = v
+		}
+	}
+	return dst
+}
diff --git a/pkg/sdk/model/filter/concat.go b/pkg/sdk/model/filter/concat.go
index 398c32fd..5a0d09bd 100644
--- a/pkg/sdk/model/filter/concat.go
+++ b/pkg/sdk/model/filter/concat.go
@@ -99,7 +99,7 @@ type Concat struct {
 type _expConcat interface{}
 
 func (c *Concat) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "concat"
+	const pluginType = "concat"
 	concat := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/filter/dedot.go b/pkg/sdk/model/filter/dedot.go
index 21259faf..aeed6078 100644
--- a/pkg/sdk/model/filter/dedot.go
+++ b/pkg/sdk/model/filter/dedot.go
@@ -75,7 +75,7 @@ func NewDedotFilterConfig() *DedotFilterConfig {
 }
 
 func (c *DedotFilterConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "dedot"
+	const pluginType = "dedot"
 	return types.NewFlatDirective(types.PluginMeta{
 		Type:      pluginType,
 		Directive: "filter",
diff --git a/pkg/sdk/model/filter/detect_exceptions.go b/pkg/sdk/model/filter/detect_exceptions.go
index a659c22b..00fb67a1 100644
--- a/pkg/sdk/model/filter/detect_exceptions.go
+++ b/pkg/sdk/model/filter/detect_exceptions.go
@@ -95,7 +95,7 @@ type DetectExceptions struct {
 type _expDetectExceptions interface{}
 
 func (d *DetectExceptions) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "detect_exceptions"
+	const pluginType = "detect_exceptions"
 	detector := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/filter/geoip.go b/pkg/sdk/model/filter/geoip.go
index 631bd503..7e49e642 100644
--- a/pkg/sdk/model/filter/geoip.go
+++ b/pkg/sdk/model/filter/geoip.go
@@ -91,7 +91,7 @@ type GeoIP struct {
 type _expGeoIP interface{}
 
 func (g *GeoIP) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "geoip"
+	const pluginType = "geoip"
 	geoIP := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -105,13 +105,11 @@ func (g *GeoIP) ToDirective(secretLoader secret.SecretLoader, id string) (types.
 	} else {
 		geoIP.Params = params
 	}
-	if len(g.Records) > 0 {
-		for _, record := range g.Records {
-			if meta, err := record.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				geoIP.SubDirectives = append(geoIP.SubDirectives, meta)
-			}
+	for _, record := range g.Records {
+		if meta, err := record.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			geoIP.SubDirectives = append(geoIP.SubDirectives, meta)
 		}
 	}
 	return geoIP, nil
diff --git a/pkg/sdk/model/filter/grep.go b/pkg/sdk/model/filter/grep.go
index e10d7aae..d17a3f71 100644
--- a/pkg/sdk/model/filter/grep.go
+++ b/pkg/sdk/model/filter/grep.go
@@ -240,25 +240,21 @@ func (r *OrSection) ToDirective(secretLoader secret.SecretLoader, id string) (ty
 		},
 	}
 
-	if len(r.Regexp) > 0 {
-		for _, regexp := range r.Regexp {
-			if meta, err := regexp.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				or.SubDirectives = append(or.SubDirectives, meta)
-			}
+	for _, regexp := range r.Regexp {
+		if meta, err := regexp.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			or.SubDirectives = append(or.SubDirectives, meta)
 		}
 	}
-
-	if len(r.Exclude) > 0 {
-		for _, exclude := range r.Exclude {
-			if meta, err := exclude.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				or.SubDirectives = append(or.SubDirectives, meta)
-			}
+	for _, exclude := range r.Exclude {
+		if meta, err := exclude.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			or.SubDirectives = append(or.SubDirectives, meta)
 		}
 	}
+
 	return or, nil
 }
 
@@ -269,30 +265,26 @@ func (r *AndSection) ToDirective(secretLoader secret.SecretLoader, id string) (t
 		},
 	}
 
-	if len(r.Regexp) > 0 {
-		for _, regexp := range r.Regexp {
-			if meta, err := regexp.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				and.SubDirectives = append(and.SubDirectives, meta)
-			}
+	for _, regexp := range r.Regexp {
+		if meta, err := regexp.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			and.SubDirectives = append(and.SubDirectives, meta)
 		}
 	}
-
-	if len(r.Exclude) > 0 {
-		for _, exclude := range r.Exclude {
-			if meta, err := exclude.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				and.SubDirectives = append(and.SubDirectives, meta)
-			}
+	for _, exclude := range r.Exclude {
+		if meta, err := exclude.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			and.SubDirectives = append(and.SubDirectives, meta)
 		}
 	}
+
 	return and, nil
 }
 
 func (g *GrepConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "grep"
+	const pluginType = "grep"
 	grep := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -301,40 +293,32 @@ func (g *GrepConfig) ToDirective(secretLoader secret.SecretLoader, id string) (t
 			Id:        id,
 		},
 	}
-	if len(g.Regexp) > 0 {
-		for _, regexp := range g.Regexp {
-			if meta, err := regexp.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				grep.SubDirectives = append(grep.SubDirectives, meta)
-			}
+	for _, regexp := range g.Regexp {
+		if meta, err := regexp.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			grep.SubDirectives = append(grep.SubDirectives, meta)
 		}
 	}
-	if len(g.Exclude) > 0 {
-		for _, exclude := range g.Exclude {
-			if meta, err := exclude.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				grep.SubDirectives = append(grep.SubDirectives, meta)
-			}
+	for _, exclude := range g.Exclude {
+		if meta, err := exclude.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			grep.SubDirectives = append(grep.SubDirectives, meta)
 		}
 	}
-	if len(g.Or) > 0 {
-		for _, or := range g.Or {
-			if meta, err := or.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				grep.SubDirectives = append(grep.SubDirectives, meta)
-			}
+	for _, or := range g.Or {
+		if meta, err := or.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			grep.SubDirectives = append(grep.SubDirectives, meta)
 		}
 	}
-	if len(g.And) > 0 {
-		for _, and := range g.And {
-			if meta, err := and.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				grep.SubDirectives = append(grep.SubDirectives, meta)
-			}
+	for _, and := range g.And {
+		if meta, err := and.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			grep.SubDirectives = append(grep.SubDirectives, meta)
 		}
 	}
 
diff --git a/pkg/sdk/model/filter/parser.go b/pkg/sdk/model/filter/parser.go
index 2127c557..53e163d7 100644
--- a/pkg/sdk/model/filter/parser.go
+++ b/pkg/sdk/model/filter/parser.go
@@ -220,26 +220,24 @@ func (p *ParseSection) ToDirective(secretLoader secret.SecretLoader, id string)
 			parseSection.Params[key] = v
 		}
 	}
-	if len(section.Patterns) > 0 {
-		for _, parseRule := range section.Patterns {
-			if parseRule.Format != "" && p.Type != "multi_format" {
-				return nil, errors.Errorf("format parameter only works with multi_format type")
-			}
-			if parseRule.Format == "none" && p.Type != "multi_format" {
-				return nil, errors.Errorf("none format type parameter only works with multi_format type")
-			}
-			if meta, err := parseRule.ToPatternDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				parseSection.SubDirectives = append(parseSection.SubDirectives, meta)
-			}
+	for _, parseRule := range section.Patterns {
+		if parseRule.Format != "" && p.Type != "multi_format" {
+			return nil, errors.Errorf("format parameter only works with multi_format type")
+		}
+		if parseRule.Format == "none" && p.Type != "multi_format" {
+			return nil, errors.Errorf("none format type parameter only works with multi_format type")
+		}
+		if meta, err := parseRule.ToPatternDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			parseSection.SubDirectives = append(parseSection.SubDirectives, meta)
 		}
 	}
 	return parseSection, nil
 }
 
 func (p *ParserConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "parser"
+	const pluginType = "parser"
 	parser := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/filter/prometheus.go b/pkg/sdk/model/filter/prometheus.go
index 3ce2cf80..64e7fd1a 100644
--- a/pkg/sdk/model/filter/prometheus.go
+++ b/pkg/sdk/model/filter/prometheus.go
@@ -150,7 +150,7 @@ func (m *MetricSection) ToDirective(secretLoader secret.SecretLoader, id string)
 }
 
 func (p *PrometheusConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "prometheus"
+	const pluginType = "prometheus"
 	prometheus := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -162,13 +162,11 @@ func (p *PrometheusConfig) ToDirective(secretLoader secret.SecretLoader, id stri
 
 	prometheusConfig := p.DeepCopy()
 
-	if len(p.Metrics) > 0 {
-		for _, metrics := range p.Metrics {
-			if meta, err := metrics.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				prometheus.SubDirectives = append(prometheus.SubDirectives, meta)
-			}
+	for _, metrics := range p.Metrics {
+		if meta, err := metrics.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			prometheus.SubDirectives = append(prometheus.SubDirectives, meta)
 		}
 	}
 
diff --git a/pkg/sdk/model/filter/record_modifier.go b/pkg/sdk/model/filter/record_modifier.go
index 6a586789..8f831943 100644
--- a/pkg/sdk/model/filter/record_modifier.go
+++ b/pkg/sdk/model/filter/record_modifier.go
@@ -100,7 +100,7 @@ func (r *Replace) ToDirective(secretLoader secret.SecretLoader, id string) (type
 }
 
 func (r *RecordModifier) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "record_modifier"
+	const pluginType = "record_modifier"
 	recordModifier := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -114,22 +114,18 @@ func (r *RecordModifier) ToDirective(secretLoader secret.SecretLoader, id string
 	} else {
 		recordModifier.Params = params
 	}
-	if len(r.Replaces) > 0 {
-		for _, replace := range r.Replaces {
-			if meta, err := replace.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				recordModifier.SubDirectives = append(recordModifier.SubDirectives, meta)
-			}
+	for _, replace := range r.Replaces {
+		if meta, err := replace.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			recordModifier.SubDirectives = append(recordModifier.SubDirectives, meta)
 		}
 	}
-	if len(r.Records) > 0 {
-		for _, record := range r.Records {
-			if meta, err := record.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				recordModifier.SubDirectives = append(recordModifier.SubDirectives, meta)
-			}
+	for _, record := range r.Records {
+		if meta, err := record.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			recordModifier.SubDirectives = append(recordModifier.SubDirectives, meta)
 		}
 	}
 	return recordModifier, nil
diff --git a/pkg/sdk/model/filter/record_transformer.go b/pkg/sdk/model/filter/record_transformer.go
index f4b35eae..e09d0857 100644
--- a/pkg/sdk/model/filter/record_transformer.go
+++ b/pkg/sdk/model/filter/record_transformer.go
@@ -97,7 +97,7 @@ func (r *Record) ToDirective(secretLoader secret.SecretLoader, id string) (types
 }
 
 func (r *RecordTransformer) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "record_transformer"
+	const pluginType = "record_transformer"
 	recordTransformer := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -111,13 +111,11 @@ func (r *RecordTransformer) ToDirective(secretLoader secret.SecretLoader, id str
 	} else {
 		recordTransformer.Params = params
 	}
-	if len(r.Records) > 0 {
-		for _, record := range r.Records {
-			if meta, err := record.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				recordTransformer.SubDirectives = append(recordTransformer.SubDirectives, meta)
-			}
+	for _, record := range r.Records {
+		if meta, err := record.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			recordTransformer.SubDirectives = append(recordTransformer.SubDirectives, meta)
 		}
 	}
 	return recordTransformer, nil
diff --git a/pkg/sdk/model/filter/stdout.go b/pkg/sdk/model/filter/stdout.go
index 7a4a6aea..9cb7734f 100644
--- a/pkg/sdk/model/filter/stdout.go
+++ b/pkg/sdk/model/filter/stdout.go
@@ -71,7 +71,7 @@ func NewStdOutFilterConfig() *StdOutFilterConfig {
 }
 
 func (c *StdOutFilterConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "stdout"
+	const pluginType = "stdout"
 	return types.NewFlatDirective(types.PluginMeta{
 		Type:      pluginType,
 		Directive: "filter",
diff --git a/pkg/sdk/model/filter/sumologic.go b/pkg/sdk/model/filter/sumologic.go
index dd5bfaf5..72364e96 100644
--- a/pkg/sdk/model/filter/sumologic.go
+++ b/pkg/sdk/model/filter/sumologic.go
@@ -116,7 +116,7 @@ type SumoLogic struct {
 type _expSumologic interface{}
 
 func (s *SumoLogic) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "kubernetes_sumologic"
+	const pluginType = "kubernetes_sumologic"
 	sumologic := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/filter/tagnormaliser.go b/pkg/sdk/model/filter/tagnormaliser.go
index aa4e3add..6dca0306 100644
--- a/pkg/sdk/model/filter/tagnormaliser.go
+++ b/pkg/sdk/model/filter/tagnormaliser.go
@@ -78,7 +78,7 @@ type TagNormaliser struct {
 type _expTagNormaliser interface{}
 
 func (t *TagNormaliser) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "tag_normaliser"
+	const pluginType = "tag_normaliser"
 	return types.NewFlatDirective(types.PluginMeta{
 		Type:      pluginType,
 		Directive: "match",
diff --git a/pkg/sdk/model/filter/throttle.go b/pkg/sdk/model/filter/throttle.go
index b1e607cc..f46d181a 100644
--- a/pkg/sdk/model/filter/throttle.go
+++ b/pkg/sdk/model/filter/throttle.go
@@ -73,7 +73,7 @@ type Throttle struct {
 type _expThrottle interface{}
 
 func (t *Throttle) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "throttle"
+	const pluginType = "throttle"
 	throttle := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/input/forward.go b/pkg/sdk/model/input/forward.go
index 02e8f431..90fd23fc 100644
--- a/pkg/sdk/model/input/forward.go
+++ b/pkg/sdk/model/input/forward.go
@@ -46,7 +46,7 @@ func NewForwardInputConfig() *ForwardInputConfig {
 }
 
 func (f *ForwardInputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "forward"
+	const pluginType = "forward"
 	pluginID := id + "_" + pluginType
 	forward := &types.GenericDirective{
 		PluginMeta: types.PluginMeta{
diff --git a/pkg/sdk/model/input/tail.go b/pkg/sdk/model/input/tail.go
index e6982b0b..5ba2355f 100644
--- a/pkg/sdk/model/input/tail.go
+++ b/pkg/sdk/model/input/tail.go
@@ -30,7 +30,7 @@ func NewTailInputConfig(path string) *TailInputConfig {
 }
 
 func (c *TailInputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "tail"
+	const pluginType = "tail"
 	return types.NewFlatDirective(types.PluginMeta{
 		Type:      pluginType,
 		Directive: "source",
diff --git a/pkg/sdk/model/output/aws_elasticsearch.go b/pkg/sdk/model/output/aws_elasticsearch.go
index e04bbed4..8610248e 100644
--- a/pkg/sdk/model/output/aws_elasticsearch.go
+++ b/pkg/sdk/model/output/aws_elasticsearch.go
@@ -108,7 +108,7 @@ func (o *EndpointCredentials) ToDirective(secretLoader secret.SecretLoader, id s
 }
 
 func (e *AwsElasticsearchOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "aws-elasticsearch-service"
+	const pluginType = "aws-elasticsearch-service"
 	kinesis := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/azurestore.go b/pkg/sdk/model/output/azurestore.go
index 9da642ab..dd8bdac1 100644
--- a/pkg/sdk/model/output/azurestore.go
+++ b/pkg/sdk/model/output/azurestore.go
@@ -63,7 +63,7 @@ type AzureStorage struct {
 }
 
 func (a *AzureStorage) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "azurestorage"
+	const pluginType = "azurestorage"
 	azure := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/cloudwatch.go b/pkg/sdk/model/output/cloudwatch.go
index 46a0bd82..6b3087be 100644
--- a/pkg/sdk/model/output/cloudwatch.go
+++ b/pkg/sdk/model/output/cloudwatch.go
@@ -142,7 +142,7 @@ type CloudWatchOutput struct {
 }
 
 func (c *CloudWatchOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "cloudwatch_logs"
+	const pluginType = "cloudwatch_logs"
 	cloudwatch := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/datadog.go b/pkg/sdk/model/output/datadog.go
index ea3b8547..d54166cb 100644
--- a/pkg/sdk/model/output/datadog.go
+++ b/pkg/sdk/model/output/datadog.go
@@ -84,7 +84,7 @@ type DatadogOutput struct {
 }
 
 func (a *DatadogOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "datadog"
+	const pluginType = "datadog"
 	datadog := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/elasticsearch.go b/pkg/sdk/model/output/elasticsearch.go
index 82d074f9..1a5415ca 100644
--- a/pkg/sdk/model/output/elasticsearch.go
+++ b/pkg/sdk/model/output/elasticsearch.go
@@ -237,7 +237,7 @@ type ElasticsearchOutput struct {
 }
 
 func (e *ElasticsearchOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "elasticsearch"
+	const pluginType = "elasticsearch"
 	elasticsearch := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/file.go b/pkg/sdk/model/output/file.go
index b6c2f986..8d22a971 100644
--- a/pkg/sdk/model/output/file.go
+++ b/pkg/sdk/model/output/file.go
@@ -91,7 +91,7 @@ type FileOutputConfig struct {
 type _expFile interface{}
 
 func (c *FileOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "file"
+	const pluginType = "file"
 	file := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/forward.go b/pkg/sdk/model/output/forward.go
index 8dee973f..bfe90b5d 100644
--- a/pkg/sdk/model/output/forward.go
+++ b/pkg/sdk/model/output/forward.go
@@ -101,7 +101,7 @@ type ForwardOutput struct {
 }
 
 func (f *ForwardOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "forward"
+	const pluginType = "forward"
 	forward := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -129,13 +129,11 @@ func (f *ForwardOutput) ToDirective(secretLoader secret.SecretLoader, id string)
 			forward.SubDirectives = append(forward.SubDirectives, format)
 		}
 	}
-	if len(f.FluentdServers) > 0 {
-		for _, server := range f.FluentdServers {
-			if serv, err := server.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				forward.SubDirectives = append(forward.SubDirectives, serv)
-			}
+	for _, server := range f.FluentdServers {
+		if serv, err := server.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			forward.SubDirectives = append(forward.SubDirectives, serv)
 		}
 	}
 	return forward, nil
diff --git a/pkg/sdk/model/output/gcs.go b/pkg/sdk/model/output/gcs.go
index f522fbdc..ea685441 100644
--- a/pkg/sdk/model/output/gcs.go
+++ b/pkg/sdk/model/output/gcs.go
@@ -77,7 +77,7 @@ type GCSOutput struct {
 }
 
 func (g *GCSOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "gcs"
+	const pluginType = "gcs"
 	gcs := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -105,13 +105,11 @@ func (g *GCSOutput) ToDirective(secretLoader secret.SecretLoader, id string) (ty
 			gcs.SubDirectives = append(gcs.SubDirectives, format)
 		}
 	}
-	if len(g.ObjectMetadata) > 0 {
-		for _, metadata := range g.ObjectMetadata {
-			if meta, err := metadata.ToDirective(secretLoader, ""); err != nil {
-				return nil, err
-			} else {
-				gcs.SubDirectives = append(gcs.SubDirectives, meta)
-			}
+	for _, metadata := range g.ObjectMetadata {
+		if meta, err := metadata.ToDirective(secretLoader, ""); err != nil {
+			return nil, err
+		} else {
+			gcs.SubDirectives = append(gcs.SubDirectives, meta)
 		}
 	}
 	return gcs, nil
diff --git a/pkg/sdk/model/output/http.go b/pkg/sdk/model/output/http.go
index c43f7f7c..54037d00 100644
--- a/pkg/sdk/model/output/http.go
+++ b/pkg/sdk/model/output/http.go
@@ -94,7 +94,7 @@ type HTTPOutputConfig struct {
 }
 
 func (c *HTTPOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "http"
+	const pluginType = "http"
 	http := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/kafka.go b/pkg/sdk/model/output/kafka.go
index d77ab910..b50d03df 100644
--- a/pkg/sdk/model/output/kafka.go
+++ b/pkg/sdk/model/output/kafka.go
@@ -122,7 +122,7 @@ type KafkaOutputConfig struct {
 }
 
 func (e *KafkaOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "kafka2"
+	const pluginType = "kafka2"
 	kafka := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/kinesis_firehose.go b/pkg/sdk/model/output/kinesis_firehose.go
index cb6d6e26..80d376b7 100644
--- a/pkg/sdk/model/output/kinesis_firehose.go
+++ b/pkg/sdk/model/output/kinesis_firehose.go
@@ -131,7 +131,7 @@ func (o *KinesisFirehoseAssumeRoleCredentials) ToDirective(secretLoader secret.S
 }
 
 func (e *KinesisFirehoseOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "kinesis_firehose"
+	const pluginType = "kinesis_firehose"
 	kinesis := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/kinesis_stream.go b/pkg/sdk/model/output/kinesis_stream.go
index aca73169..8668257c 100644
--- a/pkg/sdk/model/output/kinesis_stream.go
+++ b/pkg/sdk/model/output/kinesis_stream.go
@@ -130,7 +130,7 @@ func (o *KinesisStreamAssumeRoleCredentials) ToDirective(secretLoader secret.Sec
 }
 
 func (e *KinesisStreamOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "kinesis_streams"
+	const pluginType = "kinesis_streams"
 	kinesis := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/logdna.go b/pkg/sdk/model/output/logdna.go
index eb8674b1..fdc483cd 100644
--- a/pkg/sdk/model/output/logdna.go
+++ b/pkg/sdk/model/output/logdna.go
@@ -75,7 +75,7 @@ type LogDNAOutput struct {
 type _expLogDNA interface{}
 
 func (l *LogDNAOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "logdna"
+	const pluginType = "logdna"
 	pluginID := id + "_" + pluginType
 	logdna := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
diff --git a/pkg/sdk/model/output/logz.go b/pkg/sdk/model/output/logz.go
index 4e8cbe1d..319493ce 100644
--- a/pkg/sdk/model/output/logz.go
+++ b/pkg/sdk/model/output/logz.go
@@ -17,8 +17,10 @@ package output
 import (
 	"fmt"
 
-	"github.com/banzaicloud/logging-operator/pkg/sdk/model/types"
 	"github.com/banzaicloud/operator-tools/pkg/secret"
+
+	"github.com/banzaicloud/logging-operator/pkg/sdk/maps/mapstrstr"
+	"github.com/banzaicloud/logging-operator/pkg/sdk/model/types"
 )
 
 // +name:"LogZ"
@@ -103,7 +105,7 @@ func (e *Endpoint) ToDirective(secretLoader secret.SecretLoader) (types.Directiv
 
 // ToDirective converts LogZOutput to fluentd configuration.
 func (e *LogZOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "logzio_buffered"
+	const pluginType = "logzio_buffered"
 	logz := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
@@ -142,7 +144,7 @@ func (e *LogZOutput) ToDirective(secretLoader secret.SecretLoader, id string) (t
 	}
 
 	// add endpoint_url to parameters
-	logz.Params.Merge(map[string]string{"endpoint_url": connectionString})
+	logz.Params = mapstrstr.MergeInto(logz.Params, types.Params{"endpoint_url": connectionString})
 
 	// logz.Params = params
 	if e.Buffer != nil {
diff --git a/pkg/sdk/model/output/loki.go b/pkg/sdk/model/output/loki.go
index 5484d196..9e9fd8a5 100644
--- a/pkg/sdk/model/output/loki.go
+++ b/pkg/sdk/model/output/loki.go
@@ -110,7 +110,7 @@ func (r Label) merge(input Label) {
 }
 
 func (l *LokiOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "loki"
+	const pluginType = "loki"
 	loki := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/newrelic.go b/pkg/sdk/model/output/newrelic.go
index 75406b3d..ac197fc4 100644
--- a/pkg/sdk/model/output/newrelic.go
+++ b/pkg/sdk/model/output/newrelic.go
@@ -63,7 +63,7 @@ type NewRelicOutputConfig struct {
 }
 
 func (c *NewRelicOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "newrelic"
+	const pluginType = "newrelic"
 	newrelic := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/null.go b/pkg/sdk/model/output/null.go
index 7745747a..335cb704 100644
--- a/pkg/sdk/model/output/null.go
+++ b/pkg/sdk/model/output/null.go
@@ -40,7 +40,7 @@ func NewNullOutputConfig() *NullOutputConfig {
 }
 
 func (c *NullOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "null"
+	const pluginType = "null"
 	return types.NewFlatDirective(types.PluginMeta{
 		Type:      pluginType,
 		Directive: "match",
diff --git a/pkg/sdk/model/output/oss.go b/pkg/sdk/model/output/oss.go
index ce2b06ae..d45afb97 100644
--- a/pkg/sdk/model/output/oss.go
+++ b/pkg/sdk/model/output/oss.go
@@ -95,7 +95,7 @@ type OSSOutput struct {
 }
 
 func (o *OSSOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "oss"
+	const pluginType = "oss"
 	oss := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/redis.go b/pkg/sdk/model/output/redis.go
index 671daf9e..abb2a23d 100644
--- a/pkg/sdk/model/output/redis.go
+++ b/pkg/sdk/model/output/redis.go
@@ -72,7 +72,7 @@ type RedisOutputConfig struct {
 }
 
 func (c *RedisOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "redis"
+	const pluginType = "redis"
 	redis := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/s3.go b/pkg/sdk/model/output/s3.go
index be1297f2..8e5b10e1 100644
--- a/pkg/sdk/model/output/s3.go
+++ b/pkg/sdk/model/output/s3.go
@@ -203,7 +203,7 @@ type S3SharedCredentials struct {
 }
 
 func (c *S3OutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "s3"
+	const pluginType = "s3"
 	s3 := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/splunk_hec.go b/pkg/sdk/model/output/splunk_hec.go
index 36cb178f..86eea05e 100644
--- a/pkg/sdk/model/output/splunk_hec.go
+++ b/pkg/sdk/model/output/splunk_hec.go
@@ -132,7 +132,7 @@ func (r Fields) ToDirective(secretLoader secret.SecretLoader, id string) (types.
 }
 
 func (c *SplunkHecOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "splunk_hec"
+	const pluginType = "splunk_hec"
 	splunkHec := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/sumologic.go b/pkg/sdk/model/output/sumologic.go
index b1c88be9..e2b12742 100644
--- a/pkg/sdk/model/output/sumologic.go
+++ b/pkg/sdk/model/output/sumologic.go
@@ -112,7 +112,7 @@ type SumologicOutput struct {
 }
 
 func (s *SumologicOutput) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "sumologic"
+	const pluginType = "sumologic"
 	sumologic := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/output/syslog.go b/pkg/sdk/model/output/syslog.go
index 39d0ac25..00998c69 100644
--- a/pkg/sdk/model/output/syslog.go
+++ b/pkg/sdk/model/output/syslog.go
@@ -97,7 +97,7 @@ type SyslogOutputConfig struct {
 type _expSyslog interface{}
 
 func (s *SyslogOutputConfig) ToDirective(secretLoader secret.SecretLoader, id string) (types.Directive, error) {
-	pluginType := "syslog_rfc5424"
+	const pluginType = "syslog_rfc5424"
 	syslog := &types.OutputPlugin{
 		PluginMeta: types.PluginMeta{
 			Type:      pluginType,
diff --git a/pkg/sdk/model/render/fluent.go b/pkg/sdk/model/render/fluent.go
index b689b262..33432096 100644
--- a/pkg/sdk/model/render/fluent.go
+++ b/pkg/sdk/model/render/fluent.go
@@ -17,11 +17,12 @@ package render
 import (
 	"fmt"
 	"io"
+	"sort"
 	"strings"
 
 	"emperror.dev/errors"
-	util "github.com/banzaicloud/operator-tools/pkg/utils"
 
+	"github.com/banzaicloud/logging-operator/pkg/sdk/maps/mapstrstr"
 	"github.com/banzaicloud/logging-operator/pkg/sdk/model/types"
 )
 
@@ -53,15 +54,16 @@ func (f *FluentRender) RenderDirectives(directives []types.Directive, indent int
 		if meta.LogLevel != "" {
 			f.indented(indent+f.Indent, "@log_level %s", meta.LogLevel)
 		}
-		if len(d.GetParams()) > 0 {
-			for _, k := range util.OrderedStringMap(d.GetParams()).Keys() {
-				f.indented(indent+f.Indent, "%s %s", k, d.GetParams()[k])
+		if params := d.GetParams(); len(params) > 0 {
+			keys := mapstrstr.Keys(params)
+			sort.Strings(keys)
+			for _, k := range keys {
+				f.indented(indent+f.Indent, "%s %s", k, params[k])
 			}
 		}
-		if len(d.GetSections()) > 0 {
-			err := f.RenderDirectives(d.GetSections(), indent+f.Indent)
-			if err != nil {
-				return errors.WrapIff(err, "failed to render sections for %s", d.GetPluginMeta().Directive)
+		if sections := d.GetSections(); len(sections) > 0 {
+			if err := f.RenderDirectives(sections, indent+f.Indent); err != nil {
+				return errors.WrapIff(err, "failed to render sections for %s", meta.Directive)
 			}
 		}
 		f.indented(indent, "</%s>", meta.Directive)
diff --git a/pkg/sdk/model/types/builder.go b/pkg/sdk/model/types/builder.go
index fbb7b028..a2a9c465 100644
--- a/pkg/sdk/model/types/builder.go
+++ b/pkg/sdk/model/types/builder.go
@@ -15,7 +15,7 @@
 package types
 
 import (
-	"errors"
+	"emperror.dev/errors"
 )
 
 type SystemBuilder struct {
diff --git a/pkg/sdk/model/types/flow.go b/pkg/sdk/model/types/flow.go
index 16f6134f..cc79e717 100644
--- a/pkg/sdk/model/types/flow.go
+++ b/pkg/sdk/model/types/flow.go
@@ -19,6 +19,8 @@ import (
 	"fmt"
 	"io"
 	"sort"
+
+	"github.com/banzaicloud/logging-operator/pkg/sdk/maps/mapstrstr"
 )
 
 type FluentConfig interface {
@@ -63,12 +65,12 @@ func (f *Flow) GetPluginMeta() *PluginMeta {
 	return &f.PluginMeta
 }
 
-func (f *Flow) GetParams() map[string]string {
+func (f *Flow) GetParams() Params {
 	return nil
 }
 
 func (f *Flow) GetSections() []Directive {
-	sections := []Directive{}
+	var sections []Directive
 	for _, filter := range f.Filters {
 		sections = append(sections, filter)
 	}
@@ -128,12 +130,8 @@ func calculateFlowLabel(matches []FlowMatch, name, namespace string) (string, er
 			}
 		}
 		// Make sure the generated label is consistent
-		keys := []string{}
-		for k := range match.Labels {
-			keys = append(keys, k)
-		}
+		keys := mapstrstr.Keys(match.Labels)
 		sort.Strings(keys)
-
 		for _, k := range keys {
 			if _, err := io.WriteString(b, k); err != nil {
 				return "", err
diff --git a/pkg/sdk/model/types/router.go b/pkg/sdk/model/types/router.go
index da20dbac..43702830 100644
--- a/pkg/sdk/model/types/router.go
+++ b/pkg/sdk/model/types/router.go
@@ -16,24 +16,25 @@ package types
 
 import (
 	"encoding/json"
+	"sort"
 	"strconv"
 	"strings"
 
-	util "github.com/banzaicloud/operator-tools/pkg/utils"
+	"github.com/banzaicloud/logging-operator/pkg/sdk/maps/mapstrstr"
 )
 
 // OutputPlugin plugin: https://github.com/banzaicloud/fluent-plugin-label-router
 type Router struct {
 	PluginMeta
 	Routes []Directive `json:"routes"`
-	Params map[string]string
+	Params Params
 }
 
 func (r *Router) GetPluginMeta() *PluginMeta {
 	return &r.PluginMeta
 }
 
-func (r *Router) GetParams() map[string]string {
+func (r *Router) GetParams() Params {
 	return r.Params
 }
 
@@ -59,8 +60,8 @@ func (f FlowMatch) GetPluginMeta() *PluginMeta {
 		Directive: "match",
 	}
 }
-func (f FlowMatch) GetParams() map[string]string {
-	params := map[string]string{
+func (f FlowMatch) GetParams() Params {
+	params := Params{
 		"negate": strconv.FormatBool(f.Negate),
 	}
 	if len(f.Namespaces) > 0 {
@@ -74,7 +75,9 @@ func (f FlowMatch) GetParams() map[string]string {
 	}
 	if len(f.Labels) > 0 {
 		var sb []string
-		for _, key := range util.OrderedStringMap(f.Labels).Keys() {
+		keys := mapstrstr.Keys(f.Labels)
+		sort.Strings(keys)
+		for _, key := range keys {
 			sb = append(sb, key+":"+f.Labels[key])
 		}
 		params["labels"] = strings.Join(sb, ",")
@@ -88,7 +91,7 @@ func (f FlowMatch) GetSections() []Directive {
 
 type FlowRoute struct {
 	PluginMeta
-	Params  map[string]string
+	Params  Params
 	Matches []Directive
 }
 
@@ -96,7 +99,7 @@ func (f *FlowRoute) GetPluginMeta() *PluginMeta {
 	return &f.PluginMeta
 }
 
-func (f *FlowRoute) GetParams() map[string]string {
+func (f *FlowRoute) GetParams() Params {
 	return f.Params
 }
 
@@ -110,7 +113,7 @@ func (r *Router) AddRoute(flow *Flow) *Router {
 			Directive: "route",
 			Label:     flow.FlowLabel,
 		},
-		Params: map[string]string{},
+		Params: Params{},
 	}
 	if flow.FlowID != "" {
 		metricsLabels, _ := json.Marshal(map[string]string{"id": flow.FlowID})
@@ -123,7 +126,7 @@ func (r *Router) AddRoute(flow *Flow) *Router {
 	return r
 }
 
-func NewRouter(id string, params map[string]string) *Router {
+func NewRouter(id string, params Params) *Router {
 	return &Router{
 		PluginMeta: PluginMeta{
 			Type:      "label_router",
diff --git a/pkg/sdk/model/types/stringmaps.go b/pkg/sdk/model/types/stringmaps.go
index 7f82dd92..9c698224 100644
--- a/pkg/sdk/model/types/stringmaps.go
+++ b/pkg/sdk/model/types/stringmaps.go
@@ -33,9 +33,6 @@ type StructToStringMapper struct {
 	SecretLoader    secret.SecretLoader
 }
 
-type NullSecretLoader struct {
-}
-
 func NewStructToStringMapper(secretLoader secret.SecretLoader) *StructToStringMapper {
 	return &StructToStringMapper{
 		TagName:         "json",
@@ -63,142 +60,125 @@ func (s *StructToStringMapper) fillMap(value reflect.Value, out map[string]strin
 
 	fields := s.structFields(value)
 
-	var multierror error
+	var errs error
 	for _, field := range fields {
-		name := field.Name
-		val := value.FieldByName(name)
-		var finalVal string
+		errs = errors.Append(errs, s.processField(field, value.FieldByIndex(field.Index), out))
+	}
+	return errs
+}
 
-		tagName, tagOpts := parseTagWithName(field.Tag.Get(s.TagName))
-		if tagName != "" {
-			name = tagName
-		}
+func (s *StructToStringMapper) processField(field reflect.StructField, value reflect.Value, out map[string]string) error {
+	name := field.Name
 
-		pluginTagOpts := parseTag(field.Tag.Get(s.PluginTagName))
-		required := pluginTagOpts.Has("required")
+	tagName, tagOpts := parseTagWithName(field.Tag.Get(s.TagName))
+	if tagName != "" {
+		name = tagName
+	}
 
-		if tagOpts.Has("omitempty") {
-			if pluginTagOpts.Has("required") {
-				multierror = errors.Combine(multierror, errors.Errorf(
-					"tags for field %s are conflicting: required and omitempty cannot be set simultaneously", name))
-				continue
-			}
-			zero := reflect.Zero(val.Type()).Interface()
-			current := val.Interface()
-			if reflect.DeepEqual(current, zero) {
-				if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
-					out[name] = def
-				}
-				continue
+	pluginTagOpts := parseTag(field.Tag.Get(s.PluginTagName))
+	required := pluginTagOpts.Has("required")
+
+	if tagOpts.Has("omitempty") {
+		if required {
+			return errors.Errorf("tags for field %q are conflicting: required and omitempty cannot be set simultaneously", name)
+		}
+		if value.IsZero() {
+			if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
+				out[name] = def
 			}
+			return nil
 		}
+	}
 
-		var v reflect.Value
-		if ok, converterName := pluginTagOpts.ValueForPrefix("converter:"); ok {
-			if hook, ok := s.ConversionHooks[converterName]; ok {
-				convertedValue, err := hook(val.Interface())
-				if err != nil {
-					multierror = errors.Combine(err, errors.Errorf(
-						"failed to convert field `%s` with converter %s", name, converterName))
-				} else {
-					v = reflect.ValueOf(convertedValue)
-				}
-			} else {
-				multierror = errors.Combine(multierror, errors.Errorf(
-					"unable to convert field `%s` as the specified converter `%s` is not registered", name, converterName))
-				continue
+	if ok, converterName := pluginTagOpts.ValueForPrefix("converter:"); ok {
+		if hook, ok := s.ConversionHooks[converterName]; ok {
+			convertedValue, err := hook(value.Interface())
+			if err != nil {
+				return errors.WrapIff(err, "failed to convert field %q with converter %q", name, converterName)
 			}
+			out[name] = convertedValue
+			return nil
 		} else {
-			v = reflect.ValueOf(val.Interface())
+			return errors.Errorf("unable to convert field %q as the specified converter %q is not registered", name, converterName)
 		}
+	}
 
-		if s.SecretLoader != nil {
-			if v.Kind() == reflect.Ptr && !v.IsNil() {
-				if secretItem, ok := val.Interface().(*secret.Secret); ok {
-					loadedSecret, err := s.SecretLoader.Load(secretItem)
-					if err != nil {
-						multierror = errors.Combine(multierror, errors.WrapIff(err, "failed to load secret for field %s", name))
-					}
-					if err == nil {
-						out[name] = loadedSecret
-					}
-					continue
-				}
+	if s.SecretLoader != nil {
+		if secretItem, _ := value.Interface().(*secret.Secret); secretItem != nil {
+			loadedSecret, err := s.SecretLoader.Load(secretItem)
+			if err != nil {
+				return errors.WrapIff(err, "failed to load secret for field %q", name)
 			}
+			out[name] = loadedSecret
+			return nil
 		}
+	}
 
-		if v.Kind() == reflect.Ptr {
-			v = v.Elem()
-		}
+	if value.Kind() == reflect.Ptr {
+		value = value.Elem()
+	}
 
-		// if the field is of string type and not empty, use it's value over the default
-		switch v.Kind() {
-		case reflect.String, reflect.Int, reflect.Bool:
-			stringVal := fmt.Sprintf("%v", v)
-			if stringVal != "" {
-				finalVal = stringVal
-			} else {
-				// check if default has been set and use it
-				if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
-					finalVal = def
-				}
+	switch value.Kind() {
+	case reflect.String, reflect.Int, reflect.Bool:
+		val := fmt.Sprintf("%v", value.Interface())
+		if val == "" {
+			// check if default has been set and use it
+			if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
+				val = def
 			}
-			// can't let to return an empty string when it's required
-			if finalVal == "" && required {
-				multierror = errors.Combine(multierror, errors.Errorf("field %s is required", name))
+		}
+		// can't return an empty string when it's required
+		if val == "" && required {
+			return errors.Errorf("field %q is required", name)
+		}
+		out[name] = val
+	case reflect.Slice:
+		switch actual := value.Interface().(type) {
+		case []string, []int:
+			if value.Len() > 0 {
+				b, err := json.Marshal(actual)
+				if err != nil {
+					return errors.WrapIff(err, "can't marshal field %q with value %v as json", name, actual)
+				}
+				out[name] = string(b)
 			} else {
-				out[name] = finalVal
-			}
-		case reflect.Slice:
-			switch actual := v.Interface().(type) {
-			case []string, []int:
-				if v.Len() > 0 {
-					b, err := json.Marshal(actual)
-					if err != nil {
-						multierror = errors.Combine(multierror, errors.Errorf("can't marshal field: %q value: %q as json", name, actual), err)
+				if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
+					if _, ok := actual.([]string); ok {
+						def = strings.Join(strings.Split(def, ","), `","`)
+						if def != "" {
+							def = `"` + def + `"`
+						}
 					}
-					out[name] = string(b)
-				} else {
-					if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
-						if _, ok := actual.([]string); ok {
-							b, err := json.Marshal(strings.Split(def, ","))
-							if err != nil {
-								multierror = errors.Combine(multierror, errors.Errorf("can't marshal field: %q value: %q as json", name, def), err)
-							} else {
-								out[name] = string(b)
-							}
-						} else {
-							sliceStr := fmt.Sprintf("[%s]", def)
-							if err := json.Unmarshal([]byte(sliceStr), &actual); err != nil {
-								multierror = errors.Combine(multierror, errors.Errorf("can't unmarshal field: %q value: %q", name, sliceStr), err)
-							} else {
-								out[name] = sliceStr
-							}
+					sliceStr := fmt.Sprintf("[%s]", def)
+					if _, ok := actual.([]string); !ok {
+						if err := json.Unmarshal([]byte(sliceStr), &actual); err != nil {
+							return errors.WrapIff(err, "can't unmarshal default value %q into field %q", sliceStr, name)
 						}
 					}
+					out[name] = sliceStr
 				}
 			}
-		case reflect.Map:
-			if mapStringString, ok := v.Interface().(map[string]string); ok {
-				if len(mapStringString) > 0 {
-					b, err := json.Marshal(mapStringString)
-					if err != nil {
-						multierror = errors.Combine(multierror, errors.Errorf("can't marshal field: %q value: %q as json", name, mapStringString), err)
-					}
-					out[name] = string(b)
-				} else {
-					if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
-						validate := map[string]string{}
-						if err := json.Unmarshal([]byte(def), &validate); err != nil {
-							multierror = errors.Combine(multierror, errors.Errorf("can't marshal field: %q value: %q as json", name, def), err)
-						}
-						out[name] = def
+		}
+	case reflect.Map:
+		if mapStringString, ok := value.Interface().(map[string]string); ok {
+			if len(mapStringString) > 0 {
+				b, err := json.Marshal(mapStringString)
+				if err != nil {
+					return errors.WrapIff(err, "can't marshal field %q with value %v as json", name, mapStringString)
+				}
+				out[name] = string(b)
+			} else {
+				if ok, def := pluginTagOpts.ValueForPrefix("default:"); ok {
+					validate := map[string]string{}
+					if err := json.Unmarshal([]byte(def), &validate); err != nil {
+						return errors.WrapIff(err, "can't unmarshal default value %q into field %q", def, name)
 					}
+					out[name] = def
 				}
 			}
 		}
 	}
-	return multierror
+	return nil
 }
 
 func strctVal(s interface{}) reflect.Value {
diff --git a/pkg/sdk/model/types/types.go b/pkg/sdk/model/types/types.go
index 28ca9217..d410aee7 100644
--- a/pkg/sdk/model/types/types.go
+++ b/pkg/sdk/model/types/types.go
@@ -36,7 +36,7 @@ func GetLogKey() string {
 
 type Directive interface {
 	GetPluginMeta() *PluginMeta
-	GetParams() map[string]string
+	GetParams() Params
 	GetSections() []Directive
 }
 
@@ -58,24 +58,7 @@ func Value(value string) *PluginParam {
 	}
 }
 
-type OutputPlugin struct {
-	PluginMeta
-	Output
-	Params        Params
-	SubDirectives []Directive
-}
-
-func (s *OutputPlugin) GetPluginMeta() *PluginMeta {
-	return &s.PluginMeta
-}
-
-func (s *OutputPlugin) GetParams() map[string]string {
-	return s.Params
-}
-
-func (s *OutputPlugin) GetSections() []Directive {
-	return s.SubDirectives
-}
+type OutputPlugin = GenericDirective
 
 type PluginMeta struct {
 	Type      string `json:"type,omitempty"`
@@ -88,15 +71,15 @@ type PluginMeta struct {
 
 type GenericDirective struct {
 	PluginMeta
-	Params        map[string]string `json:"params,omitempty"`
-	SubDirectives []Directive       `json:"sections,omitempty"`
+	Params        Params      `json:"params,omitempty"`
+	SubDirectives []Directive `json:"sections,omitempty"`
 }
 
 func (d *GenericDirective) GetPluginMeta() *PluginMeta {
 	return &d.PluginMeta
 }
 
-func (d *GenericDirective) GetParams() map[string]string {
+func (d *GenericDirective) GetParams() Params {
 	return d.Params
 }
 
@@ -147,14 +130,7 @@ func (p PluginParams) Equals(target PluginParams) error {
 	return nil
 }
 
-type Params map[string]string
-
-func (p Params) Merge(input map[string]string) Params {
-	for k, v := range input {
-		p[k] = v
-	}
-	return p
-}
+type Params = map[string]string
 
 func NewFlatDirective(meta PluginMeta, config interface{}, secretLoader secret.SecretLoader) (Directive, error) {
 	directive := &GenericDirective{


Test patch:
diff --git a/pkg/sdk/model/output/aws_elasticsearch_test.go b/pkg/sdk/model/output/aws_elasticsearch_test.go
index a367ba71..facba6d7 100644
--- a/pkg/sdk/model/output/aws_elasticsearch_test.go
+++ b/pkg/sdk/model/output/aws_elasticsearch_test.go
@@ -31,7 +31,7 @@ flush_interval: 1s
 endpoint:
   url: https://CLUSTER_ENDPOINT_URL
   region: eu-west-1
-  access_key_id: 
+  access_key_id:
     value: aws-key
   secret_access_key:
     value: aws_secret
diff --git a/pkg/sdk/model/output/datadog_test.go b/pkg/sdk/model/output/datadog_test.go
index ad39dff6..26e3be33 100644
--- a/pkg/sdk/model/output/datadog_test.go
+++ b/pkg/sdk/model/output/datadog_test.go
@@ -24,7 +24,7 @@ import (
 
 func TestDatadog(t *testing.T) {
 	CONFIG := []byte(`
-dd_source: '<INTEGRATION_NAME>' 
+dd_source: '<INTEGRATION_NAME>'
 dd_tags: '<KEY1:VALUE1>,<KEY2:VALUE2>'
 dd_sourcecategory: '<MY_SOURCE_CATEGORY>'
 buffer:
diff --git a/pkg/sdk/model/output/kinesis_stream_test.go b/pkg/sdk/model/output/kinesis_stream_test.go
index 5b71dfb9..32d6c150 100644
--- a/pkg/sdk/model/output/kinesis_stream_test.go
+++ b/pkg/sdk/model/output/kinesis_stream_test.go
@@ -41,7 +41,7 @@ buffer:
 	expected := `
   <match **>
     @type kinesis_streams
-    @id test   
+    @id test
     region us-east-1
     stream_name test
     <assume_role_credentials>
diff --git a/pkg/sdk/model/output/splunk_hec_test.go b/pkg/sdk/model/output/splunk_hec_test.go
index 37587ce0..feb605c1 100644
--- a/pkg/sdk/model/output/splunk_hec_test.go
+++ b/pkg/sdk/model/output/splunk_hec_test.go
@@ -58,7 +58,7 @@ source: foo
 		source foo
 		sourcetype foo
 		<fields>
-		  dummy  
+		  dummy
 		</fields>
 	</match>
 `
diff --git a/pkg/sdk/model/render/fluent_test.go b/pkg/sdk/model/render/fluent_test.go
index e87412cc..1b7a9f01 100644
--- a/pkg/sdk/model/render/fluent_test.go
+++ b/pkg/sdk/model/render/fluent_test.go
@@ -57,7 +57,7 @@ func TestRenderDirective(t *testing.T) {
 					Directive: "match",
 					Tag:       "tag",
 				},
-				Params: map[string]string{
+				Params: types.Params{
 					"path": "file",
 				},
 			},
@@ -86,7 +86,7 @@ func TestRenderDirective(t *testing.T) {
 				PluginMeta: types.PluginMeta{
 					Directive: "match",
 				},
-				Params: map[string]string{
+				Params: types.Params{
 					"path": "file",
 				},
 			},
@@ -102,7 +102,7 @@ func TestRenderDirective(t *testing.T) {
 				PluginMeta: types.PluginMeta{
 					Directive: "match",
 				},
-				Params: map[string]string{
+				Params: types.Params{
 					"path": "file",
 				},
 				SubDirectives: []types.Directive{
@@ -110,7 +110,7 @@ func TestRenderDirective(t *testing.T) {
 						PluginMeta: types.PluginMeta{
 							Directive: "router1",
 						},
-						Params: map[string]string{
+						Params: types.Params{
 							"namespace": "asd",
 							"labels":    "{\"a\":\"b\"}",
 						},
@@ -119,7 +119,7 @@ func TestRenderDirective(t *testing.T) {
 						PluginMeta: types.PluginMeta{
 							Directive: "router2",
 						},
-						Params: map[string]string{
+						Params: types.Params{
 							"namespace": "asd2",
 						},
 					},
diff --git a/pkg/sdk/model/types/stringmaps_test.go b/pkg/sdk/model/types/stringmaps_test.go
index 01c2a38b..7bd3318f 100644
--- a/pkg/sdk/model/types/stringmaps_test.go
+++ b/pkg/sdk/model/types/stringmaps_test.go
@@ -23,11 +23,11 @@ import (
 
 	"emperror.dev/errors"
 	"github.com/banzaicloud/operator-tools/pkg/secret"
-	v1 "k8s.io/api/core/v1"
+	corev1 "k8s.io/api/core/v1"
 )
 
 func TestRequired(t *testing.T) {
-	expectedError := "field field1 is required"
+	expectedError := "field \"field1\" is required"
 	type Asd struct {
 		Field1 string `json:"field1" plugin:"required"`
 	}
@@ -42,7 +42,7 @@ func TestRequired(t *testing.T) {
 }
 
 func TestRequiredMeansItCannotEvenBeEmpty(t *testing.T) {
-	expectedError := "field field1 is required"
+	expectedError := "field \"field1\" is required"
 	type Asd struct {
 		Field1 string `json:"field1" plugin:"required"`
 	}
@@ -122,7 +122,7 @@ func TestSliceFields(t *testing.T) {
 }
 
 func TestInvalidSliceDefault(t *testing.T) {
-	expectedError := `can't unmarshal field: "field1" value: "[str]"`
+	expectedError := `can't unmarshal default value "[str]" into field "field1"`
 	type Asd struct {
 		Field1 []int `json:"field1" plugin:"default:str"`
 	}
@@ -137,7 +137,7 @@ func TestInvalidSliceDefault(t *testing.T) {
 }
 
 func TestConflictingTags(t *testing.T) {
-	expectedError := "tags for field field2 are conflicting: required and omitempty cannot be set simultaneously"
+	expectedError := "tags for field \"field2\" are conflicting: required and omitempty cannot be set simultaneously"
 	type Asd struct {
 		Field2 string `json:"field2,omitempty" plugin:"required"`
 	}
@@ -267,8 +267,8 @@ func TestSecretValueFrom(t *testing.T) {
 	testStruct := Asd{
 		Field: &secret.Secret{
 			ValueFrom: &secret.ValueFrom{
-				SecretKeyRef: &v1.SecretKeySelector{
-					LocalObjectReference: v1.LocalObjectReference{
+				SecretKeyRef: &corev1.SecretKeySelector{
+					LocalObjectReference: corev1.LocalObjectReference{
 						Name: "a",
 					},
 					Key: "b",
@@ -305,7 +305,7 @@ func TestSecretErrorWhenEmpty(t *testing.T) {
 		t.Fatal("expected an error when secret contains no value or valuefrom")
 	}
 
-	expectedError := "failed to load secret for field field: no value found"
+	expectedError := "failed to load secret for field \"field\": no value found"
 	if err.Error() != expectedError {
 		t.Fatalf("Expected `%s` got `%s`", expectedError, err.Error())
 	}