Emit server status events
This commit is contained in:
parent
bed30d9229
commit
8795e7d739
|
@ -120,6 +120,7 @@ func (d *DockerEnvironment) Start() error {
|
||||||
|
|
||||||
// No reason to try starting a container that is already running.
|
// No reason to try starting a container that is already running.
|
||||||
if c.State.Running {
|
if c.State.Running {
|
||||||
|
d.Server.Emit(StatusEvent, ProcessRunningState)
|
||||||
if !d.attached {
|
if !d.attached {
|
||||||
return d.Attach()
|
return d.Attach()
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,9 @@ func (d *DockerEnvironment) Start() error {
|
||||||
|
|
||||||
opts := types.ContainerStartOptions{}
|
opts := types.ContainerStartOptions{}
|
||||||
|
|
||||||
|
d.Server.Emit(StatusEvent, ProcessStartingState)
|
||||||
if err := d.Client.ContainerStart(context.Background(), d.Server.Uuid, opts); err != nil {
|
if err := d.Client.ContainerStart(context.Background(), d.Server.Uuid, opts); err != nil {
|
||||||
|
d.Server.Emit(StatusEvent, ProcessOfflineState)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +145,7 @@ func (d *DockerEnvironment) Start() error {
|
||||||
func (d *DockerEnvironment) Stop() error {
|
func (d *DockerEnvironment) Stop() error {
|
||||||
t := time.Second * 10
|
t := time.Second * 10
|
||||||
|
|
||||||
|
d.Server.Emit(StatusEvent, ProcessStoppingState)
|
||||||
return d.Client.ContainerStop(context.Background(), d.Server.Uuid, &t)
|
return d.Client.ContainerStop(context.Background(), d.Server.Uuid, &t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +162,7 @@ func (d *DockerEnvironment) Terminate(signal os.Signal) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.Server.Emit(StatusEvent, ProcessStoppingState)
|
||||||
return d.Client.ContainerKill(ctx, d.Server.Uuid, signal.String())
|
return d.Client.ContainerKill(ctx, d.Server.Uuid, signal.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +198,7 @@ func (d *DockerEnvironment) Attach() error {
|
||||||
go func() {
|
go func() {
|
||||||
defer d.stream.Close()
|
defer d.stream.Close()
|
||||||
defer func() {
|
defer func() {
|
||||||
|
d.Server.Emit(StatusEvent, ProcessOfflineState)
|
||||||
d.attached = false
|
d.attached = false
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -232,7 +238,7 @@ func (d *DockerEnvironment) FollowConsoleOutput() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Err(); err != nil {
|
if err := s.Err(); err != nil {
|
||||||
zap.S().Errorw("error in scanner", zap.Error(err))
|
zap.S().Warnw("error processing scanner line in console output", zap.String("server", d.Server.Uuid), zap.Error(err))
|
||||||
}
|
}
|
||||||
}(reader)
|
}(reader)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ type EventListenerFunction *func(string)
|
||||||
// Defines all of the possible output events for a server.
|
// Defines all of the possible output events for a server.
|
||||||
const (
|
const (
|
||||||
ConsoleOutputEvent = "console"
|
ConsoleOutputEvent = "console"
|
||||||
|
StatusEvent = "status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Adds an event listener for the server instance.
|
// Adds an event listener for the server instance.
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/pterodactyl/wings/config"
|
"github.com/pterodactyl/wings/config"
|
||||||
"github.com/remeh/sizedwaitgroup"
|
"github.com/remeh/sizedwaitgroup"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -14,16 +15,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines states that identify if the server is running or not.
|
|
||||||
const (
|
|
||||||
StateOffline = "off"
|
|
||||||
StateStarting = "starting"
|
|
||||||
StateRunning = "running"
|
|
||||||
StateStopping = "stopping"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ProcessState string
|
|
||||||
|
|
||||||
// High level definition for a server instance being controlled by Wings.
|
// High level definition for a server instance being controlled by Wings.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
// The unique identifier for the server that should be used when referencing
|
// The unique identifier for the server that should be used when referencing
|
||||||
|
@ -36,7 +27,7 @@ type Server struct {
|
||||||
Suspended bool `json:"suspended"`
|
Suspended bool `json:"suspended"`
|
||||||
|
|
||||||
// The power state of the server.
|
// The power state of the server.
|
||||||
State ProcessState `json:"state"`
|
State string `json:"state"`
|
||||||
|
|
||||||
// The command that should be used when booting up the server instance.
|
// The command that should be used when booting up the server instance.
|
||||||
Invocation string `json:"invocation"`
|
Invocation string `json:"invocation"`
|
||||||
|
@ -190,7 +181,7 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
withConfiguration := func (e *DockerEnvironment) {
|
withConfiguration := func(e *DockerEnvironment) {
|
||||||
e.User = cfg.User.Uid
|
e.User = cfg.User.Uid
|
||||||
e.TimezonePath = cfg.TimezonePath
|
e.TimezonePath = cfg.TimezonePath
|
||||||
e.Server = s
|
e.Server = s
|
||||||
|
@ -207,7 +198,7 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Environment = env
|
s.Environment = env
|
||||||
s.Cache = cache.New(time.Minute * 10, time.Minute * 15)
|
s.Cache = cache.New(time.Minute*10, time.Minute*15)
|
||||||
s.Filesystem = &Filesystem{
|
s.Filesystem = &Filesystem{
|
||||||
Root: cfg.Data,
|
Root: cfg.Data,
|
||||||
Server: s,
|
Server: s,
|
||||||
|
@ -221,22 +212,6 @@ func (s *Server) ReadLogfile(len int64) ([]string, error) {
|
||||||
return s.Environment.Readlog(len)
|
return s.Environment.Readlog(len)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the state of the server process.
|
|
||||||
func (s *Server) SetState(state ProcessState) error {
|
|
||||||
switch state {
|
|
||||||
case StateOffline:
|
|
||||||
case StateRunning:
|
|
||||||
case StateStarting:
|
|
||||||
case StateStopping:
|
|
||||||
s.State = state
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return errors.New("invalid state provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the server is bootable in it's current state or not. This will not
|
// Determine if the server is bootable in it's current state or not. This will not
|
||||||
// indicate why a server is not bootable, only if it is.
|
// indicate why a server is not bootable, only if it is.
|
||||||
func (s *Server) IsBootable() bool {
|
func (s *Server) IsBootable() bool {
|
||||||
|
@ -249,4 +224,35 @@ func (s *Server) IsBootable() bool {
|
||||||
// for the server is setup, and that all of the necessary files are created.
|
// for the server is setup, and that all of the necessary files are created.
|
||||||
func (s *Server) CreateEnvironment() error {
|
func (s *Server) CreateEnvironment() error {
|
||||||
return s.Environment.Create()
|
return s.Environment.Create()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProcessOfflineState = "offline"
|
||||||
|
ProcessStartingState = "starting"
|
||||||
|
ProcessRunningState = "running"
|
||||||
|
ProcessStoppingState = "stopping"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
switch state {
|
||||||
|
case ProcessOfflineState:
|
||||||
|
case ProcessStartingState:
|
||||||
|
case ProcessRunningState:
|
||||||
|
case ProcessStoppingState:
|
||||||
|
s.State = state
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return errors.New(fmt.Sprintf("invalid server state received: %s", state))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the event to any listeners that are currently registered.
|
||||||
|
s.Emit(StatusEvent, state)
|
||||||
|
|
||||||
|
// @todo handle a crash event here. Need to port the logic from the Nodejs daemon
|
||||||
|
// into this daemon. I believe its basically just if state != stopping && newState = stopped
|
||||||
|
// then crashed.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
32
websocket.go
32
websocket.go
|
@ -12,6 +12,14 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SetStateEvent = "set state"
|
||||||
|
SendServerLogsEvent = "send logs"
|
||||||
|
SendCommandEvent = "send command"
|
||||||
|
ConsoleOutputEvent = "console output"
|
||||||
|
ServerStatusEvent = "status"
|
||||||
|
)
|
||||||
|
|
||||||
type WebsocketMessage struct {
|
type WebsocketMessage struct {
|
||||||
// The event to perform. Should be one of the following that are supported:
|
// The event to perform. Should be one of the following that are supported:
|
||||||
//
|
//
|
||||||
|
@ -46,18 +54,28 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
|
||||||
|
|
||||||
s := rt.Servers.Get(ps.ByName("server"))
|
s := rt.Servers.Get(ps.ByName("server"))
|
||||||
handler := WebsocketHandler{
|
handler := WebsocketHandler{
|
||||||
Server: s,
|
Server: s,
|
||||||
Mutex: sync.Mutex{},
|
Mutex: sync.Mutex{},
|
||||||
Connection: c,
|
Connection: c,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOutput := func(data string) {
|
handleOutput := func(data string) {
|
||||||
handler.SendJson(&WebsocketMessage{
|
handler.SendJson(&WebsocketMessage{
|
||||||
Event: "console output",
|
Event: ConsoleOutputEvent,
|
||||||
Args: []string{data},
|
Args: []string{data},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleServerStatus := func(data string) {
|
||||||
|
handler.SendJson(&WebsocketMessage{
|
||||||
|
Event: ServerStatusEvent,
|
||||||
|
Args: []string{data},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
s.AddListener(server.StatusEvent, &handleServerStatus)
|
||||||
|
defer s.RemoveListener(server.StatusEvent, &handleServerStatus)
|
||||||
|
|
||||||
s.AddListener(server.ConsoleOutputEvent, &handleOutput)
|
s.AddListener(server.ConsoleOutputEvent, &handleOutput)
|
||||||
defer s.RemoveListener(server.ConsoleOutputEvent, &handleOutput)
|
defer s.RemoveListener(server.ConsoleOutputEvent, &handleOutput)
|
||||||
|
|
||||||
|
@ -102,7 +120,7 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch m.Event {
|
switch m.Event {
|
||||||
case "set state":
|
case SetStateEvent:
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
switch strings.Join(m.Args, "") {
|
switch strings.Join(m.Args, "") {
|
||||||
|
@ -121,7 +139,7 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "send logs":
|
case SendServerLogsEvent:
|
||||||
{
|
{
|
||||||
logs, err := wsh.Server.Environment.Readlog(1024 * 5)
|
logs, err := wsh.Server.Environment.Readlog(1024 * 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,14 +148,14 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
|
||||||
|
|
||||||
for _, line := range logs {
|
for _, line := range logs {
|
||||||
wsh.SendJson(&WebsocketMessage{
|
wsh.SendJson(&WebsocketMessage{
|
||||||
Event: "console output",
|
Event: ConsoleOutputEvent,
|
||||||
Args: []string{line},
|
Args: []string{line},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case "send command":
|
case SendCommandEvent:
|
||||||
{
|
{
|
||||||
return wsh.Server.Environment.SendCommand(strings.Join(m.Args, ""))
|
return wsh.Server.Environment.SendCommand(strings.Join(m.Args, ""))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user