Compare commits

..

1 Commits

Author SHA1 Message Date
Pterodactyl CI
ac6acf27ed bump version for release 2024-05-08 04:17:27 +00:00
17 changed files with 86 additions and 197 deletions

View File

@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-22.04]
go: ["1.22.5"]
go: ["1.21.9", "1.22.2"]
goos: [linux]
goarch: [amd64, arm64]
@ -62,14 +62,14 @@ jobs:
- name: Upload Release Artifact
uses: actions/upload-artifact@v4
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.22.5' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.21.8' }}
with:
name: wings_linux_${{ matrix.goarch }}
path: dist/wings
- name: Upload Debug Artifact
uses: actions/upload-artifact@v4
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.22.5' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.21.8' }}
with:
name: wings_linux_${{ matrix.goarch }}_debug
path: dist/wings_debug

View File

@ -17,7 +17,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22.5"
go-version: "1.21.9"
- name: Build release binaries
env:

View File

@ -1,15 +1,5 @@
# Changelog
## v1.11.14
### Added
* Support relative file paths for the Wings config ([#180](https://github.com/pterodactyl/wings/pull/180))
### Fixed
* Folders not being sorted before files properly ([#5078](https://github.com/pterodactyl/panel/issues/5078)
## v1.11.13
### Fixed

View File

@ -1,5 +1,5 @@
# Stage 1 (Build)
FROM golang:1.22.5-alpine AS builder
FROM golang:1.21.9-alpine AS builder
ARG VERSION
RUN apk add --update --no-cache git make

View File

@ -19,9 +19,8 @@ I would like to extend my sincere thanks to the following sponsors for helping f
[Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi)
| Company | About |
|--------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|--------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**CodeNode LLC**](https://codenode.gg/) | Looking for simplicity? Well, look no further! CodeNode has got you covered with everything you need at the rock-bottom price of $1.75 per GB, including dedicated IPs in Dallas, Texas, and Amsterdam, Netherlands. We're not just good, we're the best in the game! |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**HostEZ**](https://hostez.io) | US & EU Rust & Minecraft Hosting. DDoS Protected bare metal, VPS and colocation with low latency, high uptime and maximum availability. EZ! |

View File

@ -13,6 +13,7 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/NYTimes/logrotate"
@ -112,9 +113,6 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
if err := config.EnsurePterodactylUser(); err != nil {
log.WithField("error", err).Fatal("failed to create pterodactyl system user")
}
if err := config.ConfigurePasswd(); err != nil {
log.WithField("error", err).Fatal("failed to configure container passwd file")
}
log.WithFields(log.Fields{
"username": config.Get().System.Username,
"uid": config.Get().System.User.Uid,
@ -381,14 +379,13 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
// Reads the configuration from the disk and then sets up the global singleton
// with all the configuration values.
func initConfig() {
if !filepath.IsAbs(configPath) {
d, err := filepath.Abs(configPath)
if !strings.HasPrefix(configPath, "/") {
d, err := os.Getwd()
if err != nil {
log2.Fatalf("cmd/root: failed to get path to config file: %s", err)
log2.Fatalf("cmd/root: could not determine directory: %s", err)
}
configPath = d
configPath = path.Clean(path.Join(d, configPath))
}
err := config.FromFile(configPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
@ -443,18 +440,18 @@ in all copies or substantial portions of the Software.%s`), system.Version, time
}
func exitWithConfigurationNotice() {
fmt.Printf(colorstring.Color(`
fmt.Print(colorstring.Color(`
[_red_][white][bold]Error: Configuration File Not Found[reset]
Wings was not able to locate your configuration file, and therefore is not
able to complete its boot process. Please ensure you have copied your instance
configuration file into the default location below.
Default Location: %s
Default Location: /etc/pterodactyl/config.yml
[yellow]This is not a bug with this software. Please do not make a bug report
for this issue, it will be closed.[reset]
`), config.DefaultLocation)
`))
os.Exit(1)
}

View File

@ -172,25 +172,6 @@ type SystemConfiguration struct {
Gid int `yaml:"gid"`
} `yaml:"user"`
// Passwd controls the mounting of a generated passwd files into containers started by Wings.
Passwd struct {
// Enable controls whether generated passwd files should be mounted into containers.
//
// By default this option is disabled and Wings will not mount any additional passwd
// files into containers.
Enable bool `yaml:"enabled" default:"false"`
// Directory is the directory on disk where the generated files will be stored.
// This directory may be temporary as it will be re-created whenever Wings is started.
//
// This path **WILL** be both written to by Wings and mounted into containers created by
// Wings. If you are running Wings itself in a container, this path will need to be mounted
// into the Wings container as the exact path on the host, which should match the value
// specified here. If you are using SELinux, you will need to make sure this file has the
// correct SELinux context in order for containers to use it.
Directory string `yaml:"directory" default:"/run/wings/etc"`
} `yaml:"passwd"`
// The amount of time in seconds that can elapse before a server's disk space calculation is
// considered stale and a re-check should occur. DANGER: setting this value too low can seriously
// impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings
@ -516,37 +497,6 @@ func EnsurePterodactylUser() error {
return nil
}
// ConfigurePasswd generates required passwd files for use with containers started by Wings.
func ConfigurePasswd() error {
passwd := _config.System.Passwd
if !passwd.Enable {
return nil
}
v := []byte(fmt.Sprintf(
`root:x:0:
container:x:%d:
nogroup:x:65534:`,
_config.System.User.Gid,
))
if err := os.WriteFile(filepath.Join(passwd.Directory, "group"), v, 0o644); err != nil {
return fmt.Errorf("failed to write file to %s/group: %v", passwd.Directory, err)
}
v = []byte(fmt.Sprintf(
`root:x:0:0::/root:/bin/sh
container:x:%d:%d::/home/container:/bin/sh
nobody:x:65534:65534::/var/empty:/bin/sh
`,
_config.System.User.Uid,
_config.System.User.Gid,
))
if err := os.WriteFile(filepath.Join(passwd.Directory, "passwd"), v, 0o644); err != nil {
return fmt.Errorf("failed to write file to %s/passwd: %v", passwd.Directory, err)
}
return nil
}
// FromFile reads the configuration from the provided file and stores it in the
// global singleton for this instance.
func FromFile(path string) error {
@ -611,13 +561,6 @@ func ConfigureDirectories() error {
return err
}
if _config.System.Passwd.Enable {
log.WithField("path", _config.System.Passwd.Directory).Debug("ensuring passwd directory exists")
if err := os.MkdirAll(_config.System.Passwd.Directory, 0o755); err != nil {
return err
}
}
return nil
}

View File

@ -4,6 +4,7 @@ import (
"context"
"os"
"strings"
"syscall"
"time"
"emperror.dev/errors"
@ -142,49 +143,42 @@ func (e *Environment) Stop(ctx context.Context) error {
s := e.meta.Stop
e.mu.RUnlock()
// A native "stop" as the Type field value will just skip over all of this
// logic and end up only executing the container stop command (which may or
// may not work as expected).
if s.Type == "" || s.Type == remote.ProcessStopSignal {
if s.Type == "" {
log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using termination procedure")
}
signal := os.Kill
// Handle a few common cases, otherwise just fall through and just pass along
// the os.Kill signal to the process.
switch strings.ToUpper(s.Value) {
case "SIGABRT":
signal = syscall.SIGABRT
case "SIGINT":
signal = syscall.SIGINT
case "SIGTERM":
signal = syscall.SIGTERM
}
return e.Terminate(ctx, signal)
}
// If the process is already offline don't switch it back to stopping. Just leave it how
// it is and continue through to the stop handling for the process.
if e.st.Load() != environment.ProcessOfflineState {
e.SetState(environment.ProcessStoppingState)
}
// Handle signal based actions
if s.Type == remote.ProcessStopSignal {
log.WithField("signal_value", s.Value).Debug("stopping server using signal")
// Handle some common signals - Default to SIGKILL
signal := "SIGKILL"
switch strings.ToUpper(s.Value) {
case "SIGABRT":
signal = "SIGABRT"
case "SIGINT", "C":
signal = "SIGINT"
case "SIGTERM":
signal = "SIGTERM"
case "SIGKILL":
signal = "SIGKILL"
default:
log.Info("Unrecognised signal requested, defaulting to SIGKILL")
}
return e.SignalContainer(ctx, signal)
}
// Handle command based stops
// Only attempt to send the stop command to the instance if we are actually attached to
// the instance. If we are not for some reason, just send the container stop event.
if e.IsAttached() && s.Type == remote.ProcessStopCommand {
return e.SendCommand(s.Value)
}
if s.Type == "" {
log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using native docker stop")
}
// Fallback to a native docker stop. As we aren't passing a signal to ContainerStop docker will
// attempt to stop the container using the default stop signal, SIGTERM, unless
// another signal was specified in the Dockerfile
// Allow the stop action to run for however long it takes, similar to executing a command
// and using a different logic pathway to wait for the container to stop successfully.
//
// Using a negative timeout here will allow the container to stop gracefully,
// rather than forcefully terminating it. Value is in seconds, but -1 is
@ -230,7 +224,7 @@ func (e *Environment) WaitForStop(ctx context.Context, duration time.Duration, t
doTermination := func(s string) error {
e.log().WithField("step", s).WithField("duration", duration).Warn("container stop did not complete in time, terminating process...")
return e.Terminate(ctx, "SIGKILL")
return e.Terminate(ctx, os.Kill)
}
// We pass through the timed context for this stop action so that if one of the
@ -274,8 +268,8 @@ func (e *Environment) WaitForStop(ctx context.Context, duration time.Duration, t
return nil
}
// Sends the specified signal to the container in an attempt to stop it.
func (e *Environment) SignalContainer(ctx context.Context, signal string) error {
// Terminate forcefully terminates the container using the signal provided.
func (e *Environment) Terminate(ctx context.Context, signal os.Signal) error {
c, err := e.ContainerInspect(ctx)
if err != nil {
// Treat missing containers as an okay error state, means it is obviously
@ -300,27 +294,11 @@ func (e *Environment) SignalContainer(ctx context.Context, signal string) error
// We set it to stopping than offline to prevent crash detection from being triggered.
e.SetState(environment.ProcessStoppingState)
if err := e.client.ContainerKill(ctx, e.Id, signal); err != nil && !client.IsErrNotFound(err) {
sig := strings.TrimSuffix(strings.TrimPrefix(signal.String(), "signal "), "ed")
if err := e.client.ContainerKill(ctx, e.Id, sig); err != nil && !client.IsErrNotFound(err) {
return errors.WithStack(err)
}
return nil
}
// Terminate forcefully terminates the container using the signal provided.
// then sets its state to stopped.
func (e *Environment) Terminate(ctx context.Context, signal string) error {
// Send the signal to the container to kill it
if err := e.SignalContainer(ctx, signal); err != nil {
return errors.WithStack(err)
}
// We expect Terminate to instantly kill the container
// so go ahead and mark it as dead and clean up
e.SetState(environment.ProcessOfflineState)
return nil
}

View File

@ -2,6 +2,7 @@ package environment
import (
"context"
"os"
"time"
"github.com/pterodactyl/wings/events"
@ -71,7 +72,7 @@ type ProcessEnvironment interface {
// Terminate stops a running server instance using the provided signal. This function
// is a no-op if the server is already stopped.
Terminate(ctx context.Context, signal string) error
Terminate(ctx context.Context, signal os.Signal) error
// Destroys the environment removing any containers that were created (in Docker
// environments at least).

View File

@ -34,7 +34,7 @@ type Mount struct {
// Limits is the build settings for a given server that impact docker container
// creation and resource limits for a server instance.
type Limits struct {
// The total amount of memory in mebibytes that this server is allowed to
// The total amount of memory in megabytes that this server is allowed to
// use on the host system.
MemoryLimit int64 `json:"memory_limit"`
@ -79,7 +79,7 @@ func (l Limits) MemoryOverheadMultiplier() float64 {
}
func (l Limits) BoundedMemoryLimit() int64 {
return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1024 * 1024))
return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1_000_000))
}
// ConvertedSwap returns the amount of swap available as a total in bytes. This
@ -90,7 +90,7 @@ func (l Limits) ConvertedSwap() int64 {
return -1
}
return (l.Swap * 1024 * 1024) + l.BoundedMemoryLimit()
return (l.Swap * 1_000_000) + l.BoundedMemoryLimit()
}
// ProcessLimit returns the process limit for a container. This is currently
@ -105,7 +105,7 @@ func (l Limits) AsContainerResources() container.Resources {
pids := l.ProcessLimit()
resources := container.Resources{
Memory: l.BoundedMemoryLimit(),
MemoryReservation: l.MemoryLimit * 1024 * 1024,
MemoryReservation: l.MemoryLimit * 1_000_000,
MemorySwap: l.ConvertedSwap(),
BlkioWeight: l.IoWeight,
OomKillDisable: &l.OOMDisabled,

View File

@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1719994518,
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
"lastModified": 1706830856,
"narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
"rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f",
"type": "github"
},
"original": {
@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1721562059,
"narHash": "sha256-Tybxt65eyOARf285hMHIJ2uul8SULjFZbT9ZaEeUnP8=",
"lastModified": 1707956935,
"narHash": "sha256-ZL2TrjVsiFNKOYwYQozpbvQSwvtV/3Me7Zwhmdsfyu4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "68c9ed8bbed9dfce253cc91560bf9043297ef2fe",
"rev": "a4d4fe8c5002202493e87ec8dbc91335ff55552c",
"type": "github"
},
"original": {
@ -36,14 +36,20 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1719876945,
"narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
"dir": "lib",
"lastModified": 1706550542,
"narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "97b17f32362e475016f942bbdfda4a4a72a8a652",
"type": "github"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
@ -60,11 +66,11 @@
]
},
"locked": {
"lastModified": 1721769617,
"narHash": "sha256-6Pqa0bi5nV74IZcENKYRToRNM5obo1EQ+3ihtunJ014=",
"lastModified": 1707300477,
"narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "8db8970be1fb8be9c845af7ebec53b699fe7e009",
"rev": "ac599dab59a66304eb511af07b3883114f061b9d",
"type": "github"
},
"original": {

View File

@ -1,8 +1,6 @@
package filesystem
import (
"golang.org/x/sys/unix"
"slices"
"sync"
"sync/atomic"
"time"
@ -166,8 +164,6 @@ func (fs *Filesystem) DirectorySize(root string) (int64, error) {
return 0, err
}
var hardLinks []uint64
var size atomic.Int64
err = fs.unixFS.WalkDirat(dirfd, name, func(dirfd int, name, _ string, d ufs.DirEntry, err error) error {
if err != nil {
@ -184,16 +180,8 @@ func (fs *Filesystem) DirectorySize(root string) (int64, error) {
return errors.Wrap(err, "lstatat err")
}
var sysFileInfo = info.Sys().(*unix.Stat_t)
if sysFileInfo.Nlink > 1 {
// Hard links have the same inode number
if slices.Contains(hardLinks, sysFileInfo.Ino) {
// Don't add hard links size twice
return nil
} else {
hardLinks = append(hardLinks, sysFileInfo.Ino)
}
}
// TODO: detect if info is a hard-link and de-duplicate it.
// ref; https://github.com/pterodactyl/wings/pull/181/files
size.Add(info.Size())
return nil

View File

@ -480,9 +480,9 @@ func (fs *Filesystem) ListDirectory(p string) ([]Stat, error) {
case a.IsDir() && b.IsDir():
return 0
case a.IsDir():
return -1
default:
return 1
default:
return -1
}
})

View File

@ -29,21 +29,6 @@ func (s *Server) Mounts() []environment.Mount {
},
}
// Handle mounting a generated `/etc/passwd` if the feature is enabled.
if passwd := config.Get().System.Passwd; passwd.Enable {
s.Log().WithFields(log.Fields{"source_path": passwd.Directory}).Info("mouting generated /etc/{group,passwd} to workaround UID/GID issues")
m = append(m, environment.Mount{
Source: filepath.Join(passwd.Directory, "group"),
Target: "/etc/group",
ReadOnly: true,
})
m = append(m, environment.Mount{
Source: filepath.Join(passwd.Directory, "passwd"),
Target: "/etc/passwd",
ReadOnly: true,
})
}
// Also include any of this server's custom mounts when returning them.
return append(m, s.customMounts()...)
}
@ -71,12 +56,14 @@ func (s *Server) customMounts() []environment.Mount {
if !strings.HasPrefix(source, filepath.Clean(allowed)) {
continue
}
mounted = true
mounts = append(mounts, environment.Mount{
Source: source,
Target: target,
ReadOnly: m.ReadOnly,
})
break
}

View File

@ -3,6 +3,7 @@ package server
import (
"context"
"fmt"
"os"
"time"
"emperror.dev/errors"
@ -160,7 +161,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
return s.Environment.Start(s.Context())
case PowerActionTerminate:
return s.Environment.Terminate(s.Context(), "SIGKILL")
return s.Environment.Terminate(s.Context(), os.Kill)
}
return errors.New("attempting to handle unknown power action")

View File

@ -28,7 +28,6 @@ func (s *Server) SyncWithEnvironment() {
Mounts: s.Mounts(),
Allocations: cfg.Allocations,
Limits: cfg.Build,
Labels: cfg.Labels,
})
// For Docker specific environments we also want to update the configured image

View File

@ -1,3 +1,3 @@
package system
var Version = "develop"
var Version = "1.11.13"