Merge branch 'develop' into v2
This commit is contained in:
commit
9e98287172
2
.github/workflows/build-test.yml
vendored
2
.github/workflows/build-test.yml
vendored
|
@ -60,7 +60,7 @@ jobs:
|
|||
run: go test ./...
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.go == '^1.16' && (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') }}
|
||||
if: ${{ github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
name: wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
path: build/wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
# Changelog
|
||||
|
||||
## v1.5.1
|
||||
### Added
|
||||
* Global configuration option for toggling server crash detection (`system.crash_detection.enabled`)
|
||||
* RPM specfile
|
||||
|
||||
## v1.5.0
|
||||
### Fixed
|
||||
* Fixes a race condition when setting the application name in the console output for a server.
|
||||
* Fixes a server being reinstalled causing the `file_denylist` parameter for an Egg to be ignored until Wings is restarted.
|
||||
* Fixes YAML file parser not correctly setting boolean values.
|
||||
* Fixes potential issue where the underlying websocket connection is closed but the parent request context is not yet canceled causing a write over a closed connection.
|
||||
* Fixes race condition when closing all active websocket connections when a server is deleted.
|
||||
* Fixes logic to determine if a server's context is closed out and send a websocket close message to connected clients. Previously this fired off whenever the request itself was closed, and not when the server context was closed.
|
||||
|
||||
### Added
|
||||
* Exposes `8080` in the default Docker setup to better support proxy tools.
|
||||
|
|
15
cmd/root.go
15
cmd/root.go
|
@ -1,6 +1,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -209,7 +210,17 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
|||
st = state
|
||||
}
|
||||
|
||||
r, err := s.Environment.IsRunning()
|
||||
// Use a timed context here to avoid booting issues where Docker hangs for a
|
||||
// specific container that would cause Wings to be un-bootable until the entire
|
||||
// machine is rebooted. It is much better for us to just have a single failed
|
||||
// server instance than an entire offline node.
|
||||
//
|
||||
// @see https://github.com/pterodactyl/panel/issues/2475
|
||||
// @see https://github.com/pterodactyl/panel/issues/3358
|
||||
ctx, cancel := context.WithTimeout(cmd.Context(), time.Second * 30)
|
||||
defer cancel()
|
||||
|
||||
r, err := s.Environment.IsRunning(ctx)
|
||||
// We ignore missing containers because we don't want to actually block booting of wings at this
|
||||
// point. If we didn't do this, and you pruned all the images and then started wings you could
|
||||
// end up waiting a long period of time for all the images to be re-pulled on Wings boot rather
|
||||
|
@ -238,7 +249,7 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
|
|||
s.Log().Info("detected server is running, re-attaching to process...")
|
||||
|
||||
s.Environment.SetState(environment.ProcessRunningState)
|
||||
if err := s.Environment.Attach(); err != nil {
|
||||
if err := s.Environment.Attach(ctx); err != nil {
|
||||
s.Log().WithField("error", err).Warn("failed to attach to running server environment")
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -48,10 +48,12 @@ var DefaultTLSConfig = &tls.Config{
|
|||
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
|
||||
}
|
||||
|
||||
var mu sync.RWMutex
|
||||
var _config *Configuration
|
||||
var _jwtAlgo *jwt.HMACSHA
|
||||
var _debugViaFlag bool
|
||||
var (
|
||||
mu sync.RWMutex
|
||||
_config *Configuration
|
||||
_jwtAlgo *jwt.HMACSHA
|
||||
_debugViaFlag bool
|
||||
)
|
||||
|
||||
// Locker specific to writing the configuration to the disk, this happens
|
||||
// in areas that might already be locked, so we don't want to crash the process.
|
||||
|
@ -181,6 +183,9 @@ type SystemConfiguration struct {
|
|||
}
|
||||
|
||||
type CrashDetection struct {
|
||||
// CrashDetectionEnabled sets if crash detection is enabled globally for all servers on this node.
|
||||
CrashDetectionEnabled bool `default:"true" yaml:"enabled"`
|
||||
|
||||
// Determines if Wings should detect a server that stops with a normal exit code of
|
||||
// "0" as being crashed if the process stopped without any Wings interaction. E.g.
|
||||
// the user did not press the stop button, but the process stopped cleanly.
|
||||
|
@ -375,7 +380,7 @@ func WriteToDisk(c *Configuration) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(c.path, b, 0600); err != nil {
|
||||
if err := ioutil.WriteFile(c.path, b, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -470,7 +475,7 @@ func FromFile(path string) error {
|
|||
func ConfigureDirectories() error {
|
||||
root := _config.System.RootDirectory
|
||||
log.WithField("path", root).Debug("ensuring root data directory exists")
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
if err := os.MkdirAll(root, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -491,17 +496,17 @@ func ConfigureDirectories() error {
|
|||
}
|
||||
|
||||
log.WithField("path", _config.System.Data).Debug("ensuring server data directory exists")
|
||||
if err := os.MkdirAll(_config.System.Data, 0700); err != nil {
|
||||
if err := os.MkdirAll(_config.System.Data, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithField("path", _config.System.ArchiveDirectory).Debug("ensuring archive data directory exists")
|
||||
if err := os.MkdirAll(_config.System.ArchiveDirectory, 0700); err != nil {
|
||||
if err := os.MkdirAll(_config.System.ArchiveDirectory, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithField("path", _config.System.BackupDirectory).Debug("ensuring backup data directory exists")
|
||||
if err := os.MkdirAll(_config.System.BackupDirectory, 0700); err != nil {
|
||||
if err := os.MkdirAll(_config.System.BackupDirectory, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ func (nw noopWriter) Write(b []byte) (int, error) {
|
|||
// Calling this function will poll resources for the container in the background
|
||||
// until the provided context is canceled by the caller. Failure to cancel said
|
||||
// context will cause background memory leaks as the goroutine will not exit.
|
||||
func (e *Environment) Attach() error {
|
||||
func (e *Environment) Attach(ctx context.Context) error {
|
||||
if e.IsAttached() {
|
||||
return nil
|
||||
}
|
||||
|
@ -62,14 +62,17 @@ func (e *Environment) Attach() error {
|
|||
}
|
||||
|
||||
// Set the stream again with the container.
|
||||
if st, err := e.client.ContainerAttach(context.Background(), e.Id, opts); err != nil {
|
||||
if st, err := e.client.ContainerAttach(ctx, e.Id, opts); err != nil {
|
||||
return err
|
||||
} else {
|
||||
e.SetStream(&st)
|
||||
}
|
||||
|
||||
go func() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
// Don't use the context provided to the function, that'll cause the polling to
|
||||
// exit unexpectedly. We want a custom context for this, the one passed to the
|
||||
// function is to avoid a hang situation when trying to attach to a container.
|
||||
pollCtx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
defer e.stream.Close()
|
||||
defer func() {
|
||||
|
@ -78,7 +81,7 @@ func (e *Environment) Attach() error {
|
|||
}()
|
||||
|
||||
go func() {
|
||||
if err := e.pollResources(ctx); err != nil {
|
||||
if err := e.pollResources(pollCtx); err != nil {
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
e.log().WithField("error", err).Error("error during environment resource polling")
|
||||
} else {
|
||||
|
|
|
@ -128,20 +128,20 @@ func (e *Environment) Exists() (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Determines if the server's docker container is currently running. If there is no container
|
||||
// present, an error will be raised (since this shouldn't be a case that ever happens under
|
||||
// correctly developed circumstances).
|
||||
// IsRunning determines if the server's docker container is currently running.
|
||||
// If there is no container present, an error will be raised (since this
|
||||
// shouldn't be a case that ever happens under correctly developed
|
||||
// circumstances).
|
||||
//
|
||||
// You can confirm if the instance wasn't found by using client.IsErrNotFound from the Docker
|
||||
// API.
|
||||
// You can confirm if the instance wasn't found by using client.IsErrNotFound
|
||||
// from the Docker API.
|
||||
//
|
||||
// @see docker/client/errors.go
|
||||
func (e *Environment) IsRunning() (bool, error) {
|
||||
c, err := e.client.ContainerInspect(context.Background(), e.Id)
|
||||
func (e *Environment) IsRunning(ctx context.Context) (bool, error) {
|
||||
c, err := e.client.ContainerInspect(ctx, e.Id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return c.State.Running, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -17,16 +17,17 @@ import (
|
|||
"github.com/pterodactyl/wings/remote"
|
||||
)
|
||||
|
||||
// Run before the container starts and get the process configuration from the Panel.
|
||||
// This is important since we use this to check configuration files as well as ensure
|
||||
// we always have the latest version of an egg available for server processes.
|
||||
// OnBeforeStart run before the container starts and get the process
|
||||
// configuration from the Panel. This is important since we use this to check
|
||||
// configuration files as well as ensure we always have the latest version of
|
||||
// an egg available for server processes.
|
||||
//
|
||||
// This process will also confirm that the server environment exists and is in a bootable
|
||||
// state. This ensures that unexpected container deletion while Wings is running does
|
||||
// not result in the server becoming un-bootable.
|
||||
func (e *Environment) OnBeforeStart() error {
|
||||
// This process will also confirm that the server environment exists and is in
|
||||
// a bootable state. This ensures that unexpected container deletion while Wings
|
||||
// is running does not result in the server becoming un-bootable.
|
||||
func (e *Environment) OnBeforeStart(ctx context.Context) error {
|
||||
// Always destroy and re-create the server container to ensure that synced data from the Panel is used.
|
||||
if err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil {
|
||||
if err := e.client.ContainerRemove(ctx, e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil {
|
||||
if !client.IsErrNotFound(err) {
|
||||
return errors.WrapIf(err, "environment/docker: failed to remove container during pre-boot")
|
||||
}
|
||||
|
@ -46,10 +47,10 @@ func (e *Environment) OnBeforeStart() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Starts the server environment and begins piping output to the event listeners for the
|
||||
// console. If a container does not exist, or needs to be rebuilt that will happen in the
|
||||
// call to OnBeforeStart().
|
||||
func (e *Environment) Start() error {
|
||||
// Start will start the server environment and begins piping output to the event
|
||||
// listeners for the console. If a container does not exist, or needs to be
|
||||
// rebuilt that will happen in the call to OnBeforeStart().
|
||||
func (e *Environment) Start(ctx context.Context) error {
|
||||
sawError := false
|
||||
|
||||
// If sawError is set to true there was an error somewhere in the pipeline that
|
||||
|
@ -65,7 +66,7 @@ func (e *Environment) Start() error {
|
|||
}
|
||||
}()
|
||||
|
||||
if c, err := e.client.ContainerInspect(context.Background(), e.Id); err != nil {
|
||||
if c, err := e.client.ContainerInspect(ctx, e.Id); err != nil {
|
||||
// Do nothing if the container is not found, we just don't want to continue
|
||||
// to the next block of code here. This check was inlined here to guard against
|
||||
// a nil-pointer when checking c.State below.
|
||||
|
@ -79,7 +80,7 @@ func (e *Environment) Start() error {
|
|||
if c.State.Running {
|
||||
e.SetState(environment.ProcessRunningState)
|
||||
|
||||
return e.Attach()
|
||||
return e.Attach(ctx)
|
||||
}
|
||||
|
||||
// Truncate the log file, so we don't end up outputting a bunch of useless log information
|
||||
|
@ -101,21 +102,23 @@ func (e *Environment) Start() error {
|
|||
// Run the before start function and wait for it to finish. This will validate that the container
|
||||
// exists on the system, and rebuild the container if that is required for server booting to
|
||||
// occur.
|
||||
if err := e.OnBeforeStart(); err != nil {
|
||||
if err := e.OnBeforeStart(ctx); err != nil {
|
||||
return errors.WithStackIf(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
// If we cannot start & attach to the container in 30 seconds something has gone
|
||||
// quite sideways and we should stop trying to avoid a hanging situation.
|
||||
actx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
if err := e.client.ContainerStart(ctx, e.Id, types.ContainerStartOptions{}); err != nil {
|
||||
if err := e.client.ContainerStart(actx, e.Id, types.ContainerStartOptions{}); err != nil {
|
||||
return errors.WrapIf(err, "environment/docker: failed to start container")
|
||||
}
|
||||
|
||||
// No errors, good to continue through.
|
||||
sawError = false
|
||||
|
||||
return e.Attach()
|
||||
return e.Attach(actx)
|
||||
}
|
||||
|
||||
// Stop stops the container that the server is running in. This will allow up to
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package environment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/pterodactyl/wings/events"
|
||||
|
@ -41,9 +42,9 @@ type ProcessEnvironment interface {
|
|||
// a basic CLI environment this can probably just return true right away.
|
||||
Exists() (bool, error)
|
||||
|
||||
// Determines if the environment is currently active and running a server process
|
||||
// for this specific server instance.
|
||||
IsRunning() (bool, error)
|
||||
// IsRunning determines if the environment is currently active and running
|
||||
// a server process for this specific server instance.
|
||||
IsRunning(ctx context.Context) (bool, error)
|
||||
|
||||
// Performs an update of server resource limits without actually stopping the server
|
||||
// process. This only executes if the environment supports it, otherwise it is
|
||||
|
@ -52,11 +53,11 @@ type ProcessEnvironment interface {
|
|||
|
||||
// Runs before the environment is started. If an error is returned starting will
|
||||
// not occur, otherwise proceeds as normal.
|
||||
OnBeforeStart() error
|
||||
OnBeforeStart(ctx context.Context) error
|
||||
|
||||
// Starts a server instance. If the server instance is not in a state where it
|
||||
// can be started an error should be returned.
|
||||
Start() error
|
||||
Start(ctx context.Context) error
|
||||
|
||||
// Stops a server instance. If the server is already stopped an error should
|
||||
// not be returned.
|
||||
|
@ -84,10 +85,10 @@ type ProcessEnvironment interface {
|
|||
// server.
|
||||
Create() error
|
||||
|
||||
// Attaches to the server console environment and allows piping the output to a
|
||||
// websocket or other internal tool to monitor output. Also allows you to later
|
||||
// Attach attaches to the server console environment and allows piping the output
|
||||
// to a websocket or other internal tool to monitor output. Also allows you to later
|
||||
// send data into the environment's stdin.
|
||||
Attach() error
|
||||
Attach(ctx context.Context) error
|
||||
|
||||
// Sends the provided command to the running server instance.
|
||||
SendCommand(string) error
|
||||
|
|
|
@ -101,7 +101,7 @@ func postServerPower(c *gin.Context) {
|
|||
func postServerCommands(c *gin.Context) {
|
||||
s := ExtractServer(c)
|
||||
|
||||
if running, err := s.Environment.IsRunning(); err != nil {
|
||||
if running, err := s.Environment.IsRunning(c.Request.Context()); err != nil {
|
||||
NewServerError(err, s).Abort(c)
|
||||
return
|
||||
} else if !running {
|
||||
|
|
|
@ -12,6 +12,14 @@ import (
|
|||
"github.com/pterodactyl/wings/router/websocket"
|
||||
)
|
||||
|
||||
var expectedCloseCodes = []int{
|
||||
ws.CloseGoingAway,
|
||||
ws.CloseAbnormalClosure,
|
||||
ws.CloseNormalClosure,
|
||||
ws.CloseNoStatusReceived,
|
||||
ws.CloseServiceRestart,
|
||||
}
|
||||
|
||||
// Upgrades a connection to a websocket and passes events along between.
|
||||
func getServerWebsocket(c *gin.Context) {
|
||||
manager := middleware.ExtractManager(c)
|
||||
|
@ -24,8 +32,10 @@ func getServerWebsocket(c *gin.Context) {
|
|||
defer handler.Connection.Close()
|
||||
|
||||
// Create a context that can be canceled when the user disconnects from this
|
||||
// socket that will also cancel listeners running in separate threads.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
// socket that will also cancel listeners running in separate threads. If the
|
||||
// connection itself is terminated listeners using this context will also be
|
||||
// closed.
|
||||
ctx, cancel := context.WithCancel(c.Request.Context())
|
||||
defer cancel()
|
||||
|
||||
// Track this open connection on the server so that we can close them all programmatically
|
||||
|
@ -33,22 +43,19 @@ func getServerWebsocket(c *gin.Context) {
|
|||
s.Websockets().Push(handler.Uuid(), &cancel)
|
||||
defer s.Websockets().Remove(handler.Uuid())
|
||||
|
||||
// Listen for the context being canceled and then close the websocket connection. This normally
|
||||
// just happens because you're disconnecting from the socket in the browser, however in some
|
||||
// cases we close the connections programmatically (e.g. deleting the server) and need to send
|
||||
// a close message to the websocket so it disconnects.
|
||||
go func(ctx context.Context, c *ws.Conn) {
|
||||
ListenerLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
handler.Connection.WriteControl(ws.CloseMessage, ws.FormatCloseMessage(ws.CloseGoingAway, "server deleted"), time.Now().Add(time.Second*5))
|
||||
// A break right here without defining the specific loop would only break the select
|
||||
// and not actually break the for loop, thus causing this routine to stick around forever.
|
||||
break ListenerLoop
|
||||
}
|
||||
// If the server is deleted we need to send a close message to the connected client
|
||||
// so that they disconnect since there will be no more events sent along. Listen for
|
||||
// the request context being closed to break this loop, otherwise this routine will
|
||||
// be left hanging in the background.
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
case <-s.Context().Done():
|
||||
handler.Connection.WriteControl(ws.CloseMessage, ws.FormatCloseMessage(ws.CloseGoingAway, "server deleted"), time.Now().Add(time.Second*5))
|
||||
break
|
||||
}
|
||||
}(ctx, handler.Connection)
|
||||
}()
|
||||
|
||||
go handler.ListenForServerEvents(ctx)
|
||||
go handler.ListenForExpiration(ctx)
|
||||
|
@ -58,14 +65,7 @@ func getServerWebsocket(c *gin.Context) {
|
|||
|
||||
_, p, err := handler.Connection.ReadMessage()
|
||||
if err != nil {
|
||||
if !ws.IsCloseError(
|
||||
err,
|
||||
ws.CloseNormalClosure,
|
||||
ws.CloseGoingAway,
|
||||
ws.CloseNoStatusReceived,
|
||||
ws.CloseServiceRestart,
|
||||
ws.CloseAbnormalClosure,
|
||||
) {
|
||||
if ws.IsUnexpectedCloseError(err, expectedCloseCodes...) {
|
||||
s.Log().WithField("error", err).Warn("error handling websocket message for server")
|
||||
}
|
||||
break
|
||||
|
|
|
@ -8,16 +8,14 @@ import (
|
|||
"github.com/pterodactyl/wings/server"
|
||||
)
|
||||
|
||||
// Checks the time to expiration on the JWT every 30 seconds until the token has
|
||||
// expired. If we are within 3 minutes of the token expiring, send a notice over
|
||||
// the socket that it is expiring soon. If it has expired, send that notice as well.
|
||||
// ListenForExpiration checks the time to expiration on the JWT every 30 seconds
|
||||
// until the token has expired. If we are within 3 minutes of the token expiring,
|
||||
// send a notice over the socket that it is expiring soon. If it has expired,
|
||||
// send that notice as well.
|
||||
func (h *Handler) ListenForExpiration(ctx context.Context) {
|
||||
// Make a ticker and completion channel that is used to continuously poll the
|
||||
// JWT stored in the session to send events to the socket when it is expiring.
|
||||
ticker := time.NewTicker(time.Second * 30)
|
||||
|
||||
// Whenever this function is complete, end the ticker, close out the channel,
|
||||
// and then close the websocket connection.
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
|
@ -51,8 +49,9 @@ var e = []string{
|
|||
server.TransferStatusEvent,
|
||||
}
|
||||
|
||||
// Listens for different events happening on a server and sends them along
|
||||
// to the connected websocket.
|
||||
// ListenForServerEvents will listen for different events happening on a server
|
||||
// and send them along to the connected websocket client. This function will
|
||||
// block until the context provided to it is canceled.
|
||||
func (h *Handler) ListenForServerEvents(ctx context.Context) {
|
||||
h.server.Log().Debug("listening for server events over websocket")
|
||||
callback := func(e events.Event) {
|
||||
|
@ -67,13 +66,10 @@ func (h *Handler) ListenForServerEvents(ctx context.Context) {
|
|||
h.server.Events().On(evt, &callback)
|
||||
}
|
||||
|
||||
go func(ctx context.Context) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Once this context is stopped, de-register all of the listeners that have been registered.
|
||||
for _, evt := range e {
|
||||
h.server.Events().Off(evt, &callback)
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
<-ctx.Done()
|
||||
// Block until the context is stopped and then de-register all of the event listeners
|
||||
// that we registered earlier.
|
||||
for _, evt := range e {
|
||||
h.server.Events().Off(evt, &callback)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -368,7 +368,9 @@ func (h *Handler) HandleInbound(m Message) error {
|
|||
}
|
||||
case SendServerLogsEvent:
|
||||
{
|
||||
if running, _ := h.server.Environment.IsRunning(); !running {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)
|
||||
defer cancel()
|
||||
if running, _ := h.server.Environment.IsRunning(ctx); !running {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
114
rpm/ptero-wings.spec
Normal file
114
rpm/ptero-wings.spec
Normal file
|
@ -0,0 +1,114 @@
|
|||
Name: ptero-wings
|
||||
Version: 1.5.0
|
||||
Release: 1%{?dist}
|
||||
Summary: The server control plane for Pterodactyl Panel. Written from the ground-up with security, speed, and stability in mind.
|
||||
BuildArch: x86_64
|
||||
License: MIT
|
||||
URL: https://github.com/pterodactyl/wings
|
||||
Source0: https://github.com/pterodactyl/wings/releases/download/v%{version}/wings_linux_amd64
|
||||
|
||||
%if 0%{?rhel} && 0%{?rhel} <= 8
|
||||
BuildRequires: systemd
|
||||
%else
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
|
||||
|
||||
%description
|
||||
Wings is Pterodactyl's server control plane, built for the rapidly
|
||||
changing gaming industry and designed to be highly performant and
|
||||
secure. Wings provides an HTTP API allowing you to interface directly
|
||||
with running server instances, fetch server logs, generate backups,
|
||||
and control all aspects of the server lifecycle.
|
||||
|
||||
In addition, Wings ships with a built-in SFTP server allowing your
|
||||
system to remain free of Pterodactyl specific dependencies, and
|
||||
allowing users to authenticate with the same credentials they would
|
||||
normally use to access the Panel.
|
||||
|
||||
%prep
|
||||
|
||||
%build
|
||||
#nothing required
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}%{_bindir}
|
||||
mkdir -p %{buildroot}%{_unitdir}
|
||||
cp %{_sourcedir}/wings_linux_amd64 %{buildroot}%{_bindir}/wings
|
||||
|
||||
cat > %{buildroot}%{_unitdir}/wings.service << EOF
|
||||
[Unit]
|
||||
Description=Pterodactyl Wings Daemon
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
PartOf=docker.service
|
||||
StartLimitIntervalSec=600
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/etc/pterodactyl
|
||||
ExecStart=/usr/bin/wings
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
StartLimitInterval=180
|
||||
StartLimitBurst=30
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
%files
|
||||
%attr(0755, root, root) %{_prefix}/bin/wings
|
||||
%attr(0644, root, root) %{_unitdir}/wings.service
|
||||
|
||||
%post
|
||||
|
||||
# Reload systemd
|
||||
systemctl daemon-reload
|
||||
|
||||
# Create the required directory structure
|
||||
mkdir -p /etc/pterodactyl
|
||||
mkdir -p /var/lib/pterodactyl/{archives,backups,volumes}
|
||||
mkdir -p /var/log/pterodactyl/install
|
||||
|
||||
%preun
|
||||
|
||||
systemctl is-active %{name} >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl stop %{name}
|
||||
fi
|
||||
|
||||
systemctl is-enabled %{name} >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
systemctl disable %{name}
|
||||
fi
|
||||
|
||||
%postun
|
||||
rm -rf /var/log/pterodactyl
|
||||
|
||||
%verifyscript
|
||||
|
||||
wings --version
|
||||
|
||||
%changelog
|
||||
* Sun Sep 12 2021 Capitol Hosting Solutions Systems Engineering <syseng@chs.gg> - 1.5.0-1
|
||||
- specfile by Capitol Hosting Solutions, Upstream by Pterodactyl
|
||||
- Rebased for https://github.com/pterodactyl/wings/releases/tag/v1.5.0
|
||||
- Fixes a race condition when setting the application name in the console output for a server.
|
||||
- Fixes a server being reinstalled causing the file_denylist parameter for an Egg to be ignored until Wings is restarted.
|
||||
- Fixes YAML file parser not correctly setting boolean values.
|
||||
- Fixes potential issue where the underlying websocket connection is closed but the parent request context is not yet canceled causing a write over a closed connection.
|
||||
- Fixes race condition when closing all active websocket connections when a server is deleted.
|
||||
- Fixes logic to determine if a server's context is closed out and send a websocket close message to connected clients. Previously this fired off whenever the request itself was closed, and not when the server context was closed.
|
||||
- Exposes 8080 in the wings Dockerfile to better support reverse proxy tools.
|
||||
- Releases are now built using Go 1.17 — the minimum version required to build Wings remains Go 1.16.
|
||||
- Simplifed the logic powering server updates to only pull information from the Panel rather than trying to accept updated values. All parts of Wings needing the most up-to-date server details should call Server#Sync() to fetch the latest stored build information.
|
||||
- Installer#New() no longer requires passing all of the server data as a byte slice, rather a new Installer#ServerDetails struct is exposed which can be passed and accepts a UUID and if the server should be started after the installer finishes.
|
||||
- Removes complicated (and unused) logic during the server installation process that was a hold-over from legacy Wings architectures.
|
||||
- Removes the PATCH /api/servers/:server endpoint — if you were previously using this API call it should be replaced with POST /api/servers/:server/sync.
|
||||
|
||||
* Wed Aug 25 2021 Capitol Hosting Solutions Systems Engineering <syseng@chs.gg> - 1.4.7-1
|
||||
- specfile by Capitol Hosting Solutions, Upstream by Pterodactyl
|
||||
- Rebased for https://github.com/pterodactyl/wings/releases/tag/v1.4.7
|
||||
- SFTP access is now properly denied if a server is suspended.
|
||||
- Correctly uses start_on_completion and crash_detection_enabled for servers.
|
|
@ -128,7 +128,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
|
|||
return err
|
||||
}
|
||||
|
||||
return s.Environment.Start()
|
||||
return s.Environment.Start(s.Context())
|
||||
case PowerActionStop:
|
||||
// We're specifically waiting for the process to be stopped here, otherwise the lock is released
|
||||
// too soon, and you can rack up all sorts of issues.
|
||||
|
@ -151,7 +151,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
|
|||
return err
|
||||
}
|
||||
|
||||
return s.Environment.Start()
|
||||
return s.Environment.Start(s.Context())
|
||||
case PowerActionTerminate:
|
||||
return s.Environment.Terminate(os.Kill)
|
||||
}
|
||||
|
|
|
@ -188,7 +188,9 @@ func (s *Server) Sync() error {
|
|||
// can be called from scoped where the server may not be fully initialized,
|
||||
// therefore other things like the filesystem and environment may not exist yet.
|
||||
func (s *Server) SyncWithConfiguration(cfg remote.ServerConfigurationResponse) error {
|
||||
c := Configuration{}
|
||||
c := Configuration{
|
||||
CrashDetectionEnabled: config.Get().System.CrashDetection.CrashDetectionEnabled,
|
||||
}
|
||||
if err := json.Unmarshal(cfg.Settings, &c); err != nil {
|
||||
return errors.WithStackIf(err)
|
||||
}
|
||||
|
@ -196,9 +198,9 @@ func (s *Server) SyncWithConfiguration(cfg remote.ServerConfigurationResponse) e
|
|||
s.cfg.mu.Lock()
|
||||
defer s.cfg.mu.Unlock()
|
||||
|
||||
// Lock the new configuration. Since we have the defered Unlock above we need
|
||||
// Lock the new configuration. Since we have the deferred Unlock above we need
|
||||
// to make sure that the NEW configuration object is already locked since that
|
||||
// defer is running on the memory address for "s.cfg.mu" which we're explcitly
|
||||
// defer is running on the memory address for "s.cfg.mu" which we're explicitly
|
||||
// changing on the next line.
|
||||
c.mu.Lock()
|
||||
|
||||
|
@ -259,7 +261,7 @@ func (s *Server) EnsureDataDirectoryExists() error {
|
|||
if _, err := os.Lstat(s.fs.Path()); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
s.Log().Debug("server: creating root directory and setting permissions")
|
||||
if err := os.MkdirAll(s.fs.Path(), 0700); err != nil {
|
||||
if err := os.MkdirAll(s.fs.Path(), 0o700); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if err := s.fs.Chown("/"); err != nil {
|
||||
|
|
|
@ -44,11 +44,11 @@ func (w *WebsocketBag) Remove(u uuid.UUID) {
|
|||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
// CancelAll cancels all the stored cancel functions which has the effect of disconnecting
|
||||
// every listening websocket for the server.
|
||||
// CancelAll cancels all the stored cancel functions which has the effect of
|
||||
// disconnecting every listening websocket for the server.
|
||||
func (w *WebsocketBag) CancelAll() {
|
||||
w.mu.Lock()
|
||||
w.mu.Unlock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if w.conns != nil {
|
||||
for _, cancel := range w.conns {
|
||||
|
|
Loading…
Reference in New Issue
Block a user