[#3896cn] Add support for suspending a server and blocking boot when suspended

This commit is contained in:
Dane Everitt 2019-11-30 15:19:08 -08:00
parent fa42ed92fb
commit 0ce95aa2e3
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
5 changed files with 78 additions and 3 deletions

11
http.go
View File

@ -152,6 +152,17 @@ func (rt *Router) routeServerPower(w http.ResponseWriter, r *http.Request, ps ht
return return
} }
// Because we route all of the actual bootup process to a seperate thread we need to
// check the suspension status here, otherwise the user will hit the endpoint and then
// just sit there wondering why it returns a success but nothing actually happens.
//
// We don't really care about any of the other actions at this point, they'll all result
// in the process being stopped, which should have happened anyways if the server is suspended.
if action.Action == "start" && s.Suspended {
http.Error(w, "server is suspended", http.StatusBadRequest)
return
}
// Pass the actual heavy processing off to a seperate thread to handle so that // Pass the actual heavy processing off to a seperate thread to handle so that
// we can immediately return a response from the server. // we can immediately return a response from the server.
go func(a string, s *server.Server) { go func(a string, s *server.Server) {

View File

@ -215,6 +215,16 @@ func (d *DockerEnvironment) Start() error {
} }
}() }()
// If the server is suspended the user shouldn't be able to boot it, in those cases
// return a suspension error and let the calling area handle the issue.
//
// Theoretically you'd have the Panel handle all of this logic, but we cannot do that
// because we allow the websocket to control the server power state as well, so we'll
// need to handle that action in here.
if d.Server.Suspended {
return &suspendedError{}
}
c, err := d.Client.ContainerInspect(context.Background(), d.Server.Uuid) c, err := d.Client.ContainerInspect(context.Background(), d.Server.Uuid)
if err != nil && !client.IsErrNotFound(err) { if err != nil && !client.IsErrNotFound(err) {
return errors.WithStack(err) return errors.WithStack(err)

14
server/errors.go Normal file
View File

@ -0,0 +1,14 @@
package server
type suspendedError struct {
}
func (e *suspendedError) Error() string {
return "server is currently in a suspended state"
}
func IsSuspendedError(err error) bool {
_, ok := err.(*suspendedError)
return ok
}

View File

@ -5,6 +5,8 @@ import (
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap"
"os"
) )
// Merges data passed through in JSON form into the existing server object. // Merges data passed through in JSON form into the existing server object.
@ -59,5 +61,41 @@ func (s *Server) UpdateDataStructure(data []byte) error {
return errors.WithStack(err) return errors.WithStack(err)
} }
return s.Environment.InSituUpdate() s.runBackgroundActions()
return nil
}
// Runs through different actions once a server's configuration has been persisted
// to the disk. This function does not return anything as any failures should be logged
// but have no effect on actually updating the server itself.
//
// These tasks run in independent threads where relevant to speed up any updates
// that need to happen.
func (s *Server) runBackgroundActions() {
// Update the environment in place, allowing memory and CPU usage to be adjusted
// on the fly without the user needing to reboot (theoretically).
go func(server *Server) {
if err := server.Environment.InSituUpdate(); err != nil {
zap.S().Warnw(
"failed to perform in-situ update of server environment",
zap.String("server", server.Uuid),
zap.Error(err),
)
}
}(s)
// Check if the server is now suspended, and if so and the process is not terminated
// yet, do it immediately.
go func(server *Server) {
if server.Suspended && server.State != ProcessOfflineState {
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),
)
}
}
}(s)
} }

View File

@ -276,8 +276,10 @@ func (wsh *WebsocketHandler) SendErrorJson(err error) error {
defer wsh.Mutex.Unlock() defer wsh.Mutex.Unlock()
message := "an unexpected error was encountered while handling this request" message := "an unexpected error was encountered while handling this request"
if wsh.JWT != nil && wsh.JWT.HasPermission(PermissionReceiveErrors) { if wsh.JWT != nil {
message = err.Error() if server.IsSuspendedError(err) || wsh.JWT.HasPermission(PermissionReceiveErrors) {
message = err.Error()
}
} }
m, u := wsh.GetErrorMessage(message) m, u := wsh.GetErrorMessage(message)