Improve server state logical handling; allow setting state directly on the environment
This commit is contained in:
@@ -63,7 +63,7 @@ func (s *Server) Install(sync bool) error {
|
||||
|
||||
// Ensure that the server is marked as offline at this point, otherwise you end up
|
||||
// with a blank value which is a bit confusing.
|
||||
s.SetState(environment.ProcessOfflineState)
|
||||
s.Environment.SetState(environment.ProcessOfflineState)
|
||||
|
||||
// Push an event to the websocket so we can auto-refresh the information in the panel once
|
||||
// the install is completed.
|
||||
|
||||
@@ -63,8 +63,8 @@ func (s *Server) StartEventListeners() {
|
||||
if err != nil {
|
||||
// If the process is already stopping, just let it continue with that action rather than attempting
|
||||
// to terminate again.
|
||||
if s.GetState() != environment.ProcessStoppingState {
|
||||
s.SetState(environment.ProcessStoppingState)
|
||||
if s.Environment.State() != environment.ProcessStoppingState {
|
||||
s.Environment.SetState(environment.ProcessStoppingState)
|
||||
go func() {
|
||||
s.Log().Warn("stopping server instance, violating throttle limits")
|
||||
s.PublishConsoleOutputFromDaemon("Your server is being stopped for outputting too much data in a short period of time.")
|
||||
@@ -73,8 +73,8 @@ func (s *Server) StartEventListeners() {
|
||||
if err := s.Environment.WaitForStop(config.Get().Throttles.StopGracePeriod, true); err != nil {
|
||||
// If there is an error set the process back to running so that this throttler is called
|
||||
// again and hopefully kills the server.
|
||||
if s.GetState() != environment.ProcessOfflineState {
|
||||
s.SetState(environment.ProcessRunningState)
|
||||
if s.Environment.State() != environment.ProcessOfflineState {
|
||||
s.Environment.SetState(environment.ProcessRunningState)
|
||||
}
|
||||
|
||||
s.Log().WithField("error", errors.WithStack(err)).Error("failed to terminate environment after triggering throttle")
|
||||
@@ -100,7 +100,7 @@ func (s *Server) StartEventListeners() {
|
||||
s.Throttler().Reset()
|
||||
}
|
||||
|
||||
s.SetState(e.Data)
|
||||
s.OnStateChange()
|
||||
}
|
||||
|
||||
stats := func(e events.Event) {
|
||||
@@ -173,7 +173,7 @@ func (s *Server) onConsoleOutput(data string) {
|
||||
// If the specific line of output is one that would mark the server as started,
|
||||
// set the server to that state. Only do this if the server is not currently stopped
|
||||
// or stopping.
|
||||
_ = s.SetState(environment.ProcessRunningState)
|
||||
s.Environment.SetState(environment.ProcessRunningState)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,7 @@ func (s *Server) onConsoleOutput(data string) {
|
||||
stop := processConfiguration.Stop
|
||||
|
||||
if stop.Type == api.ProcessStopCommand && data == stop.Value {
|
||||
_ = s.SetState(environment.ProcessOfflineState)
|
||||
s.Environment.SetState(environment.ProcessOfflineState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ func FromConfiguration(data api.ServerConfigurationResponse) (*Server, error) {
|
||||
|
||||
s.resources = ResourceUsage{}
|
||||
defaults.Set(&s.resources)
|
||||
s.resources.State.Store(environment.ProcessOfflineState)
|
||||
|
||||
s.Archiver = Archiver{Server: s}
|
||||
s.fs = filesystem.New(filepath.Join(config.Get().System.Data, s.Id()), s.DiskSpace())
|
||||
|
||||
@@ -3,7 +3,9 @@ package server
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/pterodactyl/wings/environment"
|
||||
"github.com/pterodactyl/wings/system"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Defines the current resource usage for a given server instance. If a server is offline you
|
||||
@@ -16,7 +18,7 @@ type ResourceUsage struct {
|
||||
environment.Stats
|
||||
|
||||
// The current server status.
|
||||
State string `json:"state" default:"offline"`
|
||||
State system.AtomicString `json:"state"`
|
||||
|
||||
// The current disk space being used by the server. This value is not guaranteed to be accurate
|
||||
// at all times. It is "manually" set whenever server.Proc() is called. This is kind of just a
|
||||
@@ -24,16 +26,16 @@ type ResourceUsage struct {
|
||||
Disk int64 `json:"disk_bytes"`
|
||||
}
|
||||
|
||||
// Alias the resource usage so that we don't infinitely recurse when marshaling the struct.
|
||||
type IResourceUsage ResourceUsage
|
||||
|
||||
// Custom marshaler to ensure that the object is locked when we're converting it to JSON in
|
||||
// order to avoid race conditions.
|
||||
func (ru *ResourceUsage) MarshalJSON() ([]byte, error) {
|
||||
ru.mu.Lock()
|
||||
defer ru.mu.Unlock()
|
||||
|
||||
return json.Marshal(IResourceUsage(*ru))
|
||||
// Alias the resource usage so that we don't infinitely recurse when marshaling the struct.
|
||||
type alias ResourceUsage
|
||||
|
||||
return json.Marshal(alias(*ru))
|
||||
}
|
||||
|
||||
// Returns the resource usage stats for the server instance. If the server is not running, only the
|
||||
@@ -42,10 +44,10 @@ func (ru *ResourceUsage) MarshalJSON() ([]byte, error) {
|
||||
//
|
||||
// When a process is stopped all of the stats are zeroed out except for the disk.
|
||||
func (s *Server) Proc() *ResourceUsage {
|
||||
s.resources.SetDisk(s.Filesystem().CachedUsage())
|
||||
// Store the updated disk usage when requesting process usage.
|
||||
atomic.StoreInt64(&s.resources.Disk, s.Filesystem().CachedUsage())
|
||||
|
||||
// Get a read lock on the resources at this point. Don't do this before setting
|
||||
// the disk, otherwise you'll cause a deadlock.
|
||||
// Acquire a lock before attempting to return the value of resources.
|
||||
s.resources.mu.RLock()
|
||||
defer s.resources.mu.RUnlock()
|
||||
|
||||
@@ -57,24 +59,3 @@ func (s *Server) emitProcUsage() {
|
||||
s.Log().WithField("error", err).Warn("error while emitting server resource usage to listeners")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the servers current state.
|
||||
func (ru *ResourceUsage) getInternalState() string {
|
||||
ru.mu.RLock()
|
||||
defer ru.mu.RUnlock()
|
||||
|
||||
return ru.State
|
||||
}
|
||||
|
||||
// Sets the new state for the server.
|
||||
func (ru *ResourceUsage) setInternalState(state string) {
|
||||
ru.mu.Lock()
|
||||
ru.State = state
|
||||
ru.mu.Unlock()
|
||||
}
|
||||
|
||||
func (ru *ResourceUsage) SetDisk(i int64) {
|
||||
ru.mu.Lock()
|
||||
ru.Disk = i
|
||||
ru.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"github.com/pterodactyl/wings/environment"
|
||||
@@ -63,23 +62,17 @@ func saveServerStates() error {
|
||||
|
||||
// Sets the state of the server internally. This function handles crash detection as
|
||||
// well as reporting to event listeners for the server.
|
||||
func (s *Server) SetState(state string) error {
|
||||
if state != environment.ProcessOfflineState &&
|
||||
state != environment.ProcessStartingState &&
|
||||
state != environment.ProcessRunningState &&
|
||||
state != environment.ProcessStoppingState {
|
||||
return errors.New(fmt.Sprintf("invalid server state received: %s", state))
|
||||
}
|
||||
|
||||
prevState := s.GetState()
|
||||
func (s *Server) OnStateChange() {
|
||||
prevState := s.Proc().State.Load()
|
||||
|
||||
st := s.Environment.State()
|
||||
// Update the currently tracked state for the server.
|
||||
s.Proc().setInternalState(state)
|
||||
s.Proc().State.Store(st)
|
||||
|
||||
// Emit the event to any listeners that are currently registered.
|
||||
if prevState != state {
|
||||
s.Log().WithField("status", s.Proc().getInternalState()).Debug("saw server status change event")
|
||||
s.Events().Publish(StatusEvent, s.Proc().getInternalState())
|
||||
if prevState != s.Environment.State() {
|
||||
s.Log().WithField("status", st).Debug("saw server status change event")
|
||||
s.Events().Publish(StatusEvent, st)
|
||||
}
|
||||
|
||||
// Persist this change to the disk immediately so that should the Daemon be stopped or
|
||||
@@ -98,7 +91,7 @@ func (s *Server) SetState(state string) error {
|
||||
|
||||
// Reset the resource usage to 0 when the process fully stops so that all of the UI
|
||||
// views in the Panel correctly display 0.
|
||||
if state == environment.ProcessOfflineState {
|
||||
if st == environment.ProcessOfflineState {
|
||||
s.resources.mu.Lock()
|
||||
s.resources.Empty()
|
||||
s.resources.mu.Unlock()
|
||||
@@ -127,13 +120,13 @@ func (s *Server) SetState(state string) error {
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the current state of the server in a race-safe manner.
|
||||
// Deprecated
|
||||
// use Environment.State()
|
||||
func (s *Server) GetState() string {
|
||||
return s.Proc().getInternalState()
|
||||
return s.Environment.State()
|
||||
}
|
||||
|
||||
// Determines if the server state is running or not. This is different than the
|
||||
|
||||
Reference in New Issue
Block a user