[#3896cn] Add support for suspending a server and blocking boot when suspended
This commit is contained in:
parent
fa42ed92fb
commit
0ce95aa2e3
11
http.go
11
http.go
|
@ -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) {
|
||||||
|
|
|
@ -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
14
server/errors.go
Normal 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
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user