diff --git a/go.mod b/go.mod index a6a941a..6efb1e2 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect + gopkg.in/ini.v1 v1.51.0 gopkg.in/yaml.v2 v2.2.2 gotest.tools v2.2.0+incompatible // indirect ) diff --git a/go.sum b/go.sum index 6c0165d..a35c346 100644 --- a/go.sum +++ b/go.sum @@ -142,6 +142,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/parser/helpers.go b/parser/helpers.go index cc3c86d..de449c6 100644 --- a/parser/helpers.go +++ b/parser/helpers.go @@ -25,27 +25,30 @@ func readFileBytes(path string) ([]byte, error) { // Helper function to set the value of the JSON key item based on the jsonparser value // type returned. -func setPathway(c *gabs.Container, path string, value []byte, dt jsonparser.ValueType) error { - var err error +func setPathway(c *gabs.Container, path string, value []byte, vt jsonparser.ValueType) error { + v := getKeyValue(value, vt) - switch dt { + _, err := c.SetP(v, path) + + return err +} + +// Gets the value of a key based on the value type defined. +func getKeyValue(value []byte, vt jsonparser.ValueType) interface{} { + switch vt { case jsonparser.Number: { v, _ := strconv.Atoi(string(value)) - _, err = c.SetP(v, path) + return v } - break case jsonparser.Boolean: { v, _ := strconv.ParseBool(string(value)) - _, err = c.SetP(v, path) + return v } - break default: - _, err = c.SetP(string(value), path) + return string(value) } - - return err } // Iterate over an unstructured JSON/YAML/etc. interface and set all of the required diff --git a/parser/parser.go b/parser/parser.go index c4d1de9..3666b66 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/pterodactyl/wings/config" "go.uber.org/zap" + "gopkg.in/ini.v1" "io/ioutil" "os" "regexp" @@ -102,12 +103,73 @@ func (f *ConfigurationFile) Parse(path string) error { case Json: err = f.parseJsonFile(path) break + case Ini: + err = f.parseIniFile(path) + break } return err } -// Prases a json file updating any matching key/value pairs. If a match is not found, the +// Parses an ini file. +func (f *ConfigurationFile) parseIniFile(path string) error { + // Ini package can't handle a non-existent file, so handle that automatically here + // by creating it if not exists. + file, err := os.OpenFile(path, os.O_CREATE | os.O_RDWR, 0644); + if err != nil { + return errors.WithStack(err) + } + defer file.Close() + + cfg, err := ini.Load(path) + if err != nil { + return errors.WithStack(err) + } + + for _, replacement := range f.Replace { + path := strings.SplitN(replacement.Match, ".", 2) + + value, _, err := f.LookupConfigurationValue(replacement) + if err != nil { + return errors.WithStack(err) + } + + k := path[0] + s := cfg.Section("") + // Passing a key of foo.bar will look for "bar" in the "[foo]" section of the file. + if len(path) == 2 { + k = path[1] + s = cfg.Section(path[0]) + } + + // If no section was found, create that new section now and then set the + // section value we're using to be the new one. + if s == nil { + s, err = cfg.NewSection(path[0]) + if err != nil { + return errors.WithStack(err) + } + } + + // If the key exists in the file go ahead and set the value, otherwise try to + // create it in the section. + if s.HasKey(k) { + s.Key(k).SetValue(string(value)) + } else { + if _, err := s.NewKey(k, string(value)); err != nil { + return errors.WithStack(err) + } + } + } + + if _, err := cfg.WriteTo(file); err != nil { + return errors.WithStack(err) + } + + return nil +} + +// Parses a json file updating any matching key/value pairs. If a match is not found, the // value is set regardless in the file. See the commentary in parseYamlFile for more details // about what is happening during this process. func (f *ConfigurationFile) parseJsonFile(path string) error {