From fabaf21a0d2c383a794185b2d4f6306f2184b50b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 22 Dec 2019 13:21:21 -0800 Subject: [PATCH] Sync all server details when booting daemon or server process --- api/server_endpoints.go | 26 ++++++++++++++----- http.go | 2 +- server/environment_docker.go | 8 ++---- server/server.go | 48 ++++++++++++++++++++++++++---------- server/update.go | 14 ++++++----- 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/api/server_endpoints.go b/api/server_endpoints.go index aa00592..da33018 100644 --- a/api/server_endpoints.go +++ b/api/server_endpoints.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "fmt" + "github.com/pkg/errors" "github.com/pterodactyl/wings/parser" ) @@ -12,10 +13,23 @@ const ( ProcessStopNativeStop = "stop" ) +// Holds the server configuration data returned from the Panel. When a server process +// is started, Wings communicates with the Panel to fetch the latest build information +// as well as get all of the details needed to parse the given Egg. +// +// This means we do not need to hit Wings each time part of the server is updated, and +// the Panel serves as the source of truth at all times. This also means if a configuration +// is accidentally wiped on Wings we can self-recover without too much hassle, so long +// as Wings is aware of what servers should exist on it. +type ServerConfigurationResponse struct { + Settings json.RawMessage `json:"settings"` + ProcessConfiguration *ProcessConfiguration `json:"process_configuration"` +} + // 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, // and what changes to make to the configuration file for a server. -type ServerConfiguration struct { +type ProcessConfiguration struct { Startup struct { Done string `json:"done"` UserInteraction []string `json:"userInteraction"` @@ -28,10 +42,10 @@ type ServerConfiguration struct { } // Fetches the server configuration and returns the struct for it. -func (r *PanelRequest) GetServerConfiguration(uuid string) (*ServerConfiguration, *RequestError, error) { - resp, err := r.Get(fmt.Sprintf("/servers/%s/configuration", uuid)) +func (r *PanelRequest) GetServerConfiguration(uuid string) (*ServerConfigurationResponse, *RequestError, error) { + resp, err := r.Get(fmt.Sprintf("/servers/%s", uuid)) if err != nil { - return nil, nil, err + return nil, nil, errors.WithStack(err) } defer resp.Body.Close() @@ -41,11 +55,11 @@ func (r *PanelRequest) GetServerConfiguration(uuid string) (*ServerConfiguration return nil, r.Error(), nil } - res := &ServerConfiguration{} + res := &ServerConfigurationResponse{} b, _ := r.ReadBody() if err := json.Unmarshal(b, res); err != nil { - return nil, nil, err + return nil, nil, errors.WithStack(err) } return res, nil, nil diff --git a/http.go b/http.go index 303c6b6..66d0878 100644 --- a/http.go +++ b/http.go @@ -425,7 +425,7 @@ func (rt *Router) routeServerUpdate(w http.ResponseWriter, r *http.Request, ps h defer r.Body.Close() data := rt.ReaderToBytes(r.Body) - if err := s.UpdateDataStructure(data); err != nil { + if err := s.UpdateDataStructure(data, true); err != nil { zap.S().Errorw("failed to update a server's data structure", zap.String("server", s.Uuid), zap.Error(err)) http.Error(w, "failed to update data structure", http.StatusInternalServerError) diff --git a/server/environment_docker.go b/server/environment_docker.go index 7ba55f0..5878717 100644 --- a/server/environment_docker.go +++ b/server/environment_docker.go @@ -151,15 +151,11 @@ func (d *DockerEnvironment) InSituUpdate() error { // state. This ensures that unexpected container deletion while Wings is running does // not result in the server becoming unbootable. func (d *DockerEnvironment) OnBeforeStart() error { - c, rerr, err := d.Server.GetProcessConfiguration() - if err != nil { + zap.S().Infow("syncing server configuration with Panel", zap.String("server", d.Server.Uuid)) + if err := d.Server.Sync(); err != nil { return err - } else if rerr != nil { - return errors.New(rerr.String()) } - d.Server.processConfiguration = c - // If the server requires a rebuild, go ahead and delete the container from the system which // will allow the subsequent Create() call to create a new container instance for the server // to run in. diff --git a/server/server.go b/server/server.go index cafd978..3c7f25f 100644 --- a/server/server.go +++ b/server/server.go @@ -72,7 +72,7 @@ type Server struct { // Defines the process configuration for the server instance. This is dynamically // fetched from the Pterodactyl Server instance each time the server process is // started, and then cached here. - processConfiguration *api.ServerConfiguration + processConfiguration *api.ProcessConfiguration // Internal mutex used to block actions that need to occur sequentially, such as // writing the configuration to the disk. @@ -250,23 +250,45 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e // This is also done when the server is booted, however we need to account for instances // where the server is already running and the Daemon reboots. In those cases this will // allow us to you know, stop servers. - if cfg, rerr, err := s.GetProcessConfiguration(); err != nil { - return nil, err - } else if rerr != nil { - // If the response error is because a server does not exist on the Panel return - // that so that we can handle that in the future. - if rerr.Status == "404" { - return nil, &serverDoesNotExist{} + if cfg.SyncServersOnBoot { + if err := s.Sync(); err != nil { + return nil, err } - - return nil, errors.New(rerr.String()) - } else { - s.processConfiguration = cfg } return s, nil } +// Syncs the state of the server on the Panel with Wings. This ensures that we're always +// using the state of the server from the Panel and allows us to not require successful +// API calls to Wings to do things. +// +// This also means mass actions can be performed against servers on the Panel and they +// will automatically sync with Wings when the server is started. +func (s *Server) Sync() error { + cfg, rerr, err := s.GetProcessConfiguration() + if err != nil || rerr != nil { + if err != nil { + return errors.WithStack(err) + } + + if rerr.Status == "404" { + return &serverDoesNotExist{} + } + + return errors.New(rerr.String()) + } + + // Update the data structure and persist it to the disk. + if err:= s.UpdateDataStructure(cfg.Settings, false); err != nil { + return errors.WithStack(err) + } + + s.processConfiguration = cfg.ProcessConfiguration + + return nil +} + // Reads the log file for a server up to a specified number of bytes. func (s *Server) ReadLogfile(len int64) ([]string, error) { return s.Environment.Readlog(len) @@ -348,6 +370,6 @@ func (s *Server) SetState(state string) error { } // Gets the process configuration data for the server. -func (s *Server) GetProcessConfiguration() (*api.ServerConfiguration, *api.RequestError, error) { +func (s *Server) GetProcessConfiguration() (*api.ServerConfigurationResponse, *api.RequestError, error) { return api.NewRequester().GetServerConfiguration(s.Uuid) } diff --git a/server/update.go b/server/update.go index 121b834..eb50b75 100644 --- a/server/update.go +++ b/server/update.go @@ -17,9 +17,9 @@ import ( // The server will be marked as requiring a rebuild on the next boot sequence, // it is up to the specific environment to determine what needs to happen when // that is the case. -func (s *Server) UpdateDataStructure(data []byte) error { - src := Server{} - if err := json.Unmarshal(data, &src); err != nil { +func (s *Server) UpdateDataStructure(data []byte, background bool) error { + src := new(Server) + if err := json.Unmarshal(data, src); err != nil { return errors.WithStack(err) } @@ -31,8 +31,8 @@ func (s *Server) UpdateDataStructure(data []byte) error { } // Set the default values in the interface that we unmarshaled into. - if err := defaults.Set(&src); err != nil { - return err + if err := defaults.Set(src); err != nil { + return errors.WithStack(err) } // Merge the new data object that we have received with the existing server data object @@ -76,7 +76,9 @@ func (s *Server) UpdateDataStructure(data []byte) error { return errors.WithStack(err) } - s.runBackgroundActions() + if background { + s.runBackgroundActions() + } return nil }