Emit server status events

This commit is contained in:
Dane Everitt 2019-04-21 12:02:28 -07:00
parent bed30d9229
commit 8795e7d739
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
4 changed files with 70 additions and 39 deletions

View File

@ -120,6 +120,7 @@ func (d *DockerEnvironment) Start() error {
// No reason to try starting a container that is already running.
if c.State.Running {
d.Server.Emit(StatusEvent, ProcessRunningState)
if !d.attached {
return d.Attach()
}
@ -129,7 +130,9 @@ func (d *DockerEnvironment) Start() error {
opts := types.ContainerStartOptions{}
d.Server.Emit(StatusEvent, ProcessStartingState)
if err := d.Client.ContainerStart(context.Background(), d.Server.Uuid, opts); err != nil {
d.Server.Emit(StatusEvent, ProcessOfflineState)
return err
}
@ -142,6 +145,7 @@ func (d *DockerEnvironment) Start() error {
func (d *DockerEnvironment) Stop() error {
t := time.Second * 10
d.Server.Emit(StatusEvent, ProcessStoppingState)
return d.Client.ContainerStop(context.Background(), d.Server.Uuid, &t)
}
@ -158,6 +162,7 @@ func (d *DockerEnvironment) Terminate(signal os.Signal) error {
return nil
}
d.Server.Emit(StatusEvent, ProcessStoppingState)
return d.Client.ContainerKill(ctx, d.Server.Uuid, signal.String())
}
@ -193,6 +198,7 @@ func (d *DockerEnvironment) Attach() error {
go func() {
defer d.stream.Close()
defer func() {
d.Server.Emit(StatusEvent, ProcessOfflineState)
d.attached = false
}()
@ -232,7 +238,7 @@ func (d *DockerEnvironment) FollowConsoleOutput() error {
}
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)

View File

@ -7,6 +7,7 @@ type EventListenerFunction *func(string)
// Defines all of the possible output events for a server.
const (
ConsoleOutputEvent = "console"
StatusEvent = "status"
)
// Adds an event listener for the server instance.

View File

@ -1,8 +1,9 @@
package server
import (
"errors"
"fmt"
"github.com/patrickmn/go-cache"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config"
"github.com/remeh/sizedwaitgroup"
"go.uber.org/zap"
@ -14,16 +15,6 @@ import (
"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.
type Server struct {
// The unique identifier for the server that should be used when referencing
@ -36,7 +27,7 @@ type Server struct {
Suspended bool `json:"suspended"`
// 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.
Invocation string `json:"invocation"`
@ -190,7 +181,7 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e
return nil, err
}
withConfiguration := func (e *DockerEnvironment) {
withConfiguration := func(e *DockerEnvironment) {
e.User = cfg.User.Uid
e.TimezonePath = cfg.TimezonePath
e.Server = s
@ -207,7 +198,7 @@ func FromConfiguration(data []byte, cfg *config.SystemConfiguration) (*Server, e
}
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{
Root: cfg.Data,
Server: s,
@ -221,22 +212,6 @@ func (s *Server) ReadLogfile(len int64) ([]string, error) {
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
// indicate why a server is not bootable, only if it is.
func (s *Server) IsBootable() bool {
@ -250,3 +225,34 @@ func (s *Server) IsBootable() bool {
func (s *Server) CreateEnvironment() error {
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
}

View File

@ -12,6 +12,14 @@ import (
"sync"
)
const (
SetStateEvent = "set state"
SendServerLogsEvent = "send logs"
SendCommandEvent = "send command"
ConsoleOutputEvent = "console output"
ServerStatusEvent = "status"
)
type WebsocketMessage struct {
// 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"))
handler := WebsocketHandler{
Server: s,
Mutex: sync.Mutex{},
Server: s,
Mutex: sync.Mutex{},
Connection: c,
}
handleOutput := func(data string) {
handler.SendJson(&WebsocketMessage{
Event: "console output",
Event: ConsoleOutputEvent,
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)
defer s.RemoveListener(server.ConsoleOutputEvent, &handleOutput)
@ -102,7 +120,7 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
}
switch m.Event {
case "set state":
case SetStateEvent:
{
var err error
switch strings.Join(m.Args, "") {
@ -121,7 +139,7 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
return err
}
}
case "send logs":
case SendServerLogsEvent:
{
logs, err := wsh.Server.Environment.Readlog(1024 * 5)
if err != nil {
@ -130,14 +148,14 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
for _, line := range logs {
wsh.SendJson(&WebsocketMessage{
Event: "console output",
Event: ConsoleOutputEvent,
Args: []string{line},
})
}
return nil
}
case "send command":
case SendCommandEvent:
{
return wsh.Server.Environment.SendCommand(strings.Join(m.Args, ""))
}