Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 28 additions & 26 deletions parser/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,19 +185,15 @@ func (cfr *ConfigurationFileReplacement) setValueWithSjson(jsonStr string, path
}
setValue = value
default:
if v, err := strconv.Atoi(value); err == nil {
setValue = v
} else {
setValue = value
}
setValue = value
}
}

return sjson.Set(jsonStr, path, setValue)
}

// Looks up a configuration value on the Daemon given a dot-notated syntax.
func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplacement) (string, error) {
func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplacement) (result string, err error) {
// If this is not something that we can do a regex lookup on then just continue
// on our merry way. If the value isn't a string, we're not going to be doing anything
// with it anyways.
Expand All @@ -208,29 +204,35 @@ func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplac
// If there is a match, lookup the value in the configuration for the Daemon. If no key
// is found, just return the string representation, otherwise use the value from the
// daemon configuration here.
huntPath := configMatchRegex.ReplaceAllString(
configMatchRegex.FindString(cfr.ReplaceWith.String()), "$1",
)
result = configMatchRegex.ReplaceAllStringFunc(cfr.ReplaceWith.String(), func(placeholder string) string {
if err != nil {
return placeholder
}
keyPath := configMatchRegex.ReplaceAllString(placeholder, "$1")

var path []string
for _, value := range strings.Split(huntPath, ".") {
path = append(path, strcase.ToSnake(value))
}
var path []string
for _, part := range strings.Split(keyPath, ".") {
path = append(path, strcase.ToSnake(part))
}

// Look for the key in the configuration file, and if found return that value to the
// calling function.
match, _, _, err := jsonparser.Get(f.configuration, path...)
if err != nil {
if err != jsonparser.KeyPathNotFoundError {
return string(match), err
// Look for the key in the Wings configuration and substitute the placeholder.
match, dataType, _, err := jsonparser.Get(f.configuration, path...)
if err != nil {
if err != jsonparser.KeyPathNotFoundError {
return placeholder
}
log.WithFields(log.Fields{"path": path, "filename": f.FileName}).Debug("attempted to load a configuration value that does not exist")
// Leave placeholder intact so the misconfiguration is visible.
return placeholder
}

// Only substitute scalar values, not whole objects or arrays.
if dataType == jsonparser.Object || dataType == jsonparser.Array {
return placeholder
}

log.WithFields(log.Fields{"path": path, "filename": f.FileName}).Debug("attempted to load a configuration value that does not exist")
return string(match)
})

// If there is no key, keep the original value intact, that way it is obvious there
// is a replace issue at play.
return string(match), nil
} else {
return configMatchRegex.ReplaceAllString(cfr.ReplaceWith.String(), string(match)), nil
}
return result, err
}
90 changes: 82 additions & 8 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,27 @@ func (f *ConfigurationFile) UnmarshalJSON(data []byte) error {
return err
}

if err := json.Unmarshal(*m["file"], &f.FileName); err != nil {
fileRaw, ok := m["file"]
if !ok || fileRaw == nil {
return errors.New("parser: configuration file missing required 'file' key")
}
if err := json.Unmarshal(*fileRaw, &f.FileName); err != nil {
return err
}

if err := json.Unmarshal(*m["parser"], &f.Parser); err != nil {
parserRaw, ok := m["parser"]
if !ok || parserRaw == nil {
return errors.New("parser: configuration file missing required 'parser' key")
}
if err := json.Unmarshal(*parserRaw, &f.Parser); err != nil {
return err
}

if err := json.Unmarshal(*m["replace"], &f.Replace); err != nil {
log.WithField("file", f.FileName).WithField("error", err).Warn("failed to unmarshal configuration file replacement")

f.Replace = []ConfigurationFileReplacement{}
f.Replace = []ConfigurationFileReplacement{}
if replaceRaw, ok := m["replace"]; ok && replaceRaw != nil {
if err := json.Unmarshal(*replaceRaw, &f.Replace); err != nil {
log.WithField("file", f.FileName).WithField("error", err).Warn("failed to unmarshal configuration file replacement")
}
}

// test if "create_file" exists, if not just assume true
Expand Down Expand Up @@ -204,13 +213,29 @@ func (cfr *ConfigurationFileReplacement) UnmarshalJSON(data []byte) error {
return nil
}

type templatableConfig struct {
Docker struct {
Interface string `json:"interface"`
Network struct {
Interface string `json:"interface"`
} `json:"network"`
} `json:"docker"`
}

func newTemplatableConfig(c *config.Configuration) templatableConfig {
var t templatableConfig
t.Docker.Interface = c.Docker.Network.Interface
t.Docker.Network.Interface = c.Docker.Network.Interface
return t
}

// Parse parses a given configuration file and updates all the values within
// as defined in the API response from the Panel.
func (f *ConfigurationFile) Parse(file ufs.File) error {
//log.WithField("path", path).WithField("parser", f.Parser.String()).Debug("parsing server configuration file")

// What the fuck is going on here?
if mb, err := json.Marshal(config.Get()); err != nil {
if mb, err := json.Marshal(newTemplatableConfig(config.Get())); err != nil {
return err
} else {
f.configuration = mb
Expand Down Expand Up @@ -292,6 +317,7 @@ func (f *ConfigurationFile) parseXmlFile(file ufs.File) error {
k := xmlValueMatchRegex.ReplaceAllString(value, "$1")
v := xmlValueMatchRegex.ReplaceAllString(value, "$2")

element.RemoveAttr(k)
element.CreateAttr(k, v)
} else {
element.SetText(value)
Expand Down Expand Up @@ -462,9 +488,12 @@ func (f *ConfigurationFile) parseYamlFile(file ufs.File) error {
}

var jsonData interface{}
if err := json.Unmarshal(data, &jsonData); err != nil {
yamlDecoder := json.NewDecoder(bytes.NewReader(data))
yamlDecoder.UseNumber()
if err := yamlDecoder.Decode(&jsonData); err != nil {
return err
}
jsonData = normalizeYamlTypes(jsonData)

marshaled, err := yaml.Marshal(jsonData)
if err != nil {
Expand Down Expand Up @@ -536,6 +565,43 @@ func (f *ConfigurationFile) parseTomlFile(file ufs.File) error {
return nil
}

// normalizeYamlTypes converts json.Number values (produced by UseNumber()) into
// proper Go numeric types so that yaml.Marshal writes integers as plain integers
// instead of float64 scientific notation (e.g. 1.5e+18 for large Snowflake IDs).
func normalizeYamlTypes(value interface{}) interface{} {
switch typed := value.(type) {
case map[string]interface{}:
for key, item := range typed {
typed[key] = normalizeYamlTypes(item)
}
return typed
case []interface{}:
for i := range typed {
typed[i] = normalizeYamlTypes(typed[i])
}
return typed
case json.Number:
s := typed.String()
// Preserve float representation: if the number contains '.', 'e' or 'E'
// it was originally a float and must not be coerced to int64.
if strings.ContainsAny(s, ".eE") {
if floatVal, err := typed.Float64(); err == nil {
return floatVal
}
return s
}
if intVal, err := typed.Int64(); err == nil {
return intVal
}
if floatVal, err := typed.Float64(); err == nil {
return floatVal
}
return s
default:
return value
}
}

func normalizeTomlTypes(value interface{}) interface{} {
switch typed := value.(type) {
case map[string]interface{}:
Expand Down Expand Up @@ -585,6 +651,14 @@ func (f *ConfigurationFile) parseTextFile(file ufs.File) error {
if !bytes.HasPrefix(line, []byte(replace.Match)) {
continue
}
// If an if_value is set, only replace when the remainder of the line matches.
// Trim trailing \r\n so Windows line endings do not break the comparison.
if replace.IfValue != "" {
remainder := bytes.TrimRight(bytes.TrimPrefix(line, []byte(replace.Match)), "\r\n")
if string(remainder) != replace.IfValue {
continue
}
}
b.Write(replace.ReplaceWith.Bytes())
replaced = true
}
Expand Down
Loading