From 947279a07c0c9ed7c02d9eee448c5cacfac7b8c1 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 12:06:47 -0700 Subject: [PATCH] Don't abort entire boot process due to one bad server egg; closes pterodactyl/panel#2448 --- api/server_endpoints.go | 19 +++++++++---------- environment/docker/environment.go | 4 ++-- environment/docker/power.go | 4 ++-- environment/docker/stream.go | 12 +++++------- server/loader.go | 15 +++++++++++++-- server/server.go | 6 +++--- 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/api/server_endpoints.go b/api/server_endpoints.go index e974f5c..0368b0c 100644 --- a/api/server_endpoints.go +++ b/api/server_endpoints.go @@ -34,7 +34,7 @@ type InstallationScript struct { } // GetAllServerConfigurations fetches configurations for all servers assigned to this node. -func (r *PanelRequest) GetAllServerConfigurations() (map[string]*ServerConfigurationResponse, *RequestError, error) { +func (r *PanelRequest) GetAllServerConfigurations() (map[string]json.RawMessage, *RequestError, error) { resp, err := r.Get("/servers") if err != nil { return nil, nil, errors.WithStack(err) @@ -48,7 +48,7 @@ func (r *PanelRequest) GetAllServerConfigurations() (map[string]*ServerConfigura } b, _ := r.ReadBody() - res := map[string]*ServerConfigurationResponse{} + res := map[string]json.RawMessage{} if len(b) == 2 { return res, nil, nil } @@ -61,24 +61,23 @@ func (r *PanelRequest) GetAllServerConfigurations() (map[string]*ServerConfigura } // Fetches the server configuration and returns the struct for it. -func (r *PanelRequest) GetServerConfiguration(uuid string) (*ServerConfigurationResponse, *RequestError, error) { +func (r *PanelRequest) GetServerConfiguration(uuid string) (ServerConfigurationResponse, *RequestError, error) { + res := ServerConfigurationResponse{} + resp, err := r.Get(fmt.Sprintf("/servers/%s", uuid)) if err != nil { - return nil, nil, errors.WithStack(err) + return res, nil, errors.WithStack(err) } defer resp.Body.Close() r.Response = resp - if r.HasError() { - return nil, r.Error(), nil + return res, r.Error(), nil } - res := &ServerConfigurationResponse{} b, _ := r.ReadBody() - - if err := json.Unmarshal(b, res); err != nil { - return nil, nil, errors.WithStack(err) + if err := json.Unmarshal(b, &res); err != nil { + return res, nil, errors.WithStack(err) } return res, nil, nil diff --git a/environment/docker/environment.go b/environment/docker/environment.go index 2c3f191..4893032 100644 --- a/environment/docker/environment.go +++ b/environment/docker/environment.go @@ -14,7 +14,7 @@ import ( type Metadata struct { Image string - Stop *api.ProcessStopConfiguration + Stop api.ProcessStopConfiguration } // Ensure that the Docker environment is always implementing all of the methods @@ -171,7 +171,7 @@ func (e *Environment) Config() *environment.Configuration { } // Sets the stop configuration for the environment. -func (e *Environment) SetStopConfiguration(c *api.ProcessStopConfiguration) { +func (e *Environment) SetStopConfiguration(c api.ProcessStopConfiguration) { e.mu.Lock() e.meta.Stop = c e.mu.Unlock() diff --git a/environment/docker/power.go b/environment/docker/power.go index 20bcaa7..3a4a071 100644 --- a/environment/docker/power.go +++ b/environment/docker/power.go @@ -126,8 +126,8 @@ func (e *Environment) Stop() error { s := e.meta.Stop e.mu.RUnlock() - if s == nil || s.Type == api.ProcessStopSignal { - if s == nil { + if s.Type == "" || s.Type == api.ProcessStopSignal { + if s.Type == "" { log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using termination procedure") } diff --git a/environment/docker/stream.go b/environment/docker/stream.go index 029fcce..8b20c2a 100644 --- a/environment/docker/stream.go +++ b/environment/docker/stream.go @@ -33,13 +33,11 @@ func (e *Environment) SendCommand(c string) error { e.mu.RLock() defer e.mu.RUnlock() - if e.meta.Stop != nil { - // If the command being processed is the same as the process stop command then we want to mark - // the server as entering the stopping state otherwise the process will stop and Wings will think - // it has crashed and attempt to restart it. - if e.meta.Stop.Type == "command" && c == e.meta.Stop.Value { - e.Events().Publish(environment.StateChangeEvent, environment.ProcessStoppingState) - } + // If the command being processed is the same as the process stop command then we want to mark + // the server as entering the stopping state otherwise the process will stop and Wings will think + // it has crashed and attempt to restart it. + if e.meta.Stop.Type == "command" && c == e.meta.Stop.Value { + e.Events().Publish(environment.StateChangeEvent, environment.ProcessStoppingState) } _, err := e.stream.Conn.Write([]byte(c + "\n")) diff --git a/server/loader.go b/server/loader.go index e2e12da..89996b3 100644 --- a/server/loader.go +++ b/server/loader.go @@ -1,6 +1,7 @@ package server import ( + "encoding/json" "fmt" "github.com/apex/log" "github.com/creasty/defaults" @@ -49,8 +50,18 @@ func LoadDirectory() error { data := data pool.Submit(func() { + // Parse the json.RawMessage into an expected struct value. We do this here so that a single broken + // server does not cause the entire boot process to hang, and allows us to show more useful error + // messaging in the output. + d := api.ServerConfigurationResponse{} + log.WithField("server", uuid).Info("creating new server object from API response") - s, err := FromConfiguration(data) + if err := json.Unmarshal(data, &d); err != nil { + log.WithField("server", uuid).WithField("error", err).Error("failed to parse server configuration from API response, skipping...") + return + } + + s, err := FromConfiguration(d) if err != nil { log.WithField("server", uuid).WithField("error", err).Error("failed to load server, skipping...") return @@ -73,7 +84,7 @@ func LoadDirectory() error { // Initializes a server using a data byte array. This will be marshaled into the // given struct using a YAML marshaler. This will also configure the given environment // for a server. -func FromConfiguration(data *api.ServerConfigurationResponse) (*Server, error) { +func FromConfiguration(data api.ServerConfigurationResponse) (*Server, error) { cfg := Configuration{} if err := defaults.Set(&cfg); err != nil { return nil, errors.Wrap(err, "failed to set struct defaults for server configuration") diff --git a/server/server.go b/server/server.go index fc14d55..84756c1 100644 --- a/server/server.go +++ b/server/server.go @@ -128,7 +128,7 @@ func (s *Server) Sync() error { return s.SyncWithConfiguration(cfg) } -func (s *Server) SyncWithConfiguration(cfg *api.ServerConfigurationResponse) error { +func (s *Server) SyncWithConfiguration(cfg api.ServerConfigurationResponse) error { // Update the data structure and persist it to the disk. if err := s.UpdateDataStructure(cfg.Settings); err != nil { return errors.WithStack(err) @@ -147,7 +147,7 @@ func (s *Server) SyncWithConfiguration(cfg *api.ServerConfigurationResponse) err if e, ok := s.Environment.(*docker.Environment); ok { s.Log().Debug("syncing stop configuration with configured docker environment") e.SetImage(s.Config().Container.Image) - e.SetStopConfiguration(&cfg.ProcessConfiguration.Stop) + e.SetStopConfiguration(cfg.ProcessConfiguration.Stop) } return nil @@ -178,7 +178,7 @@ func (s *Server) CreateEnvironment() error { } // Gets the process configuration data for the server. -func (s *Server) GetProcessConfiguration() (*api.ServerConfigurationResponse, *api.RequestError, error) { +func (s *Server) GetProcessConfiguration() (api.ServerConfigurationResponse, *api.RequestError, error) { return api.NewRequester().GetServerConfiguration(s.Id()) }