Add support for modifying daemon configuration on-the-fly

This commit is contained in:
Dane Everitt 2020-04-11 16:17:46 -07:00
parent 03045c94be
commit 083bea5504
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
5 changed files with 52 additions and 15 deletions

View File

@ -17,6 +17,12 @@ import (
) )
type Configuration struct { type Configuration struct {
sync.RWMutex `json:"-" yaml:"-"`
// Locker specific to writing the configuration to the disk, this happens
// in areas that might already be locked so we don't want to crash the process.
writeLock sync.Mutex
// Determines if wings should be running in debug mode. This value is ignored // Determines if wings should be running in debug mode. This value is ignored
// if the debug flag is passed through the command line arguments. // if the debug flag is passed through the command line arguments.
Debug bool Debug bool
@ -207,7 +213,7 @@ func ReadConfiguration(path string) (*Configuration, error) {
c := new(Configuration) c := new(Configuration)
// Configures the default values for many of the configuration options present // Configures the default values for many of the configuration options present
// in the structs. Valkues set in the configuration file take priority over the // in the structs. Values set in the configuration file take priority over the
// default values. // default values.
if err := defaults.Set(c); err != nil { if err := defaults.Set(c); err != nil {
return nil, err return nil, err
@ -224,20 +230,29 @@ func ReadConfiguration(path string) (*Configuration, error) {
return c, nil return c, nil
} }
var Mutex sync.RWMutex
var _config *Configuration var _config *Configuration
var _debugViaFlag bool var _debugViaFlag bool
// Set the global configuration instance. // Set the global configuration instance. This is a blocking operation such that
// anything trying to set a different configuration value, or read the configuration
// will be paused until it is complete.
func Set(c *Configuration) { func Set(c *Configuration) {
Mutex.Lock()
_config = c _config = c
Mutex.Unlock()
} }
func SetDebugViaFlag(d bool) { func SetDebugViaFlag(d bool) {
_debugViaFlag = d _debugViaFlag = d
} }
// Get the global configuration instance. // Get the global configuration instance. This is a read-safe operation that will block
// if the configuration is presently being modified.
func Get() *Configuration { func Get() *Configuration {
Mutex.RLock()
defer Mutex.RUnlock()
return _config return _config
} }
@ -294,6 +309,9 @@ func (c *Configuration) setSystemUser(u *user.User) error {
uid, _ := strconv.Atoi(u.Uid) uid, _ := strconv.Atoi(u.Uid)
gid, _ := strconv.Atoi(u.Gid) gid, _ := strconv.Atoi(u.Gid)
c.Lock()
defer c.Unlock()
c.System.Username = u.Username c.System.Username = u.Username
c.System.User.Uid = uid c.System.User.Uid = uid
c.System.User.Gid = gid c.System.User.Gid = gid
@ -369,6 +387,10 @@ func (c *Configuration) WriteToDisk() error {
return err return err
} }
// Obtain an exclusive write against the configuration file.
c.writeLock.Lock()
defer c.writeLock.Unlock()
if err := ioutil.WriteFile("config.yml", b, 0644); err != nil { if err := ioutil.WriteFile("config.yml", b, 0644); err != nil {
return err return err
} }

View File

@ -30,6 +30,7 @@ func Configure() *gin.Engine {
// All of the routes beyond this mount will use an authorization middleware // All of the routes beyond this mount will use an authorization middleware
// and will not be accessible without the correct Authorization header provided. // and will not be accessible without the correct Authorization header provided.
protected := router.Use(AuthorizationMiddleware) protected := router.Use(AuthorizationMiddleware)
protected.POST("/api/update", postUpdateConfiguration)
protected.GET("/api/system", getSystemInformation) protected.GET("/api/system", getSystemInformation)
protected.GET("/api/servers", getAllServers) protected.GET("/api/servers", getAllServers)
protected.POST("/api/servers", postCreateServer) protected.POST("/api/servers", postCreateServer)

View File

@ -3,6 +3,7 @@ package router
import ( import (
"bytes" "bytes"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/installer" "github.com/pterodactyl/wings/installer"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
@ -63,3 +64,25 @@ func postCreateServer(c *gin.Context) {
c.Status(http.StatusAccepted) c.Status(http.StatusAccepted)
} }
// Updates the running configuration for this daemon instance.
func postUpdateConfiguration(c *gin.Context) {
// A backup of the configuration for error purposes.
ccopy := *config.Get()
// A copy of the configuration we're using to bind the data recevied into.
cfg := *config.Get()
c.BindJSON(&cfg)
config.Set(&cfg)
if err := config.Get().WriteToDisk(); err != nil {
// If there was an error writing to the disk, revert back to the configuration we had
// before this code was run.
config.Set(&ccopy)
TrackedError(err).AbortWithServerError(c)
return
}
c.Status(http.StatusNoContent)
}

View File

@ -16,6 +16,9 @@ func NewCollection(servers []*Server) *Collection {
// Return all of the items in the collection. // Return all of the items in the collection.
func (c *Collection) All() []*Server { func (c *Collection) All() []*Server {
c.RLock()
defer c.RUnlock()
return c.items return c.items
} }

View File

@ -69,10 +69,6 @@ func (s *Server) UpdateDataStructure(data []byte, background bool) error {
s.Allocations.Mappings = src.Allocations.Mappings s.Allocations.Mappings = src.Allocations.Mappings
} }
/*if _, err := s.WriteConfigurationToDisk(); err != nil {
return errors.WithStack(err)
}*/
if background { if background {
s.runBackgroundActions() s.runBackgroundActions()
} }
@ -105,14 +101,6 @@ func (s *Server) runBackgroundActions() {
if server.Suspended && server.GetState() != ProcessOfflineState { if server.Suspended && server.GetState() != ProcessOfflineState {
zap.S().Infow("server suspended with running process state, terminating now", zap.String("server", server.Uuid)) zap.S().Infow("server suspended with running process state, terminating now", zap.String("server", server.Uuid))
/*if err := server.Environment.Terminate(os.Kill); err != nil {
zap.S().Warnw(
"failed to terminate server environment after seeing suspension",
zap.String("server", server.Uuid),
zap.Error(err),
)
}*/
if err := server.Environment.WaitForStop(10, true); err != nil { if err := server.Environment.WaitForStop(10, true); err != nil {
zap.S().Warnw( zap.S().Warnw(
"failed to stop server environment after seeing suspension", "failed to stop server environment after seeing suspension",