-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
props.go
200 lines (171 loc) · 5.29 KB
/
props.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package main
import (
"bufio"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"io"
"log"
"os"
"regexp"
"strings"
)
type bulkEntry struct {
Env string
Mappings map[string]string
Allowed []string
}
type bulkConfig map[string]*bulkEntry
func setPropertiesInFile(filename string, bulkConfig bulkConfig, tmpdir string) error {
// Open the properties file
// ...for reading and writing, since we'll read it first time through and then possibly re-write it
// ...also open with create flag to create the file, if absent
propsFile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return errors.Wrapf(err, "unable to access properties file %s", filename)
}
//noinspection GoUnhandledErrorResult
defer propsFile.Close()
// Setup a temp file for making changes as we go
tmpFile, err := os.CreateTemp(tmpdir, "*.properties")
if err != nil {
return errors.Wrap(err, "unable to create temporary file for modifications")
}
// ...remove temp file after we leave here
//noinspection GoUnhandledErrorResult
defer os.Remove(tmpFile.Name())
//noinspection GoUnhandledErrorResult
defer tmpFile.Close()
writer := bufio.NewWriter(tmpFile)
commentsRe := regexp.MustCompile("#.*")
propRe := regexp.MustCompile(`(.+?)\s*=\s*(.*)`)
// Go through each line of the existing properties file
var modified = false
scanner := bufio.NewScanner(propsFile)
for scanner.Scan() {
resultLine := scanner.Text()
// strip away commented parts of line
line := commentsRe.ReplaceAllString(resultLine, "")
// trim surrounding whitespace
line = strings.TrimSpace(line)
// see if remainder is a property setting line
if groups := propRe.FindStringSubmatch(line); groups != nil {
property := groups[1]
if entry, entryExists := bulkConfig[property]; entryExists {
delete(bulkConfig, property)
value, err := resolveValue(property, entry)
if err != nil {
return err
}
if value != "" && value != groups[2] {
modified = true
log.Printf("Setting %s to %s in %s\n", property, value, filename)
resultLine = fmt.Sprintf("%s=%s", property, value)
}
}
}
// write the newly modified or existing, if not a match, line to temp file
_, err := fmt.Fprintln(writer, resultLine)
if err != nil {
return errors.Wrap(err, "failed to write to temp file")
}
}
// Process properties that weren't in existing file
for property, entry := range bulkConfig {
modified = true
value, err := resolveValue(property, entry)
if err != nil {
return err
}
if value != "" {
log.Printf("Setting %s to %s in %s\n", property, value, filename)
_, err = fmt.Fprintf(writer, "%s=%s\n", property, value)
if err != nil {
return errors.Wrap(err, "failed to write to temp file")
}
}
}
// If modification was needed
if modified {
// ...flush temp content to disk
if err := writer.Flush(); err != nil {
return errors.Wrap(err, "failed to flush temp content")
}
return copyOverTempFile(tmpFile, propsFile)
}
return nil
}
func resolveValue(property string, entry *bulkEntry) (string, error) {
value := os.Getenv(entry.Env)
if value != "" {
if entry.Mappings != nil {
if mappedValue, mappingExists := entry.Mappings[value]; mappingExists {
value = mappedValue
}
}
if isAllowed(entry.Allowed, value) {
return value, nil
} else {
return "", errors.Errorf("Value '%s' for property %s is in allowed list: %v", value, property, entry.Allowed)
}
} else {
return "", nil
}
}
func copyOverTempFile(tmpFile *os.File, propsFile *os.File) error {
// ...rewind the files to the beginning
_, err := tmpFile.Seek(0, 0)
if err != nil {
return errors.Wrap(err, "could not rewind temp content file")
}
_, err = propsFile.Seek(0, 0)
if err != nil {
return errors.Wrap(err, "could not rewind properties file for re-writing")
}
// ...truncate original properties file since we're overwriting it
if err := propsFile.Truncate(0); err != nil {
return errors.Wrap(err, "failed to truncate properties file")
}
// ...copy modified, temp file content over into what was the original properties file
_, err = io.Copy(propsFile, tmpFile)
if err != nil {
return errors.Wrap(err, "could not copy temp content to properties file")
}
return nil
}
func setBulkProperties(filename string, bulkDefinitionsFilename string, tmpdir string) error {
bulkDefinitionsFile, err := os.Open(bulkDefinitionsFilename)
if err != nil {
return errors.Wrap(err, "unable to open bulk definitions file")
}
//noinspection GoUnhandledErrorResult
defer bulkDefinitionsFile.Close()
decoder := json.NewDecoder(bulkDefinitionsFile)
var bulkConfig bulkConfig
err = decoder.Decode(&bulkConfig)
if err != nil {
return errors.Wrap(err, "unable to decode bulk definitions")
}
return setPropertiesInFile(filename, bulkConfig, tmpdir)
}
func setSingleProperty(filename string, property string, envVar string, mappings map[string]string, allowed []string, tmpdir string) error {
bulkConfig := make(bulkConfig)
bulkConfig[property] = &bulkEntry{
Env: envVar,
Mappings: mappings,
Allowed: allowed,
}
return setPropertiesInFile(filename, bulkConfig, tmpdir)
}
func isAllowed(allowed []string, value string) bool {
if allowed != nil && len(allowed) > 0 {
for _, v := range allowed {
if value == v {
return true
}
}
return false
} else {
return true
}
}