[#3896bk] Configure base support for properties file parsing
This commit is contained in:
parent
11c6738264
commit
1003abaa63
|
@ -3,8 +3,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/buger/jsonparser"
|
"github.com/pterodactyl/wings/parser"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,44 +13,6 @@ const (
|
||||||
ProcessStopNativeStop = "stop"
|
ProcessStopNativeStop = "stop"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines a single find/replace instance for a given server configuration file.
|
|
||||||
type ConfigurationFileReplacement struct {
|
|
||||||
Match string `json:"match"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
ValueType jsonparser.ValueType `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfr *ConfigurationFileReplacement) UnmarshalJSON(data []byte) error {
|
|
||||||
if m, err := jsonparser.GetString(data, "match"); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
cfr.Match = m
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, dt, _, err := jsonparser.Get(data, "value"); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
if dt != jsonparser.String && dt != jsonparser.Number && dt != jsonparser.Boolean {
|
|
||||||
return errors.New(
|
|
||||||
fmt.Sprintf("cannot parse JSON: received unexpected replacement value type: %d", dt),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfr.Value = string(v)
|
|
||||||
cfr.ValueType = dt
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines a configuration file for the server startup. These will be looped over
|
|
||||||
// and modified before the server finishes booting.
|
|
||||||
type ConfigurationFile struct {
|
|
||||||
FileName string `json:"file"`
|
|
||||||
Parser string `json:"parser"`
|
|
||||||
Replace []ConfigurationFileReplacement `json:"replace"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines the process configuration for a given server instance. This sets what the
|
// Defines the process configuration for a given server instance. This sets what the
|
||||||
// daemon is looking for to mark a server as done starting, what to do when stopping,
|
// daemon is looking for to mark a server as done starting, what to do when stopping,
|
||||||
// and what changes to make to the configuration file for a server.
|
// and what changes to make to the configuration file for a server.
|
||||||
|
@ -64,7 +25,7 @@ type ServerConfiguration struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
} `json:"stop"`
|
} `json:"stop"`
|
||||||
ConfigurationFiles []ConfigurationFile `json:"configs"`
|
ConfigurationFiles []parser.ConfigurationFile `json:"configs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetches the server configuration and returns the struct for it.
|
// Fetches the server configuration and returns the struct for it.
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -23,6 +23,7 @@ require (
|
||||||
github.com/imdario/mergo v0.3.8
|
github.com/imdario/mergo v0.3.8
|
||||||
github.com/julienschmidt/httprouter v1.2.0
|
github.com/julienschmidt/httprouter v1.2.0
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.1
|
||||||
github.com/mcuadros/go-defaults v1.1.0
|
github.com/mcuadros/go-defaults v1.1.0
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
||||||
github.com/olebedev/emitter v0.0.0-20190110104742-e8d1457e6aee
|
github.com/olebedev/emitter v0.0.0-20190110104742-e8d1457e6aee
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -53,6 +53,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mcuadros/go-defaults v1.1.0 h1:K0LgSNfsSUrbEHR7HgfZpOHVWYsPnYh/dKTA7pGeZ/I=
|
github.com/mcuadros/go-defaults v1.1.0 h1:K0LgSNfsSUrbEHR7HgfZpOHVWYsPnYh/dKTA7pGeZ/I=
|
||||||
github.com/mcuadros/go-defaults v1.1.0/go.mod h1:vl9cJiNIIHISQeboDhZBUCiCOa3GkeioLe3Y95NXF6Y=
|
github.com/mcuadros/go-defaults v1.1.0/go.mod h1:vl9cJiNIIHISQeboDhZBUCiCOa3GkeioLe3Y95NXF6Y=
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||||
|
|
98
parser/parser.go
Normal file
98
parser/parser.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/buger/jsonparser"
|
||||||
|
"github.com/magiconair/properties"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigurationParser string
|
||||||
|
|
||||||
|
// The file parsing options that are available for a server configuration file.
|
||||||
|
const (
|
||||||
|
File = "file"
|
||||||
|
Yaml = "yaml"
|
||||||
|
Properties = "properties"
|
||||||
|
Ini = "ini"
|
||||||
|
Json = "json"
|
||||||
|
Xml = "xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines a configuration file for the server startup. These will be looped over
|
||||||
|
// and modified before the server finishes booting.
|
||||||
|
type ConfigurationFile struct {
|
||||||
|
FileName string `json:"file"`
|
||||||
|
Parser ConfigurationParser `json:"parser"`
|
||||||
|
Replace []ConfigurationFileReplacement `json:"replace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines a single find/replace instance for a given server configuration file.
|
||||||
|
type ConfigurationFileReplacement struct {
|
||||||
|
Match string `json:"match"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
ValueType jsonparser.ValueType `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfr *ConfigurationFileReplacement) UnmarshalJSON(data []byte) error {
|
||||||
|
if m, err := jsonparser.GetString(data, "match"); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
cfr.Match = m
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, dt, _, err := jsonparser.Get(data, "value"); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if dt != jsonparser.String && dt != jsonparser.Number && dt != jsonparser.Boolean {
|
||||||
|
return errors.New(
|
||||||
|
fmt.Sprintf("cannot parse JSON: received unexpected replacement value type: %d", dt),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfr.Value = string(v)
|
||||||
|
cfr.ValueType = dt
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses a given configuration file and updates all of the values within as defined
|
||||||
|
// in the API response from the Panel.
|
||||||
|
func (f *ConfigurationFile) Parse(path string) error {
|
||||||
|
zap.S().Debugw("parsing configuration file", zap.String("path", path), zap.String("parser", string(f.Parser)))
|
||||||
|
|
||||||
|
switch f.Parser {
|
||||||
|
case Properties:
|
||||||
|
f.parsePropertiesFile(path)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses a properties file and updates the values within it to match those that
|
||||||
|
// are passed. Writes the file once completed.
|
||||||
|
func (f *ConfigurationFile) parsePropertiesFile(path string) error {
|
||||||
|
p, err := properties.LoadFile(path, properties.UTF8)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, replace := range f.Replace {
|
||||||
|
if _, _, err := p.Set(replace.Match, replace.Value); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644);
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Write(w, properties.UTF8)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
33
server/config_parser.go
Normal file
33
server/config_parser.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parent function that will update all of the defined configuration files for a server
|
||||||
|
// automatically to ensure that they always use the specified values.
|
||||||
|
func (s *Server) UpdateConfigurationFiles() {
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
|
||||||
|
for _, v := range s.processConfiguration.ConfigurationFiles {
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func(server *Server) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
p, err := s.Filesystem.SafePath(v.FileName)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorw("failed to generate safe path for configuration file", zap.String("server", server.Uuid), zap.Error(err))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v.Parse(p); err != nil {
|
||||||
|
zap.S().Errorw("failed to parse and update server configuration file", zap.String("server", server.Uuid), zap.Error(err))
|
||||||
|
}
|
||||||
|
}(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
|
@ -261,6 +261,13 @@ func (d *DockerEnvironment) Start() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the configuration files defined for the server before beginning the boot process.
|
||||||
|
// This process executes a bunch of parallel updates, so we just block until that process
|
||||||
|
// is completed. Any errors as a result of this will just be bubbled out in the logger,
|
||||||
|
// we don't need to actively do anything about it at this point, worst comes to worst the
|
||||||
|
// server starts in a weird state and the user can manually adjust.
|
||||||
|
d.Server.UpdateConfigurationFiles()
|
||||||
|
|
||||||
// Reset the permissions on files for the server before actually trying
|
// Reset the permissions on files for the server before actually trying
|
||||||
// to start it.
|
// to start it.
|
||||||
if err := d.Server.Filesystem.Chown("/"); err != nil {
|
if err := d.Server.Filesystem.Chown("/"); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user