diff --git a/pkg/config/generator.go b/pkg/config/generator.go
new file mode 100644
index 0000000..fc6ca55
--- /dev/null
+++ b/pkg/config/generator.go
@@ -0,0 +1,45 @@
+package config
+
+// FileContentGenerator
+// we can use this interface to generate file config content in config map
+// and use GenerateAllFile function to generate configMap data
+type FileContentGenerator interface {
+ Generate() string
+ FileName() string
+}
+
+// EnvGenerator
+// we can use this interface to generate env config in config map
+// and use GenerateAllEnv function to generate configMap data
+type EnvGenerator interface {
+ Generate() map[string]string
+}
+
+// Parser
+// config content parser, can get config content by golang-template or create config content self(customize)
+// see template_parser.go
+type Parser interface {
+ Parse() (string, error)
+}
+
+func GenerateAllFile(confGenerator []FileContentGenerator) map[string]string {
+ data := make(map[string]string)
+ for _, generator := range confGenerator {
+ if generator.Generate() != "" {
+ data[generator.FileName()] = generator.Generate()
+ }
+ }
+ return data
+}
+
+func GenerateAllEnv(confGenerator []EnvGenerator) map[string]string {
+ data := make(map[string]string)
+ for _, generator := range confGenerator {
+ if generator.Generate() != nil {
+ for k, v := range generator.Generate() {
+ data[k] = v
+ }
+ }
+ }
+ return data
+}
diff --git a/pkg/config/template_parser.go b/pkg/config/template_parser.go
new file mode 100644
index 0000000..d2ea597
--- /dev/null
+++ b/pkg/config/template_parser.go
@@ -0,0 +1,28 @@
+package config
+
+import (
+ "bytes"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "text/template"
+)
+
+var logging = ctrl.Log.WithName("template-parser")
+
+type TemplateParser struct {
+ Value interface{}
+ Template string
+}
+
+func (t *TemplateParser) Parse() (string, error) {
+ temp, err := template.New("").Parse(t.Template)
+ if err != nil {
+ logging.Error(err, "failed to parse template", "template", t.Template)
+ return t.Template, err
+ }
+ var b bytes.Buffer
+ if err := temp.Execute(&b, t.Value); err != nil {
+ logging.Error(err, "failed to execute template", "template", t.Template, "data", t.Value)
+ return t.Template, err
+ }
+ return b.String(), nil
+}
diff --git a/pkg/config/template_parser_test.go b/pkg/config/template_parser_test.go
new file mode 100644
index 0000000..f30c113
--- /dev/null
+++ b/pkg/config/template_parser_test.go
@@ -0,0 +1,76 @@
+package config
+
+import "testing"
+
+const log4jProperties = `log4j.rootLogger=INFO, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+{{- if .LogDir}}
+log4j.appender.stdout.File={{.LogDir}}/{{.LogName}}.log
+{{- else}}
+log4j.appender.stdout.File=logs/app.log
+{{- end}}
+log4j.appender.stdout.Threshold=INFO
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+{{ range .Loggers }}
+log4j.logger.{{.Logger}}={{.Level}}
+{{- end}}
+`
+
+func TestTemplateParser_Parse(t1 *testing.T) {
+ type fields struct {
+ Value interface{}
+ Template string
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want string
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {
+ name: "parse template simplely",
+ fields: fields{
+ Value: map[string]interface{}{
+ "LogDir": "customLogs",
+ "LogName": "customApp",
+ "Loggers": []map[string]string{
+ {"Logger": "test1", "Level": "WARN"},
+ {"Logger": "test2", "Level": "DEBUG"},
+ },
+ },
+ Template: log4jProperties,
+ },
+ want: `log4j.rootLogger=INFO, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.File=customLogs/customApp.log
+log4j.appender.stdout.Threshold=INFO
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+log4j.logger.test1=WARN
+log4j.logger.test2=DEBUG
+`,
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t1.Run(tt.name, func(t1 *testing.T) {
+ t := &TemplateParser{
+ Value: tt.fields.Value,
+ Template: tt.fields.Template,
+ }
+ got, err := t.Parse()
+ if (err != nil) != tt.wantErr {
+ t1.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t1.Errorf("Parse() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/pkg/util/configuration.go b/pkg/util/configuration.go
new file mode 100644
index 0000000..80dbc02
--- /dev/null
+++ b/pkg/util/configuration.go
@@ -0,0 +1,124 @@
+package util
+
+import (
+ "bufio"
+ "fmt"
+ "strings"
+)
+
+type NameValuePair struct {
+ comment string
+ Name string
+ Value string
+}
+
+// MakeConfigFileContent returns the content of a configuration file
+// content such as:
+// ```
+// key1 value1
+// key2 value2
+// ```
+func MakeConfigFileContent(config map[string]string) string {
+ content := ""
+ if len(config) == 0 {
+ return content
+ }
+ for k, v := range config {
+ content += fmt.Sprintf("%s %s\n", k, v)
+ }
+ return content
+}
+
+// MakePropertiesFileContent returns the content of a properties file
+// content such as:
+// ```properties
+// key1=value1
+// key2=value2
+// ```
+func MakePropertiesFileContent(config map[string]string) string {
+ content := ""
+ if len(config) == 0 {
+ return content
+ }
+ for k, v := range config {
+ content += fmt.Sprintf("%s=%s\n", k, v)
+ }
+ return content
+}
+
+func OverrideConfigFileContent(current string, override string) string {
+ if current == "" {
+ return override
+ }
+ if override == "" {
+ return current
+ }
+ return current + "\n" + override
+}
+
+// OverridePropertiesFileContent use bufio resolve properties
+func OverridePropertiesFileContent(current string, override []NameValuePair) (string, error) {
+ var properties []NameValuePair
+ //scan current
+ if err := ScanProperties(current, &properties); err != nil {
+ logger.Error(err, "failed to scan current properties")
+ return "", err
+ }
+ // override
+ OverrideProperties(override, &properties)
+
+ // to string
+ var res string
+ for _, v := range properties {
+ res += fmt.Sprintf("%s%s=%s\n", v.comment, v.Name, v.Value)
+ }
+ return res, nil
+}
+
+func ScanProperties(current string, properties *[]NameValuePair) error {
+ scanner := bufio.NewScanner(strings.NewReader(current))
+
+ var comment string
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "#") || len(line) == 0 {
+ comment += line + "\n"
+ continue
+ }
+
+ items := strings.Split(line, "=")
+ if len(items) == 2 {
+ *properties = append(*properties, NameValuePair{
+ comment: comment,
+ Name: items[0],
+ Value: items[1],
+ })
+ comment = ""
+ } else {
+ return fmt.Errorf("invalid property line: %s", line)
+ }
+ }
+ return scanner.Err()
+}
+
+func OverrideProperties(override []NameValuePair, current *[]NameValuePair) {
+ if len(override) == 0 {
+ return
+ }
+ var currentKeys = make(map[string]int)
+ for i, v := range *current {
+ currentKeys[v.Name] = i
+ }
+
+ for _, v := range override {
+ if _, ok := currentKeys[v.Name]; ok {
+ (*current)[currentKeys[v.Name]].Value = v.Value // override
+ } else {
+ // append new
+ *current = append(*current, NameValuePair{
+ Name: v.Name,
+ Value: v.Value,
+ })
+ }
+ }
+}
diff --git a/pkg/util/configuration_test.go b/pkg/util/configuration_test.go
new file mode 100644
index 0000000..531bf40
--- /dev/null
+++ b/pkg/util/configuration_test.go
@@ -0,0 +1,53 @@
+package util
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestOverrideXmlFileContent(t *testing.T) {
+ const origin = `
+
+
+ key1
+ value1
+
+ `
+
+ type args struct {
+ current string
+ override map[string]string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ // TODO: Add test cases.
+ {
+ name: "test1",
+ args: args{
+ current: origin,
+ override: map[string]string{"key2": "value2"},
+ },
+ want: `
+
+
+ key1
+ value1
+
+
+ key2
+ value2
+
+`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := OverrideXmlContent(tt.args.current, tt.args.override); strings.TrimSpace(got) != strings.TrimSpace(tt.want) {
+ t.Errorf("OverrideXmlContent() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/pkg/util/name_util.go b/pkg/util/name.go
similarity index 100%
rename from pkg/util/name_util.go
rename to pkg/util/name.go
diff --git a/pkg/util/reconciler_util.go b/pkg/util/reconciler.go
similarity index 100%
rename from pkg/util/reconciler_util.go
rename to pkg/util/reconciler.go
diff --git a/pkg/util/security_util.go b/pkg/util/security.go
similarity index 100%
rename from pkg/util/security_util.go
rename to pkg/util/security.go
diff --git a/pkg/util/security_util_test.go b/pkg/util/security_test.go
similarity index 100%
rename from pkg/util/security_util_test.go
rename to pkg/util/security_test.go
diff --git a/pkg/util/xml.go b/pkg/util/xml.go
new file mode 100644
index 0000000..62d95e6
--- /dev/null
+++ b/pkg/util/xml.go
@@ -0,0 +1,117 @@
+package util
+
+import (
+ "bytes"
+ "encoding/xml"
+)
+
+type XmlNameValuePair struct {
+ Name string `xml:"name"`
+ Value string `xml:"value"`
+}
+
+type XmlConfiguration struct {
+ XMLName xml.Name `xml:"configuration"`
+ Properties []XmlNameValuePair `xml:"property"`
+}
+
+func NewXmlConfiguration(properties []XmlNameValuePair) *XmlConfiguration {
+ return &XmlConfiguration{
+ Properties: properties,
+ }
+}
+
+func (c *XmlConfiguration) String(properties []XmlNameValuePair) string {
+ if len(c.Properties) != 0 {
+ c.Properties = c.DistinctProperties(properties)
+ }
+ buf := new(bytes.Buffer)
+ if _, err := buf.WriteString("\n"); err != nil {
+ logger.Error(err, "failed to write xml document head")
+ }
+ enc := xml.NewEncoder(buf)
+ enc.Indent("", " ")
+ if err := enc.Encode(c); err != nil {
+ logger.Error(err, "failed to encode xml document")
+ panic(err)
+ }
+ return buf.String()
+}
+
+// DistinctProperties distinct properties by name,
+func (c *XmlConfiguration) DistinctProperties(properties []XmlNameValuePair) []XmlNameValuePair {
+ var collect []XmlNameValuePair
+ collect = append(collect, c.Properties...)
+ collect = append(collect, properties...)
+
+ var distinctProperties []XmlNameValuePair
+ var distinctKeys map[string]int
+ for idx, v := range collect {
+ if distinctKeys == nil {
+ distinctKeys = make(map[string]int)
+ }
+ if existIdx, ok := distinctKeys[v.Name]; !ok {
+ distinctKeys[v.Name] = idx
+ distinctProperties = append(distinctProperties, v)
+ } else {
+ distinctProperties[existIdx] = v
+ }
+ }
+ return distinctProperties
+
+ //var distinctMap = make(map[string]XmlNameValuePair)
+ //for _, v := range collect {
+ // distinctMap[v.Name] = v
+ //}
+ //return maps.Values(distinctMap)
+}
+
+func (c *XmlConfiguration) StringWithProperties(properties map[string]string) string {
+ var pairs []XmlNameValuePair
+ for k, v := range properties {
+ pairs = append(pairs, XmlNameValuePair{
+ Name: k,
+ Value: v,
+ })
+ }
+ return c.String(pairs)
+}
+
+// Append to exist xml dom
+func Append(originXml string, properties []XmlNameValuePair) string {
+ var xmlDom XmlConfiguration
+ //string -> dom
+ if err := xml.Unmarshal([]byte(originXml), &xmlDom); err != nil {
+ panic(err)
+ }
+ return xmlDom.String(properties)
+}
+
+// OverrideXmlContent overrides the content of a xml file
+// append the override properties to the current xml dom
+func OverrideXmlContent(current string, overrideProperties map[string]string) string {
+ var xmlDom XmlConfiguration
+ //string -> dom
+ if err := xml.Unmarshal([]byte(current), &xmlDom); err != nil {
+ panic(err)
+ }
+ // do override
+ for k, v := range overrideProperties {
+ overridePair := XmlNameValuePair{
+ Name: k,
+ Value: v,
+ }
+ xmlDom.Properties = append(xmlDom.Properties, overridePair)
+ }
+ // dom -> string
+ var b bytes.Buffer
+ if _, err := b.WriteString("\n"); err != nil {
+ logger.Error(err, "failed to write string")
+ }
+ encoder := xml.NewEncoder(&b)
+ encoder.Indent("", " ")
+ if err := encoder.Encode(xmlDom); err != nil {
+ logger.Error(err, "failed to encode xml")
+ }
+ return b.String()
+}
diff --git a/pkg/util/xml_test.go b/pkg/util/xml_test.go
new file mode 100644
index 0000000..98cbb9b
--- /dev/null
+++ b/pkg/util/xml_test.go
@@ -0,0 +1,71 @@
+package util
+
+import (
+ "testing"
+)
+
+func TestXmlConfiguration_Append(t *testing.T) {
+
+ const origin = `
+
+
+ key1
+ value1
+
+ `
+
+ type args struct {
+ originXml string
+ properties []XmlNameValuePair
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ // TODO: Add test cases.
+ {
+ name: "append a new property",
+ args: args{
+ originXml: origin,
+ properties: []XmlNameValuePair{{Name: "key2", Value: "value2"}},
+ },
+ want: `
+
+
+ key1
+ value1
+
+
+ key2
+ value2
+
+`,
+ },
+ {
+ name: "append a new property, but the name exists in origin xml, should override it",
+ args: args{
+ originXml: origin,
+ properties: []XmlNameValuePair{{Name: "key1", Value: "value2"}, {Name: "key3", Value: "value3"}},
+ },
+ want: `
+
+
+ key1
+ value2
+
+
+ key3
+ value3
+
+`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := Append(tt.args.originXml, tt.args.properties); got != tt.want {
+ t.Errorf("XmlConfiguration.Append() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}