Compare commits

...

57 Commits

Author SHA1 Message Date
Dane Everitt
04d714f19c Update CHANGELOG.md 2020-12-06 15:49:46 -08:00
Dane Everitt
a18f60bd05 Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-12-06 15:35:04 -08:00
Dane Everitt
deea5babbc Publish message when error occurs during crash handling 2020-12-06 15:34:58 -08:00
Matthew Penner
e41b3dc09a Merge branch 'matthewpi/backups-patch-1' into develop
# Conflicts:
#	loggers/cli/cli.go
2020-12-06 16:18:15 -07:00
Matthew Penner
6366794838 Actually initialize the cache 2020-12-06 16:15:54 -07:00
Matthew Penner
c01a39d881 Add caching to build-test workflow 2020-12-06 16:12:41 -07:00
Dane Everitt
3f2ce59766 Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-12-06 15:10:14 -08:00
Dane Everitt
bcf0c72e47 Correctly report errors to admin users over the websocket; closes pterodactyl/panel#2709 2020-12-06 15:10:08 -08:00
Matthew Penner
bd5892b70c yeet useless boi 2020-12-06 16:05:57 -07:00
Dane Everitt
70ea61f22f test first I guess 2020-12-06 15:04:00 -08:00
Dane Everitt
1b0c2e1764 * 2020-12-06 15:02:50 -08:00
Dane Everitt
3d532f6e0b Simplify logic 2020-12-06 14:58:35 -08:00
Matthew Penner
81fd1a3758 Make logged stacktraces more useful 2020-12-06 15:55:35 -07:00
Matthew Penner
b52c3fb61e Cleanup backup/backup_s3.go 2020-12-06 15:25:11 -07:00
Matthew Penner
bc3d92f9e6 Change backup upload id cache to use the cache package 2020-12-06 15:23:44 -07:00
Matthew Penner
ee08829a28 s3 backups: handle CompleteMultipartUpload and AbortMultipartUpload on the panel 2020-12-06 13:56:17 -07:00
Dane Everitt
83f0d2c953 Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-12-06 10:53:57 -08:00
Dane Everitt
605be3ebad Update README.md 2020-12-06 10:53:55 -08:00
Matthew Penner
ca6dc2c964 Remove .travis.yml 2020-12-03 18:32:59 -07:00
Dane Everitt
dc41126e25 Merge pull request #72 from TortleWortle/patch-1
Allow multiple publishing on multiple interfaces on same port.
2020-12-02 08:18:38 -08:00
TortleWortle
da4c542724 Update allocations.go 2020-12-02 14:27:37 +01:00
Tortle
e7d93a5248 Update allocations.go 2020-12-01 23:03:05 +01:00
Tortle
51aa4c73cd Update allocations.go 2020-12-01 21:20:01 +01:00
Tortle
4a7510d36f Allow multiple publishing on multiple interfaces on same port.
Fixes the issue where you cannot have multiple ip addresses on the same port for a server.
2020-12-01 20:43:40 +01:00
Matthew Penner
ba0a1a651e Only use the permission bits for ModeBits 2020-11-29 16:13:07 -07:00
Matthew Penner
068f41393d Return mode_bits when listing files in a directory 2020-11-29 14:20:24 -07:00
Matthew Penner
169e8b8f8b Fix chmod endpoint mode value 2020-11-29 13:48:15 -07:00
Matthew Penner
c6e2889075 Fix chmod endpoint 2020-11-29 13:44:28 -07:00
Matthew Penner
f62f714863 Add chmod endpoint to server files 2020-11-29 13:07:45 -07:00
Dane Everitt
da9ace5d9d Create FUNDING.yml 2020-11-29 11:58:20 -08:00
Dane Everitt
97345123ce Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-11-29 11:48:03 -08:00
Dane Everitt
8aa9105ed3 Make paths lowercase for cert serving; closes pterodactyl/panel#2745 2020-11-29 11:47:52 -08:00
Matthew Penner
e8088f85d0 Lets not try to log the server ID twice 2020-11-29 12:35:02 -07:00
Matthew Penner
0c8476c79b Cleanup router/router_transfer.go 2020-11-29 12:33:44 -07:00
Matthew Penner
a7a66e8bc0 Notify panel of failed archive generation when transferring a server 2020-11-29 12:31:54 -07:00
Matthew Penner
65a861a9b6 Remove temporary transfer archives; closes https://github.com/pterodactyl/panel/issues/2742 2020-11-28 17:00:52 -07:00
Matthew Penner
de51fd1c51 Error handling improvements (#71)
* Remove `emperror.dev/errors`, remove all `errors#Wrap` and `errors#WithStack` calls
* Improve logging in `server/backup.go`
2020-11-28 16:57:10 -07:00
Matthew Penner
40c70673cd Use registry authentication when pulling install image, fixes https://github.com/pterodactyl/panel/issues/2589 2020-11-20 14:36:29 -07:00
Matthew Penner
73b221d022 Switch old Server#GetState() calls over to Server#Environment#State() 2020-11-20 14:35:29 -07:00
Matthew Penner
287b286940 Oh, that's why it was done like that 2020-11-18 18:44:25 -07:00
Matthew Penner
1d0e85cf55 Attempt to fix 'archive/tar: write too long' error when creating a backup 2020-11-18 16:30:34 -07:00
Matthew Penner
30ec6dc78d fix panic when getting error stack 2020-11-15 13:34:19 -07:00
Matthew Penner
121a4d1146 Attempt to fix codeql-analysis.yml 2020-11-15 13:04:03 -07:00
Matthew Penner
b9be373671 Attempt to force GitHub Actions to use go1.15.5 2020-11-15 13:00:33 -07:00
Dane Everitt
aedd0e406c appease docker compose [skip docker] 2020-11-14 20:50:09 -08:00
Dane Everitt
82b23ef638 Add support for automated docker build 2020-11-14 20:42:12 -08:00
Dane Everitt
d970ec35b7 Update dockerfile to build quicker 2020-11-14 19:22:29 -08:00
Dane Everitt
e2872e786e Tiny optimization for handling string output from server console 2020-11-12 21:41:35 -08:00
Dane Everitt
f81e35d960 Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-11-12 20:50:18 -08:00
Dane Everitt
672fb860ea Don't add a stack for deadline exceeded when terminating 2020-11-12 20:50:15 -08:00
Dane Everitt
8081c83de4 Allow disabling disk checking with 0 value for check interval 2020-11-12 20:50:03 -08:00
Dane Everitt
f379d0e54a Merge pull request #68 from gzzchh/develop
Turn off the CGO to make it static linked
2020-11-11 10:58:49 -08:00
Dane Everitt
ffb6bd72ef Don't accidentally reset a server's state when starting it after a system reboot; closes pterodactyl/panel#2695 2020-11-10 21:21:20 -08:00
Dane Everitt
488ef9de54 Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-11-10 20:36:42 -08:00
Dane Everitt
34349d4b48 Don't report a context cancelation as an error 2020-11-10 20:36:40 -08:00
Matthew Penner
2197d82957 Clean paths from AllowedMounts to ensure consistency 2020-11-09 16:58:01 -07:00
gzzchh
33e584b447 Turn off the CGO to make it static linked 2020-11-08 14:42:55 +08:00
70 changed files with 779 additions and 632 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [DaneEveritt]
custom: ["https://paypal.me/PterodactylSoftware"]

View File

@@ -1,12 +1,10 @@
name: "Build & Test" name: Run Tests
on: on:
push: push:
branches-ignore: branches-ignore:
- 'master' - 'master'
- 'release/**' - 'release/**'
pull_request: pull_request:
jobs: jobs:
build: build:
strategy: strategy:
@@ -15,7 +13,7 @@ jobs:
matrix: matrix:
os: [ ubuntu-20.04 ] os: [ ubuntu-20.04 ]
go: [ 1.15 ] go: [ 1.15.6 ]
goos: [ linux ] goos: [ linux ]
goarch: [ amd64, arm, arm64 ] goarch: [ amd64, arm, arm64 ]
@@ -28,12 +26,40 @@ jobs:
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Print Environment
id: env
run: |
printf "Go Executable Path: $(which go)\n"
printf "Go Version: $(go version)\n"
printf "\n\nGo Environment:\n\n"
go env
printf "\n\nSystem Environment:\n\n"
env
echo "::set-output name=version_tag::${GITHUB_REF/refs\/tags\//}"
echo "::set-output name=short_sha::$(git rev-parse --short HEAD)"
echo "::set-output name=go_cache::$(go env GOCACHE)"
- name: Build Cache
uses: actions/cache@v2
with:
path: ${{ steps.env.outputs.go_cache }}
key: ${{ runner.os }}-${{ matrix.go }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go }}-go
- name: Get Dependencies
run: |
go get -v -t -d ./...
- name: Build - name: Build
env: env:
GOOS: ${{ matrix.goos }} GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }} GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
SRC_PATH: github.com/pterodactyl/wings
run: | run: |
go build -v -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=dev-${GIT_COMMIT:0:7}" -o build/wings_${{ matrix.goos }}_${{ matrix.goarch }} wings.go go build -v -trimpath -ldflags="-s -w -X ${SRC_PATH}/system.Version=dev-${GIT_COMMIT:0:7}" -o build/wings_${{ matrix.goos }}_${{ matrix.goarch }} wings.go
- name: Test - name: Test
run: go test ./... run: go test ./...

View File

@@ -1,16 +1,12 @@
name: "Code scanning - action" name: CodeQL Scanning
on: on:
push: push:
branches:
- 'develop'
pull_request: pull_request:
schedule:
- cron: '0 21 * * 6'
jobs: jobs:
CodeQL-Build: CodeQL-Build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -18,7 +14,6 @@ jobs:
# We must fetch at least the immediate parents so that if this is # We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head. # a pull request then we can checkout the head.
fetch-depth: 2 fetch-depth: 2
# If this run was triggered by a pull request event, then checkout # If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit. # the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2 - run: git checkout HEAD^2

47
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Publish Docker Image
on:
push:
branches:
- 'develop'
tags:
- 'v*'
jobs:
push_to_registry:
name: Push Image to GitHub Packages
runs-on: ubuntu-latest
# Always run against a tag, even if the commit into the tag has [docker skip]
# within the commit message.
if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))"
steps:
- uses: actions/checkout@v2
- uses: crazy-max/ghaction-docker-meta@v1
id: docker_meta
with:
images: ghcr.io/pterodactyl/wings
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Release Production Build
uses: docker/build-push-action@v2
if: "!contains(github.ref, 'develop')"
env:
REF: ${{ github.ref }}
with:
push: true
build-args: |
VERSION=${REF:11}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
- name: Release Development Build
uses: docker/build-push-action@v2
if: "contains(github.ref, 'develop')"
with:
push: ${{ github.event_name != 'pull_request' }}
build-args: |
VERSION=dev-${GIT_COMMIT:0:7}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}

View File

@@ -1,10 +1,8 @@
name: "Release" name: Create Release
on: on:
push: push:
tags: tags:
- 'v*' - 'v*'
jobs: jobs:
release: release:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@@ -20,9 +18,9 @@ jobs:
env: env:
REF: ${{ github.ref }} REF: ${{ github.ref }}
run: | run: |
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_amd64 -v wings.go CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_amd64 -v wings.go
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_arm64 -v wings.go CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_arm64 -v wings.go
GOOS=linux GOARCH=arm go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_arm -v wings.go CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_arm -v wings.go
- name: Test - name: Test
run: go test ./... run: go test ./...

View File

@@ -1,47 +0,0 @@
os: linux
dist: xenial
language: go
go:
- 1.13.x
go_import_path: "github.com/pterodactyl/wings"
services:
- docker
install:
- mkdir -p $GOPATH/bin
# Install used tools
- go get github.com/mitchellh/gox
- go get github.com/haya14busa/goverage
- go get github.com/schrej/godacov
- go mod download
script:
- make cross-build
- goverage -v -coverprofile=coverage.out ./...
- godacov -t $CODACY_TOKEN -r ./coverage.out -c $TRAVIS_COMMIT
deploy:
provider: releases
api_key:
secure: HQ8AvnSsOW2aDUKv25sU83SswK9rReGeFi68SotLGPdWyFBWJbp/JEHhw9swSqvhLPykx5QqLnRPG4nomOp2i5dVTXgM/7C3wQ2ULymkJDZqDJEAxjm1IuNsjXgcFqp0zcNXL3g0moaorHS2XZpzbgaewlCyYoEb+3SZUGzOCPIjSFvoIBaAYx6kRn+pyWo1I0mQChno2i7SGvAoZwh/hZIO6L5FZe5PcpBs/SxkZ+/shsGMk7CIyNMhG6CQTE1tlr+ZenluXjtliZfc4XwkHG/9MICNl8ihUrnN6YfdvJZXLQvolZQ0QJ5Eyb04jQd1yzKR1hcLx2S42IAWxaWTy5QxSN8QyG5wBRNg567ib5FEqY4M1nyQnWQbUbaiYloYBp14aR1L9DQw8+xmXnlgnTUPq1w+cOpQLeY/RENCalgHe7NoI3lClC2b7/c1j+O7RA68yYUFUod0y7ZXcCwsJkbRk7xgyDEAGs+rq8wLknj6f8y8cfNm179lRARwblnmo9uA43Tlee8DBSziSvJy/mYMzdIQeb+PHuznXjr4fze7x+zvronkiD/JH8MjJl3SWaE7DGtc5jz4+aRxU3rMbHwToEOY6u5pIsvz5PRFYWBvKX2+VoxmdR+m1qhAxsg0wtbA0CTnqgHNGMIFDWVTDQSy8LvJt+usUn1RtrYyyiI=
file_glob: true
file: build/*
on:
tags: true
branch: master
notifications:
email: false
webhooks:
urls:
- https://misc.schrej.net/travistodiscord/pterodev.php
on_success: change
on_failure: always
on_error: always
on_cancel: always
on_start: never

View File

@@ -1,5 +1,35 @@
# Changelog # Changelog
## v1.1.3
### Fixed
* Fixes `archive/tar: write too long` error when creating a server backup.
* Fixes server installation docker images not using authentication properly during the pull.
* Fixes temporary transfer files not being removed after the transfer is completed.
* Fixes TLS certificate checking to be all lowercase to avoid any lookup issues when an all-caps domain is provided.
* Fixes multiple interfaces with the same port not being publishable for a server.
* Fixes errors encountered during websocket processes being incorrectly passed back to the Panel as a JWT error rather than a generic Wings error for admin users.
### Added
* Added logic to notify the Panel when archive generation fails.
* Added endpoint to run `chmod` commands against server files and updated API response to include the mode bits when requesting files.
### Changed
* Updated internals to call `Server.Environment.State()` rather than deprecated `Server.GetState()` functions.
* Improved error handling logic and massively simplified error passing around the codebase.
## v1.1.2
### Fixed
* Fixes binaries built as part of the release process not being usable in MUSL based environments (such as our Docker images).
* Fixes server states being incorrectly set back to offline when a server is started after a system restart.
### Changed
* Improved logic for cleaning `allowed_mount` paths for consistency.
* Certain context cancelation deadline errors are no longer wrong reported at an error level (since they're expected).
* Very minor micro-optimizations for some string handling with server console output.
### Added
* Added a hidden option to disable all disk checking for servers by setting the `disk_check_interval` to `0` in the config file.
## v1.1.1 ## v1.1.1
### Fixed ### Fixed
* Fixes certain files returning invalid data in the request due to a bad header set after sending data down the line. * Fixes certain files returning invalid data in the request due to a bad header set after sending data down the line.

View File

@@ -1,14 +1,11 @@
# ----------------------------------
# Pterodactyl Panel Dockerfile
# ----------------------------------
FROM golang:1.15-alpine FROM golang:1.15-alpine
ARG VERSION="develop"
COPY . /go/wings/ COPY . /go/wings/
WORKDIR /go/wings/ WORKDIR /go/wings/
RUN apk add --no-cache upx \ RUN apk add --no-cache upx \
&& go build -ldflags="-s -w" \ && CGO_ENABLED=0 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${VERSION}" \
&& upx --brute wings && upx wings
FROM alpine:latest FROM alpine:latest
COPY --from=0 /go/wings/wings /usr/bin/ COPY --from=0 /go/wings/wings /usr/bin/
CMD ["wings","--config", "/etc/pterodactyl/config.yml"] CMD ["wings", "--config", "/etc/pterodactyl/config.yml"]

View File

@@ -23,7 +23,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f
| [**MineStrator**](https://minestrator.com/) | Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord, trust us. | | [**MineStrator**](https://minestrator.com/) | Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord, trust us. |
| [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. | | [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. |
| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | | [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! |
| [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. | | [**XCORE**](https://xcore-server.de/) | XCORE offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. |
| [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHostings reliable servers and network. Easy to use, provisioned in a couple of minutes. | | [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHostings reliable servers and network. Easy to use, provisioned in a couple of minutes. |
| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. | | [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. |
| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. | | [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. |

View File

@@ -2,10 +2,10 @@ package api
import ( import (
"bytes" "bytes"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
"io" "io"
@@ -69,7 +69,7 @@ func (r *Request) Endpoint(endpoint string) string {
func (r *Request) Make(method, url string, body io.Reader, opts ...func(r *http.Request)) (*Response, error) { func (r *Request) Make(method, url string, body io.Reader, opts ...func(r *http.Request)) (*Response, error) {
req, err := http.NewRequest(method, url, body) req, err := http.NewRequest(method, url, body)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
req.Header.Set("User-Agent", fmt.Sprintf("Pterodactyl Wings/v%s (id:%s)", system.Version, config.Get().AuthenticationTokenId)) req.Header.Set("User-Agent", fmt.Sprintf("Pterodactyl Wings/v%s (id:%s)", system.Version, config.Get().AuthenticationTokenId))
@@ -127,7 +127,7 @@ func (r *Request) Get(url string, data Q) (*Response, error) {
func (r *Request) Post(url string, data interface{}) (*Response, error) { func (r *Request) Post(url string, data interface{}) (*Response, error) {
b, err := json.Marshal(data) b, err := json.Marshal(data)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
return r.Make(http.MethodPost, r.Endpoint(url), bytes.NewBuffer(b)) return r.Make(http.MethodPost, r.Endpoint(url), bytes.NewBuffer(b))
@@ -167,10 +167,10 @@ func (r *Response) Read() ([]byte, error) {
func (r *Response) Bind(v interface{}) error { func (r *Response) Bind(v interface{}) error {
b, err := r.Read() b, err := r.Read()
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
return errors.WithStackIf(json.Unmarshal(b, &v)) return json.Unmarshal(b, &v)
} }
// Returns the error message from the API call as a string. The error message will be formatted // Returns the error message from the API call as a string. The error message will be formatted

View File

@@ -1,22 +1,25 @@
package api package api
import ( import (
"emperror.dev/errors"
"fmt" "fmt"
"github.com/patrickmn/go-cache"
"strconv" "strconv"
"time"
) )
// backupUploadIDs stores a cache of active S3 backups.
var backupUploadIDs = cache.New(time.Hour*3, time.Minute*5)
type BackupRemoteUploadResponse struct { type BackupRemoteUploadResponse struct {
CompleteMultipartUpload string `json:"complete_multipart_upload"` UploadID string `json:"upload_id"`
AbortMultipartUpload string `json:"abort_multipart_upload"` Parts []string `json:"parts"`
Parts []string `json:"parts"` PartSize int64 `json:"part_size"`
PartSize int64 `json:"part_size"`
} }
func (r *Request) GetBackupRemoteUploadURLs(backup string, size int64) (*BackupRemoteUploadResponse, error) { func (r *Request) GetBackupRemoteUploadURLs(backup string, size int64) (*BackupRemoteUploadResponse, error) {
resp, err := r.Get(fmt.Sprintf("/backups/%s", backup), Q{"size": strconv.FormatInt(size, 10)}) resp, err := r.Get(fmt.Sprintf("/backups/%s", backup), Q{"size": strconv.FormatInt(size, 10)})
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -26,13 +29,19 @@ func (r *Request) GetBackupRemoteUploadURLs(backup string, size int64) (*BackupR
var res BackupRemoteUploadResponse var res BackupRemoteUploadResponse
if err := resp.Bind(&res); err != nil { if err := resp.Bind(&res); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
// Store the backup upload id for later use, this is a janky way to be able to use it later with SendBackupStatus.
// Yes, the timeout of 3 hours is intentional, if this value is removed before the backup completes,
// the backup will fail even if it uploaded properly.
backupUploadIDs.Set(backup, res.UploadID, 0)
return &res, nil return &res, nil
} }
type BackupRequest struct { type BackupRequest struct {
UploadID string `json:"upload_id"`
Checksum string `json:"checksum"` Checksum string `json:"checksum"`
ChecksumType string `json:"checksum_type"` ChecksumType string `json:"checksum_type"`
Size int64 `json:"size"` Size int64 `json:"size"`
@@ -42,9 +51,14 @@ type BackupRequest struct {
// Notifies the panel that a specific backup has been completed and is now // Notifies the panel that a specific backup has been completed and is now
// available for a user to view and download. // available for a user to view and download.
func (r *Request) SendBackupStatus(backup string, data BackupRequest) error { func (r *Request) SendBackupStatus(backup string, data BackupRequest) error {
// Set the UploadID on the data.
if v, ok := backupUploadIDs.Get(backup); ok {
data.UploadID = v.(string)
}
resp, err := r.Post(fmt.Sprintf("/backups/%s", backup), data) resp, err := r.Post(fmt.Sprintf("/backups/%s", backup), data)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer resp.Body.Close() defer resp.Body.Close()

View File

@@ -2,7 +2,6 @@ package api
import ( import (
"context" "context"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
@@ -57,7 +56,7 @@ type RawServerData struct {
func (r *Request) GetServers() ([]RawServerData, error) { func (r *Request) GetServers() ([]RawServerData, error) {
resp, err := r.Get("/servers", Q{"per_page": strconv.Itoa(int(config.Get().RemoteQuery.BootServersPerPage))}) resp, err := r.Get("/servers", Q{"per_page": strconv.Itoa(int(config.Get().RemoteQuery.BootServersPerPage))})
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -67,7 +66,7 @@ func (r *Request) GetServers() ([]RawServerData, error) {
var res allServerResponse var res allServerResponse
if err := resp.Bind(&res); err != nil { if err := resp.Bind(&res); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
var mu sync.Mutex var mu sync.Mutex
@@ -117,7 +116,7 @@ func (r *Request) GetServers() ([]RawServerData, error) {
} }
if err := g.Wait(); err != nil { if err := g.Wait(); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
} }
@@ -130,7 +129,7 @@ func (r *Request) GetServerConfiguration(uuid string) (ServerConfigurationRespon
resp, err := r.Get(fmt.Sprintf("/servers/%s", uuid), nil) resp, err := r.Get(fmt.Sprintf("/servers/%s", uuid), nil)
if err != nil { if err != nil {
return cfg, errors.WithStackIf(err) return cfg, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -139,7 +138,7 @@ func (r *Request) GetServerConfiguration(uuid string) (ServerConfigurationRespon
} }
if err := resp.Bind(&cfg); err != nil { if err := resp.Bind(&cfg); err != nil {
return cfg, errors.WithStackIf(err) return cfg, err
} }
return cfg, nil return cfg, nil
@@ -150,7 +149,7 @@ func (r *Request) GetInstallationScript(uuid string) (InstallationScript, error)
var is InstallationScript var is InstallationScript
resp, err := r.Get(fmt.Sprintf("/servers/%s/install", uuid), nil) resp, err := r.Get(fmt.Sprintf("/servers/%s/install", uuid), nil)
if err != nil { if err != nil {
return is, errors.WithStackIf(err) return is, err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -159,7 +158,7 @@ func (r *Request) GetInstallationScript(uuid string) (InstallationScript, error)
} }
if err := resp.Bind(&is); err != nil { if err := resp.Bind(&is); err != nil {
return is, errors.WithStackIf(err) return is, err
} }
return is, nil return is, nil
@@ -169,7 +168,7 @@ func (r *Request) GetInstallationScript(uuid string) (InstallationScript, error)
func (r *Request) SendInstallationStatus(uuid string, successful bool) error { func (r *Request) SendInstallationStatus(uuid string, successful bool) error {
resp, err := r.Post(fmt.Sprintf("/servers/%s/install", uuid), D{"successful": successful}) resp, err := r.Post(fmt.Sprintf("/servers/%s/install", uuid), D{"successful": successful})
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -183,7 +182,7 @@ func (r *Request) SendInstallationStatus(uuid string, successful bool) error {
func (r *Request) SendArchiveStatus(uuid string, successful bool) error { func (r *Request) SendArchiveStatus(uuid string, successful bool) error {
resp, err := r.Post(fmt.Sprintf("/servers/%s/archive", uuid), D{"successful": successful}) resp, err := r.Post(fmt.Sprintf("/servers/%s/archive", uuid), D{"successful": successful})
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -193,7 +192,7 @@ func (r *Request) SendArchiveStatus(uuid string, successful bool) error {
func (r *Request) SendTransferFailure(uuid string) error { func (r *Request) SendTransferFailure(uuid string) error {
resp, err := r.Get(fmt.Sprintf("/servers/%s/transfer/failure", uuid), nil) resp, err := r.Get(fmt.Sprintf("/servers/%s/transfer/failure", uuid), nil)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -203,7 +202,7 @@ func (r *Request) SendTransferFailure(uuid string) error {
func (r *Request) SendTransferSuccess(uuid string) error { func (r *Request) SendTransferSuccess(uuid string) error {
resp, err := r.Get(fmt.Sprintf("/servers/%s/transfer/success", uuid), nil) resp, err := r.Get(fmt.Sprintf("/servers/%s/transfer/success", uuid), nil)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer resp.Body.Close() defer resp.Body.Close()

View File

@@ -1,8 +1,8 @@
package api package api
import ( import (
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"regexp" "regexp"
) )

View File

@@ -18,9 +18,9 @@ import (
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
"github.com/docker/cli/components/engine/pkg/parsers/operatingsystem"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/parsers/operatingsystem"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
"github.com/spf13/cobra" "github.com/spf13/cobra"

View File

@@ -19,7 +19,7 @@ import (
"github.com/pterodactyl/wings/loggers/cli" "github.com/pterodactyl/wings/loggers/cli"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
"emperror.dev/errors" "github.com/pkg/errors"
"github.com/pkg/profile" "github.com/pkg/profile"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
@@ -77,7 +77,7 @@ func readConfiguration() (*config.Configuration, error) {
} }
if s, err := os.Stat(p); err != nil { if s, err := os.Stat(p); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} else if s.IsDir() { } else if s.IsDir() {
return nil, errors.New("cannot use directory as configuration file path") return nil, errors.New("cannot use directory as configuration file path")
} }
@@ -199,7 +199,7 @@ func rootCmdRun(*cobra.Command, []string) {
states, err := server.CachedServerStates() states, err := server.CachedServerStates()
if err != nil { if err != nil {
log.WithField("error", errors.WithStackIf(err)).Error("failed to retrieve locally cached server states from disk, assuming all servers in offline state") log.WithField("error", err).Error("failed to retrieve locally cached server states from disk, assuming all servers in offline state")
} }
// Create a new workerpool that limits us to 4 servers being bootstrapped at a time // Create a new workerpool that limits us to 4 servers being bootstrapped at a time
@@ -235,7 +235,7 @@ func rootCmdRun(*cobra.Command, []string) {
// as a result will result in a slow boot. // as a result will result in a slow boot.
if !r && (st == environment.ProcessRunningState || st == environment.ProcessStartingState) { if !r && (st == environment.ProcessRunningState || st == environment.ProcessStartingState) {
if err := s.HandlePowerAction(server.PowerActionStart); err != nil { if err := s.HandlePowerAction(server.PowerActionStart); err != nil {
s.Log().WithField("error", errors.WithStackIf(err)).Warn("failed to return server to running state") s.Log().WithField("error", err).Warn("failed to return server to running state")
} }
} else if r || (!r && s.IsRunning()) { } else if r || (!r && s.IsRunning()) {
// If the server is currently running on Docker, mark the process as being in that state. // If the server is currently running on Docker, mark the process as being in that state.
@@ -248,15 +248,14 @@ func rootCmdRun(*cobra.Command, []string) {
s.Environment.SetState(environment.ProcessRunningState) s.Environment.SetState(environment.ProcessRunningState)
if err := s.Environment.Attach(); err != nil { if err := s.Environment.Attach(); err != nil {
s.Log().WithField("error", errors.WithStackIf(err)).Warn("failed to attach to running server environment") s.Log().WithField("error", err).Warn("failed to attach to running server environment")
} }
} else {
return // At this point we've determined that the server should indeed be in an offline state, so we'll
// make a call to set that state just to ensure we don't ever accidentally end up with some invalid
// state being tracked.
s.Environment.SetState(environment.ProcessOfflineState)
} }
// Addresses potentially invalid data in the stored file that can cause Wings to lose
// track of what the actual server state is.
s.Environment.SetState(environment.ProcessOfflineState)
}) })
} }
@@ -337,7 +336,6 @@ func rootCmdRun(*cobra.Command, []string) {
if err := s.ListenAndServeTLS("", ""); err != nil { if err := s.ListenAndServeTLS("", ""); err != nil {
log.WithFields(log.Fields{"auto_tls": true, "tls_hostname": tlsHostname, "error": err}). log.WithFields(log.Fields{"auto_tls": true, "tls_hostname": tlsHostname, "error": err}).
Fatal("failed to configure HTTP server using auto-tls") Fatal("failed to configure HTTP server using auto-tls")
os.Exit(1)
} }
return return
@@ -345,9 +343,8 @@ func rootCmdRun(*cobra.Command, []string) {
// Check if main http server should run with TLS. // Check if main http server should run with TLS.
if c.Api.Ssl.Enabled { if c.Api.Ssl.Enabled {
if err := s.ListenAndServeTLS(c.Api.Ssl.CertificateFile, c.Api.Ssl.KeyFile); err != nil { if err := s.ListenAndServeTLS(strings.ToLower(c.Api.Ssl.CertificateFile), strings.ToLower(c.Api.Ssl.KeyFile)); err != nil {
log.WithFields(log.Fields{"auto_tls": false, "error": err}).Fatal("failed to configure HTTPS server") log.WithFields(log.Fields{"auto_tls": false, "error": err}).Fatal("failed to configure HTTPS server")
os.Exit(1)
} }
return return
} }
@@ -356,7 +353,6 @@ func rootCmdRun(*cobra.Command, []string) {
s.TLSConfig = nil s.TLSConfig = nil
if err := s.ListenAndServe(); err != nil { if err := s.ListenAndServe(); err != nil {
log.WithField("error", err).Fatal("failed to configure HTTP server") log.WithField("error", err).Fatal("failed to configure HTTP server")
os.Exit(1)
} }
} }
@@ -369,13 +365,13 @@ func Execute() error {
// in the code without having to pass around a logger instance. // in the code without having to pass around a logger instance.
func configureLogging(logDir string, debug bool) error { func configureLogging(logDir string, debug bool) error {
if err := os.MkdirAll(path.Join(logDir, "/install"), 0700); err != nil { if err := os.MkdirAll(path.Join(logDir, "/install"), 0700); err != nil {
return errors.WithStackIf(err) return err
} }
p := filepath.Join(logDir, "/wings.log") p := filepath.Join(logDir, "/wings.log")
w, err := logrotate.NewFile(p) w, err := logrotate.NewFile(p)
if err != nil { if err != nil {
panic(errors.WrapIf(err, "failed to open process log file")) panic(errors.WithMessage(err, "failed to open process log file"))
} }
if debug { if debug {

View File

@@ -1,11 +1,11 @@
package config package config
import ( import (
"emperror.dev/errors"
"fmt" "fmt"
"github.com/cobaugh/osrelease" "github.com/cobaugh/osrelease"
"github.com/creasty/defaults" "github.com/creasty/defaults"
"github.com/gbrlsnchs/jwt/v3" "github.com/gbrlsnchs/jwt/v3"
"github.com/pkg/errors"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -47,12 +47,6 @@ type Configuration struct {
System SystemConfiguration `json:"system" yaml:"system"` System SystemConfiguration `json:"system" yaml:"system"`
Docker DockerConfiguration `json:"docker" yaml:"docker"` Docker DockerConfiguration `json:"docker" yaml:"docker"`
// The amount of time in seconds that should elapse between disk usage checks
// run by the daemon. Setting a higher number can result in better IO performance
// at an increased risk of a malicious user creating a process that goes over
// the assigned disk limits.
DiskCheckTimeout int `yaml:"disk_check_timeout"`
// Defines internal throttling configurations for server processes to prevent // Defines internal throttling configurations for server processes to prevent
// someone from running an endless loop that spams data to logs. // someone from running an endless loop that spams data to logs.
Throttles ConsoleThrottles Throttles ConsoleThrottles
@@ -198,7 +192,7 @@ func GetJwtAlgorithm() *jwt.HMACSHA {
func NewFromPath(path string) (*Configuration, error) { func NewFromPath(path string) (*Configuration, error) {
c := new(Configuration) c := new(Configuration)
if err := defaults.Set(c); err != nil { if err := defaults.Set(c); err != nil {
return c, errors.WithStackIf(err) return c, err
} }
c.unsafeSetPath(path) c.unsafeSetPath(path)
@@ -236,12 +230,12 @@ func (c *Configuration) EnsurePterodactylUser() (*user.User, error) {
if err == nil { if err == nil {
return u, c.setSystemUser(u) return u, c.setSystemUser(u)
} else if _, ok := err.(user.UnknownUserError); !ok { } else if _, ok := err.(user.UnknownUserError); !ok {
return nil, errors.WithStackIf(err) return nil, err
} }
sysName, err := getSystemName() sysName, err := getSystemName()
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
var command = fmt.Sprintf("useradd --system --no-create-home --shell /bin/false %s", c.System.Username) var command = fmt.Sprintf("useradd --system --no-create-home --shell /bin/false %s", c.System.Username)
@@ -254,17 +248,17 @@ func (c *Configuration) EnsurePterodactylUser() (*user.User, error) {
// We have to create the group first on Alpine, so do that here before continuing on // We have to create the group first on Alpine, so do that here before continuing on
// to the user creation process. // to the user creation process.
if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil { if _, err := exec.Command("addgroup", "-S", c.System.Username).Output(); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
} }
split := strings.Split(command, " ") split := strings.Split(command, " ")
if _, err := exec.Command(split[0], split[1:]...).Output(); err != nil { if _, err := exec.Command(split[0], split[1:]...).Output(); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
if u, err := user.Lookup(c.System.Username); err != nil { if u, err := user.Lookup(c.System.Username); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} else { } else {
return u, c.setSystemUser(u) return u, c.setSystemUser(u)
} }
@@ -306,11 +300,11 @@ func (c *Configuration) WriteToDisk() error {
b, err := yaml.Marshal(&ccopy) b, err := yaml.Marshal(&ccopy)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
if err := ioutil.WriteFile(c.GetPath(), b, 0644); err != nil { if err := ioutil.WriteFile(c.GetPath(), b, 0644); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -320,7 +314,7 @@ func (c *Configuration) WriteToDisk() error {
func getSystemName() (string, error) { func getSystemName() (string, error) {
// use osrelease to get release version and ID // use osrelease to get release version and ID
if release, err := osrelease.Read(); err != nil { if release, err := osrelease.Read(); err != nil {
return "", errors.WithStackIf(err) return "", err
} else { } else {
return release["ID"], nil return release["ID"], nil
} }

View File

@@ -1,7 +1,6 @@
package config package config
import ( import (
"emperror.dev/errors"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@@ -73,7 +72,7 @@ func (c RegistryConfiguration) Base64() (string, error) {
b, err := json.Marshal(authConfig) b, err := json.Marshal(authConfig)
if err != nil { if err != nil {
return "", errors.WithStackIf(err) return "", err
} }
return base64.URLEncoding.EncodeToString(b), nil return base64.URLEncoding.EncodeToString(b), nil

View File

@@ -2,9 +2,9 @@ package config
import ( import (
"context" "context"
"emperror.dev/errors"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"html/template" "html/template"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -53,13 +53,12 @@ type SystemConfiguration struct {
// considered stale and a re-check should occur. DANGER: setting this value too low can seriously // 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 // impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings
// process. // process.
//
// Set to 0 to disable disk checking entirely. This will always return 0 for the disk space used
// by a server and should only be set in extreme scenarios where performance is critical and
// disk usage is not a concern.
DiskCheckInterval int64 `default:"150" yaml:"disk_check_interval"` DiskCheckInterval int64 `default:"150" yaml:"disk_check_interval"`
// 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.
DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`
// If set to true, file permissions for a server will be checked when the process is // If set to true, file permissions for a server will be checked when the process is
// booted. This can cause boot delays if the server has a large amount of files. In most // booted. This can cause boot delays if the server has a large amount of files. In most
// cases disabling this should not have any major impact unless external processes are // cases disabling this should not have any major impact unless external processes are
@@ -74,6 +73,20 @@ type SystemConfiguration struct {
WebsocketLogCount int `default:"150" yaml:"websocket_log_count"` WebsocketLogCount int `default:"150" yaml:"websocket_log_count"`
Sftp SftpConfiguration `yaml:"sftp"` Sftp SftpConfiguration `yaml:"sftp"`
CrashDetection CrashDetection `yaml:"crash_detection"`
}
type CrashDetection struct {
// 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.
DetectCleanExitAsCrash bool `default:"true" yaml:"detect_clean_exit_as_crash"`
// Timeout specifies the timeout between crashes that will not cause the server
// to be automatically restarted, this value is used to prevent servers from
// becoming stuck in a boot-loop after multiple consecutive crashes.
Timeout int `default:"60" json:"timeout"`
} }
// Ensures that all of the system directories exist on the system. These directories are // Ensures that all of the system directories exist on the system. These directories are
@@ -94,7 +107,7 @@ func (sc *SystemConfiguration) ConfigureDirectories() error {
// that. // that.
if d, err := filepath.EvalSymlinks(sc.Data); err != nil { if d, err := filepath.EvalSymlinks(sc.Data); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return errors.WithStackIf(err) return err
} }
} else if d != sc.Data { } else if d != sc.Data {
sc.Data = d sc.Data = d
@@ -130,13 +143,13 @@ func (sc *SystemConfiguration) EnableLogRotation() error {
} }
if st, err := os.Stat("/etc/logrotate.d"); err != nil && !os.IsNotExist(err) { if st, err := os.Stat("/etc/logrotate.d"); err != nil && !os.IsNotExist(err) {
return errors.WithStackIf(err) return err
} else if (err != nil && os.IsNotExist(err)) || !st.IsDir() { } else if (err != nil && os.IsNotExist(err)) || !st.IsDir() {
return nil return nil
} }
if _, err := os.Stat("/etc/logrotate.d/wings"); err != nil && !os.IsNotExist(err) { if _, err := os.Stat("/etc/logrotate.d/wings"); err != nil && !os.IsNotExist(err) {
return errors.WithStackIf(err) return err
} else if err == nil { } else if err == nil {
return nil return nil
} }
@@ -147,7 +160,7 @@ func (sc *SystemConfiguration) EnableLogRotation() error {
// it so files can be rotated easily. // it so files can be rotated easily.
f, err := os.Create("/etc/logrotate.d/wings") f, err := os.Create("/etc/logrotate.d/wings")
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer f.Close() defer f.Close()
@@ -167,10 +180,10 @@ func (sc *SystemConfiguration) EnableLogRotation() error {
}`) }`)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
return errors.WrapIf(t.Execute(f, sc), "failed to write logrotate file to disk") return errors.WithMessage(t.Execute(f, sc), "failed to write logrotate file to disk")
} }
// Returns the location of the JSON file that tracks server states. // Returns the location of the JSON file that tracks server states.
@@ -190,7 +203,7 @@ func (sc *SystemConfiguration) ConfigureTimezone() error {
if sc.Timezone == "" { if sc.Timezone == "" {
if b, err := ioutil.ReadFile("/etc/timezone"); err != nil { if b, err := ioutil.ReadFile("/etc/timezone"); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return errors.WrapIf(err, "failed to open /etc/timezone for automatic server timezone calibration") return errors.WithMessage(err, "failed to open /etc/timezone for automatic server timezone calibration")
} }
ctx, _ := context.WithTimeout(context.Background(), time.Second*5) ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
@@ -224,5 +237,5 @@ func (sc *SystemConfiguration) ConfigureTimezone() error {
_, err := time.LoadLocation(sc.Timezone) _, err := time.LoadLocation(sc.Timezone)
return errors.WrapIf(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone)) return errors.WithMessage(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone))
} }

View File

@@ -1,17 +1,17 @@
version: '3.5' version: '3.8'
services: services:
daemon: wings:
build: . image: ghcr.io/pterodactyl/wings:latest
restart: always restart: always
networks: networks:
- daemon0 - wings0
ports: ports:
- "8080:8080" - "8080:8080"
- "2022:2022" - "2022:2022"
tty: true tty: true
environment: environment:
- "DEBUG=false" TZ: "UTC"
- "TZ=UTC" # change to the three letter timezone of your choosing DEBUG: "false"
volumes: volumes:
- "/var/run/docker.sock:/var/run/docker.sock" - "/var/run/docker.sock:/var/run/docker.sock"
- "/var/lib/docker/containers/:/var/lib/docker/containers/" - "/var/lib/docker/containers/:/var/lib/docker/containers/"
@@ -19,17 +19,16 @@ services:
- "/var/lib/pterodactyl/:/var/lib/pterodactyl/" - "/var/lib/pterodactyl/:/var/lib/pterodactyl/"
- "/var/log/pterodactyl/:/var/log/pterodactyl/" - "/var/log/pterodactyl/:/var/log/pterodactyl/"
- "/tmp/pterodactyl/:/tmp/pterodactyl/" - "/tmp/pterodactyl/:/tmp/pterodactyl/"
## you may need /srv/daemon-data if you are upgrading from an old daemon # you may need /srv/daemon-data if you are upgrading from an old daemon
## - "/srv/daemon-data/:/srv/daemon-data/" #- "/srv/daemon-data/:/srv/daemon-data/"
## Required for ssl if you user let's encrypt. uncomment to use. # Required for ssl if you user let's encrypt. uncomment to use.
## - "/etc/letsencrypt/:/etc/letsencrypt/" #- "/etc/letsencrypt/:/etc/letsencrypt/"
networks: networks:
daemon0: wings0:
name: daemon0 name: wings0
driver: bridge driver: bridge
ipam: ipam:
config: config:
- subnet: "172.21.0.0/16" - subnet: "172.21.0.0/16"
driver_opts: driver_opts:
com.docker.network.bridge.name: daemon0 com.docker.network.bridge.name: wings0

View File

@@ -38,15 +38,16 @@ func (a *Allocations) Bindings() nat.PortMap {
continue continue
} }
binding := []nat.PortBinding{ binding := nat.PortBinding{
{ HostIP: ip,
HostIP: ip, HostPort: strconv.Itoa(port),
HostPort: strconv.Itoa(port),
},
} }
out[nat.Port(fmt.Sprintf("%d/tcp", port))] = binding tcp := nat.Port(fmt.Sprintf("%d/tcp", port))
out[nat.Port(fmt.Sprintf("%d/udp", port))] = binding udp := nat.Port(fmt.Sprintf("%d/udp", port))
out[tcp] = append(out[tcp], binding)
out[udp] = append(out[udp], binding)
} }
} }

View File

@@ -4,7 +4,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
@@ -13,6 +12,7 @@ import (
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/daemon/logger/jsonfilelog"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"io" "io"
@@ -36,7 +36,7 @@ func (e *Environment) Attach() error {
} }
if err := e.followOutput(); err != nil { if err := e.followOutput(); err != nil {
return errors.WithStackIf(err) return err
} }
opts := types.ContainerAttachOptions{ opts := types.ContainerAttachOptions{
@@ -48,7 +48,7 @@ func (e *Environment) Attach() error {
// Set the stream again with the container. // 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(context.Background(), e.Id, opts); err != nil {
return errors.WithStackIf(err) return err
} else { } else {
e.SetStream(&st) e.SetStream(&st)
} }
@@ -70,14 +70,19 @@ func (e *Environment) Attach() error {
// indicates that the container is no longer running. // indicates that the container is no longer running.
go func(ctx context.Context) { go func(ctx context.Context) {
if err := e.pollResources(ctx); err != nil { if err := e.pollResources(ctx); err != nil {
log.WithField("environment_id", e.Id).WithField("error", errors.WithStackIf(err)).Error("error during environment resource polling") l := log.WithField("environment_id", e.Id)
if !errors.Is(err, context.Canceled) {
l.WithField("error", err).Error("error during environment resource polling")
} else {
l.Warn("stopping server resource polling: context canceled")
}
} }
}(ctx) }(ctx)
// Stream the reader output to the console which will then fire off events and handle console // Stream the reader output to the console which will then fire off events and handle console
// throttling and sending the output to the user. // throttling and sending the output to the user.
if _, err := io.Copy(console, e.stream.Reader); err != nil { if _, err := io.Copy(console, e.stream.Reader); err != nil {
log.WithField("environment_id", e.Id).WithField("error", errors.WithStackIf(err)).Error("error while copying environment output to console") log.WithField("environment_id", e.Id).WithField("error", err).Error("error while copying environment output to console")
} }
}(c) }(c)
@@ -115,7 +120,7 @@ func (e *Environment) InSituUpdate() error {
return nil return nil
} }
return errors.WithStackIf(err) return err
} }
u := container.UpdateConfig{ u := container.UpdateConfig{
@@ -125,7 +130,7 @@ func (e *Environment) InSituUpdate() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel() defer cancel()
if _, err := e.client.ContainerUpdate(ctx, e.Id, u); err != nil { if _, err := e.client.ContainerUpdate(ctx, e.Id, u); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -140,12 +145,12 @@ func (e *Environment) Create() error {
if _, err := e.client.ContainerInspect(context.Background(), e.Id); err == nil { if _, err := e.client.ContainerInspect(context.Background(), e.Id); err == nil {
return nil return nil
} else if !client.IsErrNotFound(err) { } else if !client.IsErrNotFound(err) {
return errors.WithStackIf(err) return err
} }
// Try to pull the requested image before creating the container. // Try to pull the requested image before creating the container.
if err := e.ensureImageExists(e.meta.Image); err != nil { if err := e.ensureImageExists(e.meta.Image); err != nil {
return errors.WithStackIf(err) return err
} }
a := e.Configuration.Allocations() a := e.Configuration.Allocations()
@@ -220,7 +225,7 @@ func (e *Environment) Create() error {
} }
if _, err := e.client.ContainerCreate(context.Background(), conf, hostConf, nil, e.Id); err != nil { if _, err := e.client.ContainerCreate(context.Background(), conf, hostConf, nil, e.Id); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -272,7 +277,7 @@ func (e *Environment) Destroy() error {
func (e *Environment) followOutput() error { func (e *Environment) followOutput() error {
if exists, err := e.Exists(); !exists { if exists, err := e.Exists(); !exists {
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
return errors.New(fmt.Sprintf("no such container: %s", e.Id)) return errors.New(fmt.Sprintf("no such container: %s", e.Id))
@@ -291,9 +296,19 @@ func (e *Environment) followOutput() error {
defer reader.Close() defer reader.Close()
r := bufio.NewReader(reader) r := bufio.NewReader(reader)
// Micro-optimization to create these replacements one time when this routine
// fires up, rather than on every line that is executed.
cr := []byte(" \r")
crr := []byte("\r\n")
// Avoid constantly re-allocating memory when we're flooding lines through this
// function by using the same buffer for the duration of the call and just truncating
// the value back to 0 every loop.
var str strings.Builder
ParentLoop: ParentLoop:
for { for {
var b bytes.Buffer str.Reset()
var line []byte var line []byte
var isPrefix bool var isPrefix bool
@@ -305,7 +320,7 @@ func (e *Environment) followOutput() error {
// in line with that it thinks is the terminal size. Those returns break a lot of output handling, // in line with that it thinks is the terminal size. Those returns break a lot of output handling,
// so we'll just replace them with proper new-lines and then split it later and send each line as // so we'll just replace them with proper new-lines and then split it later and send each line as
// its own event in the response. // its own event in the response.
b.Write(bytes.ReplaceAll(line, []byte(" \r"), []byte("\r\n"))) str.Write(bytes.Replace(line, cr, crr, -1))
// Finish this loop and begin outputting the line if there is no prefix (the line fit into // Finish this loop and begin outputting the line if there is no prefix (the line fit into
// the default buffer), or if we hit the end of the line. // the default buffer), or if we hit the end of the line.
@@ -322,7 +337,7 @@ func (e *Environment) followOutput() error {
// Publish the line for this loop. Break on new-line characters so every line is sent as a single // Publish the line for this loop. Break on new-line characters so every line is sent as a single
// output event, otherwise you get funky handling in the browser console. // output event, otherwise you get funky handling in the browser console.
for _, line := range strings.Split(b.String(), "\r\n") { for _, line := range strings.Split(str.String(), "\r\n") {
e.Events().Publish(environment.ConsoleOutputEvent, line) e.Events().Publish(environment.ConsoleOutputEvent, line)
} }
@@ -338,7 +353,7 @@ func (e *Environment) followOutput() error {
} }
}(reader) }(reader)
return errors.WithStackIf(err) return err
} }
// Pulls the image from Docker. If there is an error while pulling the image from the source // Pulls the image from Docker. If there is an error while pulling the image from the source

View File

@@ -2,7 +2,6 @@ package docker
import ( import (
"context" "context"
"emperror.dev/errors"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
@@ -156,7 +155,7 @@ func (e *Environment) ExitState() (uint32, bool, error) {
return 1, false, nil return 1, false, nil
} }
return 0, false, errors.WithStackIf(err) return 0, false, err
} }
return uint32(c.State.ExitCode), c.State.OOMKilled, nil return uint32(c.State.ExitCode), c.State.OOMKilled, nil

View File

@@ -2,11 +2,11 @@ package docker
import ( import (
"context" "context"
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"os" "os"
@@ -26,7 +26,7 @@ func (e *Environment) OnBeforeStart() error {
// the Panel is usee. // the Panel is usee.
if err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil { if err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil {
if !client.IsErrNotFound(err) { if !client.IsErrNotFound(err) {
return errors.WrapIf(err, "failed to remove server docker container during pre-boot") return errors.WithMessage(err, "failed to remove server docker container during pre-boot")
} }
} }
@@ -69,7 +69,7 @@ func (e *Environment) Start() error {
// //
// @see https://github.com/pterodactyl/panel/issues/2000 // @see https://github.com/pterodactyl/panel/issues/2000
if !client.IsErrNotFound(err) { if !client.IsErrNotFound(err) {
return errors.WithStackIf(err) return err
} }
} else { } else {
// If the server is running update our internal state and continue on with the attach. // If the server is running update our internal state and continue on with the attach.
@@ -84,7 +84,7 @@ func (e *Environment) Start() error {
// to truncate them. // to truncate them.
if _, err := os.Stat(c.LogPath); err == nil { if _, err := os.Stat(c.LogPath); err == nil {
if err := os.Truncate(c.LogPath, 0); err != nil { if err := os.Truncate(c.LogPath, 0); err != nil {
return errors.WithStackIf(err) return err
} }
} }
} }
@@ -99,14 +99,14 @@ func (e *Environment) Start() error {
// exists on the system, and rebuild the container if that is required for server booting to // exists on the system, and rebuild the container if that is required for server booting to
// occur. // occur.
if err := e.OnBeforeStart(); err != nil { if err := e.OnBeforeStart(); err != nil {
return errors.WithStackIf(err) return err
} }
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel() defer cancel()
if err := e.client.ContainerStart(ctx, e.Id, types.ContainerStartOptions{}); err != nil { if err := e.client.ContainerStart(ctx, e.Id, types.ContainerStartOptions{}); err != nil {
return errors.WithStackIf(err) return err
} }
// No errors, good to continue through. // No errors, good to continue through.
@@ -169,7 +169,7 @@ func (e *Environment) Stop() error {
// will be terminated forcefully depending on the value of the second argument. // will be terminated forcefully depending on the value of the second argument.
func (e *Environment) WaitForStop(seconds uint, terminate bool) error { func (e *Environment) WaitForStop(seconds uint, terminate bool) error {
if err := e.Stop(); err != nil { if err := e.Stop(); err != nil {
return errors.WithStackIf(err) return err
} }
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(seconds)*time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Duration(seconds)*time.Second)
@@ -183,22 +183,27 @@ func (e *Environment) WaitForStop(seconds uint, terminate bool) error {
case <-ctx.Done(): case <-ctx.Done():
if ctxErr := ctx.Err(); ctxErr != nil { if ctxErr := ctx.Err(); ctxErr != nil {
if terminate { if terminate {
log.WithField("container_id", e.Id).Debug("server did not stop in time, executing process termination") log.WithField("container_id", e.Id).Info("server did not stop in time, executing process termination")
return errors.WithStackIf(e.Terminate(os.Kill)) return e.Terminate(os.Kill)
} }
return errors.WithStackIf(ctxErr) return ctxErr
} }
case err := <-errChan: case err := <-errChan:
if err != nil { if err != nil {
if terminate { if terminate {
log.WithField("container_id", e.Id).WithField("error", errors.WithStackIf(err)).Warn("error while waiting for container stop, attempting process termination") l := log.WithField("container_id", e.Id)
if errors.Is(err, context.DeadlineExceeded) {
l.Warn("deadline exceeded for container stop; terminating process")
} else {
l.WithField("error", err).Warn("error while waiting for container stop; terminating process")
}
return errors.WithStackIf(e.Terminate(os.Kill)) return e.Terminate(os.Kill)
} }
return errors.WithStackIf(err) return err
} }
case <-ok: case <-ok:
} }
@@ -210,7 +215,7 @@ func (e *Environment) WaitForStop(seconds uint, terminate bool) error {
func (e *Environment) Terminate(signal os.Signal) error { func (e *Environment) Terminate(signal os.Signal) error {
c, err := e.client.ContainerInspect(context.Background(), e.Id) c, err := e.client.ContainerInspect(context.Background(), e.Id)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
if !c.State.Running { if !c.State.Running {

View File

@@ -1,8 +1,8 @@
package docker package docker
import ( import (
"emperror.dev/errors"
"fmt" "fmt"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
) )

View File

@@ -2,10 +2,10 @@ package docker
import ( import (
"context" "context"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"io" "io"
"math" "math"
@@ -15,18 +15,17 @@ import (
// Attach to the instance and then automatically emit an event whenever the resource usage for the // Attach to the instance and then automatically emit an event whenever the resource usage for the
// server process changes. // server process changes.
func (e *Environment) pollResources(ctx context.Context) error { func (e *Environment) pollResources(ctx context.Context) error {
l := log.WithField("container_id", e.Id)
l.Debug("starting resource polling for container")
defer l.Debug("stopped resource polling for container")
if e.st.Load() == environment.ProcessOfflineState { if e.st.Load() == environment.ProcessOfflineState {
return errors.New("cannot enable resource polling on a stopped server") return errors.New("cannot enable resource polling on a stopped server")
} }
l := log.WithField("container_id", e.Id)
l.Debug("starting resource polling for container")
defer l.Debug("stopped resource polling for container")
stats, err := e.client.ContainerStats(context.Background(), e.Id, true) stats, err := e.client.ContainerStats(context.Background(), e.Id, true)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer stats.Body.Close() defer stats.Body.Close()
@@ -41,7 +40,7 @@ func (e *Environment) pollResources(ctx context.Context) error {
if err := dec.Decode(&v); err != nil { if err := dec.Decode(&v); err != nil {
if err != io.EOF { if err != io.EOF {
l.WithField("error", errors.WithStackIf(err)).Warn("error while processing Docker stats output for container") l.WithField("error", err).Warn("error while processing Docker stats output for container")
} else { } else {
l.Debug("io.EOF encountered during stats decode, stopping polling...") l.Debug("io.EOF encountered during stats decode, stopping polling...")
} }
@@ -76,7 +75,7 @@ func (e *Environment) pollResources(ctx context.Context) error {
} }
if b, err := json.Marshal(st); err != nil { if b, err := json.Marshal(st); err != nil {
l.WithField("error", errors.WithStackIf(err)).Warn("error while marshaling stats object for environment") l.WithField("error", err).Warn("error while marshaling stats object for environment")
} else { } else {
e.Events().Publish(environment.ResourceEvent, string(b)) e.Events().Publish(environment.ResourceEvent, string(b))
} }

View File

@@ -4,9 +4,9 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"strconv" "strconv"
) )
@@ -15,7 +15,7 @@ type dockerLogLine struct {
Log string `json:"log"` Log string `json:"log"`
} }
var ErrNotAttached = errors.Sentinel("not attached to instance") var ErrNotAttached = errors.New("not attached to instance")
func (e *Environment) setStream(s *types.HijackedResponse) { func (e *Environment) setStream(s *types.HijackedResponse) {
e.mu.Lock() e.mu.Lock()
@@ -42,7 +42,7 @@ func (e *Environment) SendCommand(c string) error {
_, err := e.stream.Conn.Write([]byte(c + "\n")) _, err := e.stream.Conn.Write([]byte(c + "\n"))
return errors.WithStackIf(err) return err
} }
// Reads the log file for the server. This does not care if the server is running or not, it will // Reads the log file for the server. This does not care if the server is running or not, it will
@@ -54,7 +54,7 @@ func (e *Environment) Readlog(lines int) ([]string, error) {
Tail: strconv.Itoa(lines), Tail: strconv.Itoa(lines),
}) })
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
defer r.Close() defer r.Close()

View File

@@ -1,7 +1,6 @@
package events package events
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/gammazero/workerpool" "github.com/gammazero/workerpool"
"strings" "strings"
@@ -69,7 +68,7 @@ func (e *EventBus) Publish(topic string, data string) {
func (e *EventBus) PublishJson(topic string, data interface{}) error { func (e *EventBus) PublishJson(topic string, data interface{}) error {
b, err := json.Marshal(data) b, err := json.Marshal(data)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
e.Publish(topic, string(b)) e.Publish(topic, string(b))

3
go.mod
View File

@@ -3,7 +3,6 @@ module github.com/pterodactyl/wings
go 1.13 go 1.13
require ( require (
emperror.dev/errors v0.8.0
github.com/AlecAivazis/survey/v2 v2.1.0 github.com/AlecAivazis/survey/v2 v2.1.0
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Jeffail/gabs/v2 v2.5.1 github.com/Jeffail/gabs/v2 v2.5.1
@@ -18,7 +17,6 @@ require (
github.com/containerd/containerd v1.3.7 // indirect github.com/containerd/containerd v1.3.7 // indirect
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b // indirect github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b // indirect
github.com/creasty/defaults v1.5.0 github.com/creasty/defaults v1.5.0
github.com/docker/cli v17.12.1-ce-rc2+incompatible
github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
@@ -58,6 +56,7 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.5.0 github.com/pkg/profile v1.5.0
github.com/pkg/sftp v1.11.0 github.com/pkg/sftp v1.11.0
github.com/prometheus/common v0.11.1 // indirect github.com/prometheus/common v0.11.1 // indirect

8
go.sum
View File

@@ -1,7 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
emperror.dev/errors v0.8.0 h1:4lycVEx0sdJkwDUfQ9pdu6SR0x7rgympt5f4+ok8jDk=
emperror.dev/errors v0.8.0/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE=
github.com/AlecAivazis/survey/v2 v2.1.0 h1:AT4+23hOFopXYZaNGugbk7MWItkz0SfTmH/Hk92KeeE= github.com/AlecAivazis/survey/v2 v2.1.0 h1:AT4+23hOFopXYZaNGugbk7MWItkz0SfTmH/Hk92KeeE=
github.com/AlecAivazis/survey/v2 v2.1.0/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/AlecAivazis/survey/v2 v2.1.0/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
@@ -95,8 +93,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/cli v17.12.1-ce-rc2+incompatible h1:ESUycEAqvFuLglAHkUW66rCc2djYtd3i1x231svLq9o=
github.com/docker/cli v17.12.1-ce-rc2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8= github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8=
@@ -563,13 +559,9 @@ go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=

View File

@@ -1,10 +1,10 @@
package installer package installer
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
@@ -43,21 +43,21 @@ func New(data []byte) (*Installer, error) {
// Unmarshal the environment variables from the request into the server struct. // Unmarshal the environment variables from the request into the server struct.
if b, _, _, err := jsonparser.Get(data, "environment"); err != nil { if b, _, _, err := jsonparser.Get(data, "environment"); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} else { } else {
cfg.EnvVars = make(environment.Variables) cfg.EnvVars = make(environment.Variables)
if err := json.Unmarshal(b, &cfg.EnvVars); err != nil { if err := json.Unmarshal(b, &cfg.EnvVars); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
} }
// Unmarshal the allocation mappings from the request into the server struct. // Unmarshal the allocation mappings from the request into the server struct.
if b, _, _, err := jsonparser.Get(data, "allocations", "mappings"); err != nil { if b, _, _, err := jsonparser.Get(data, "allocations", "mappings"); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} else { } else {
cfg.Allocations.Mappings = make(map[string][]int) cfg.Allocations.Mappings = make(map[string][]int)
if err := json.Unmarshal(b, &cfg.Allocations.Mappings); err != nil { if err := json.Unmarshal(b, &cfg.Allocations.Mappings); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
} }
@@ -66,7 +66,7 @@ func New(data []byte) (*Installer, error) {
c, err := api.New().GetServerConfiguration(cfg.Uuid) c, err := api.New().GetServerConfiguration(cfg.Uuid)
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
return nil, errors.WithStackIf(err) return nil, err
} }
return nil, errors.New(err.Error()) return nil, errors.New(err.Error())

View File

@@ -6,16 +6,17 @@ import (
"github.com/apex/log/handlers/cli" "github.com/apex/log/handlers/cli"
color2 "github.com/fatih/color" color2 "github.com/fatih/color"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
"emperror.dev/errors" "github.com/pkg/errors"
"io" "io"
"math"
"os" "os"
"sync" "sync"
"time" "time"
) )
var Default = New(os.Stderr, true) var Default = New(os.Stderr, true)
var bold = color2.New(color2.Bold) var bold = color2.New(color2.Bold)
var boldred = color2.New(color2.Bold, color2.FgRed)
var Strings = [...]string{ var Strings = [...]string{
log.DebugLevel: "DEBUG", log.DebugLevel: "DEBUG",
@@ -60,7 +61,6 @@ func (h *Handler) HandleLog(e *log.Entry) error {
if name == "source" { if name == "source" {
continue continue
} }
fmt.Fprintf(h.Writer, " %s=%v", color.Sprint(name), e.Fields.Get(name)) fmt.Fprintf(h.Writer, " %s=%v", color.Sprint(name), e.Fields.Get(name))
} }
@@ -70,45 +70,16 @@ func (h *Handler) HandleLog(e *log.Entry) error {
if name != "error" { if name != "error" {
continue continue
} }
var br = color2.New(color2.Bold, color2.FgRed)
if err, ok := e.Fields.Get("error").(error); ok { if err, ok := e.Fields.Get("error").(error); ok {
fmt.Fprintf(h.Writer, "\n%s%+v\n\n", br.Sprintf("Stacktrace:"), getErrorStack(err, false)) if e, ok := errors.Cause(err).(tracer); ok {
} else { st := e.StackTrace()
fmt.Fprintf(h.Writer, "\n%s%+v\n\n", br.Sprintf("Invalid Error:"), err) l := math.Min(float64(len(st)), 10)
fmt.Fprintf(h.Writer, "\n%s%+v\n\n", boldred.Sprintf("Stacktrace:"), st[0:int(l)])
} else {
fmt.Fprintf(h.Writer, "\n%s\n%+v\n\n", boldred.Sprintf("Stacktrace:"), err)
}
} }
} }
return nil return nil
} }
func getErrorStack(err error, i bool) errors.StackTrace {
e, ok := err.(tracer)
if !ok {
if i {
// Just abort out of this and return a stacktrace leading up to this point. It isn't perfect
// but it'll at least include what function lead to this being called which we can then handle.
return errors.WrapIf(err, "failed to generate stacktrace for caught error").(tracer).StackTrace()
}
return getErrorStack(errors.WrapIf(err, err.Error()), true)
}
st := e.StackTrace()
l := len(st)
// If this was an internal stack generation we're going to skip over the top four items in the stack
// trace since they'll point to the error that was generated by this function.
f := 0
if i {
f = 5
}
if i && l > 9 {
l = 9
} else if !i && l > 5 {
l = 5
}
return st[f:l]
}

View File

View File

@@ -2,11 +2,11 @@ package parser
import ( import (
"bytes" "bytes"
"emperror.dev/errors"
"github.com/Jeffail/gabs/v2" "github.com/Jeffail/gabs/v2"
"github.com/apex/log" "github.com/apex/log"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/iancoleman/strcase" "github.com/iancoleman/strcase"
"github.com/pkg/errors"
"io/ioutil" "io/ioutil"
"os" "os"
"regexp" "regexp"
@@ -76,13 +76,13 @@ func (cfr *ConfigurationFileReplacement) getKeyValue(value []byte) interface{} {
func (f *ConfigurationFile) IterateOverJson(data []byte) (*gabs.Container, error) { func (f *ConfigurationFile) IterateOverJson(data []byte) (*gabs.Container, error) {
parsed, err := gabs.ParseJSON(data) parsed, err := gabs.ParseJSON(data)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
for _, v := range f.Replace { for _, v := range f.Replace {
value, err := f.LookupConfigurationValue(v) value, err := f.LookupConfigurationValue(v)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
// Check for a wildcard character, and if found split the key on that value to // Check for a wildcard character, and if found split the key on that value to
@@ -101,7 +101,7 @@ func (f *ConfigurationFile) IterateOverJson(data []byte) (*gabs.Container, error
continue continue
} }
return nil, errors.WrapIf(err, "failed to set config value of array child") return nil, errors.WithMessage(err, "failed to set config value of array child")
} }
} }
} else { } else {
@@ -110,7 +110,7 @@ func (f *ConfigurationFile) IterateOverJson(data []byte) (*gabs.Container, error
continue continue
} }
return nil, errors.WrapIf(err, "unable to set config value at pathway: "+v.Match) return nil, errors.WithMessage(err, "unable to set config value at pathway: "+v.Match)
} }
} }
} }
@@ -138,7 +138,7 @@ func setValueAtPath(c *gabs.Container, path string, value interface{}) error {
_, err = c.SetP(value, path) _, err = c.SetP(value, path)
} }
return errors.WithStackIf(err) return err
} }
i, _ := strconv.Atoi(matches[2]) i, _ := strconv.Atoi(matches[2])
@@ -147,7 +147,7 @@ func setValueAtPath(c *gabs.Container, path string, value interface{}) error {
ct, err := c.ArrayElementP(i, matches[1]) ct, err := c.ArrayElementP(i, matches[1])
if err != nil { if err != nil {
if i != 0 || (!errors.Is(err, gabs.ErrNotArray) && !errors.Is(err, gabs.ErrNotFound)) { if i != 0 || (!errors.Is(err, gabs.ErrNotArray) && !errors.Is(err, gabs.ErrNotFound)) {
return errors.WrapIf(err, "error while parsing array element at path") return errors.WithMessage(err, "error while parsing array element at path")
} }
var t = make([]interface{}, 1) var t = make([]interface{}, 1)
@@ -162,7 +162,7 @@ func setValueAtPath(c *gabs.Container, path string, value interface{}) error {
// an empty object if we have additional things to set on the array, or just an empty array type // an empty object if we have additional things to set on the array, or just an empty array type
// if there is not an object structure detected (no matches[3] available). // if there is not an object structure detected (no matches[3] available).
if _, err = c.SetP(t, matches[1]); err != nil { if _, err = c.SetP(t, matches[1]); err != nil {
return errors.WrapIf(err, "failed to create empty array for missing element") return errors.WithMessage(err, "failed to create empty array for missing element")
} }
// Set our cursor to be the array element we expect, which in this case is just the first element // Set our cursor to be the array element we expect, which in this case is just the first element
@@ -170,7 +170,7 @@ func setValueAtPath(c *gabs.Container, path string, value interface{}) error {
// to match additional elements. In those cases the server will just have to be rebooted or something. // to match additional elements. In those cases the server will just have to be rebooted or something.
ct, err = c.ArrayElementP(0, matches[1]) ct, err = c.ArrayElementP(0, matches[1])
if err != nil { if err != nil {
return errors.WrapIf(err, "failed to find array element at path") return errors.WithMessage(err, "failed to find array element at path")
} }
} }
@@ -187,7 +187,7 @@ func setValueAtPath(c *gabs.Container, path string, value interface{}) error {
} }
if err != nil { if err != nil {
return errors.WrapIf(err, "failed to set value at config path: "+path) return errors.WithMessage(err, "failed to set value at config path: "+path)
} }
return nil return nil
@@ -253,7 +253,7 @@ func (f *ConfigurationFile) LookupConfigurationValue(cfr ConfigurationFileReplac
match, _, _, err := jsonparser.Get(f.configuration, path...) match, _, _, err := jsonparser.Get(f.configuration, path...)
if err != nil { if err != nil {
if err != jsonparser.KeyPathNotFoundError { if err != jsonparser.KeyPathNotFoundError {
return string(match), errors.WithStackIf(err) return string(match), err
} }
log.WithFields(log.Fields{"path": path, "filename": f.FileName}).Debug("attempted to load a configuration value that does not exist") log.WithFields(log.Fields{"path": path, "filename": f.FileName}).Debug("attempted to load a configuration value that does not exist")

View File

@@ -2,13 +2,13 @@ package parser
import ( import (
"bufio" "bufio"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/apex/log" "github.com/apex/log"
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/icza/dyno" "github.com/icza/dyno"
"github.com/magiconair/properties" "github.com/magiconair/properties"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@@ -166,17 +166,17 @@ func (f *ConfigurationFile) Parse(path string, internal bool) error {
b := strings.TrimSuffix(path, filepath.Base(path)) b := strings.TrimSuffix(path, filepath.Base(path))
if err := os.MkdirAll(b, 0755); err != nil { if err := os.MkdirAll(b, 0755); err != nil {
return errors.WrapIf(err, "failed to create base directory for missing configuration file") return errors.WithMessage(err, "failed to create base directory for missing configuration file")
} else { } else {
if _, err := os.Create(path); err != nil { if _, err := os.Create(path); err != nil {
return errors.WrapIf(err, "failed to create missing configuration file") return errors.WithMessage(err, "failed to create missing configuration file")
} }
} }
return f.Parse(path, true) return f.Parse(path, true)
} }
return errors.WithStackIf(err) return err
} }
// Parses an xml file. // Parses an xml file.
@@ -348,12 +348,12 @@ func (f *ConfigurationFile) parseJsonFile(path string) error {
func (f *ConfigurationFile) parseYamlFile(path string) error { func (f *ConfigurationFile) parseYamlFile(path string) error {
b, err := readFileBytes(path) b, err := readFileBytes(path)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
i := make(map[string]interface{}) i := make(map[string]interface{})
if err := yaml.Unmarshal(b, &i); err != nil { if err := yaml.Unmarshal(b, &i); err != nil {
return errors.WithStackIf(err) return err
} }
// Unmarshal the yaml data into a JSON interface such that we can work with // Unmarshal the yaml data into a JSON interface such that we can work with
@@ -361,20 +361,20 @@ func (f *ConfigurationFile) parseYamlFile(path string) error {
// makes working with unknown JSON significantly easier. // makes working with unknown JSON significantly easier.
jsonBytes, err := json.Marshal(dyno.ConvertMapI2MapS(i)) jsonBytes, err := json.Marshal(dyno.ConvertMapI2MapS(i))
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
// Now that the data is converted, treat it just like JSON and pass it to the // Now that the data is converted, treat it just like JSON and pass it to the
// iterator function to update values as necessary. // iterator function to update values as necessary.
data, err := f.IterateOverJson(jsonBytes) data, err := f.IterateOverJson(jsonBytes)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
// Remarshal the JSON into YAML format before saving it back to the disk. // Remarshal the JSON into YAML format before saving it back to the disk.
marshaled, err := yaml.Marshal(data.Data()) marshaled, err := yaml.Marshal(data.Data())
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
return ioutil.WriteFile(path, marshaled, 0644) return ioutil.WriteFile(path, marshaled, 0644)
@@ -386,7 +386,7 @@ func (f *ConfigurationFile) parseYamlFile(path string) error {
func (f *ConfigurationFile) parseTextFile(path string) error { func (f *ConfigurationFile) parseTextFile(path string) error {
input, err := ioutil.ReadFile(path) input, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
lines := strings.Split(string(input), "\n") lines := strings.Split(string(input), "\n")
@@ -403,7 +403,7 @@ func (f *ConfigurationFile) parseTextFile(path string) error {
} }
if err := ioutil.WriteFile(path, []byte(strings.Join(lines, "\n")), 0644); err != nil { if err := ioutil.WriteFile(path, []byte(strings.Join(lines, "\n")), 0644); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -415,7 +415,7 @@ func (f *ConfigurationFile) parsePropertiesFile(path string) error {
// Open the file. // Open the file.
f2, err := os.Open(path) f2, err := os.Open(path)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
var s strings.Builder var s strings.Builder
@@ -437,20 +437,20 @@ func (f *ConfigurationFile) parsePropertiesFile(path string) error {
// Handle any scanner errors. // Handle any scanner errors.
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return errors.WithStackIf(err) return err
} }
// Decode the properties file. // Decode the properties file.
p, err := properties.LoadFile(path, properties.UTF8) p, err := properties.LoadFile(path, properties.UTF8)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
// Replace any values that need to be replaced. // Replace any values that need to be replaced.
for _, replace := range f.Replace { for _, replace := range f.Replace {
data, err := f.LookupConfigurationValue(replace) data, err := f.LookupConfigurationValue(replace)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
v, ok := p.Get(replace.Match) v, ok := p.Get(replace.Match)
@@ -462,7 +462,7 @@ func (f *ConfigurationFile) parsePropertiesFile(path string) error {
} }
if _, _, err := p.Set(replace.Match, data); err != nil { if _, _, err := p.Set(replace.Match, data); err != nil {
return errors.WithStackIf(err) return err
} }
} }
@@ -482,7 +482,7 @@ func (f *ConfigurationFile) parsePropertiesFile(path string) error {
// Open the file for writing. // Open the file for writing.
w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer w.Close() defer w.Close()

View File

@@ -1,11 +1,11 @@
package router package router
import ( import (
"emperror.dev/errors"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
"net/http" "net/http"
@@ -75,7 +75,7 @@ func (e *RequestError) AbortWithStatus(status int, c *gin.Context) {
if status >= 500 { if status >= 500 {
e.logger().WithField("error", e.Err).Error("encountered HTTP/500 error while handling request") e.logger().WithField("error", e.Err).Error("encountered HTTP/500 error while handling request")
c.Error(errors.WithStackIf(e)) c.Error(e)
} else { } else {
e.logger().WithField("error", e.Err).Debug("encountered non-HTTP/500 error while handling request") e.logger().WithField("error", e.Err).Debug("encountered non-HTTP/500 error while handling request")
} }

View File

@@ -87,6 +87,7 @@ func Configure() *gin.Engine {
files.POST("/delete", postServerDeleteFiles) files.POST("/delete", postServerDeleteFiles)
files.POST("/compress", postServerCompressFiles) files.POST("/compress", postServerCompressFiles)
files.POST("/decompress", postServerDecompressFiles) files.POST("/decompress", postServerDecompressFiles)
files.POST("/chmod", postServerChmodFile)
} }
backup := server.Group("/backup") backup := server.Group("/backup")

View File

@@ -3,9 +3,9 @@ package router
import ( import (
"bytes" "bytes"
"context" "context"
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/router/tokens" "github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
"net/http" "net/http"
@@ -227,7 +227,7 @@ func deleteServer(c *gin.Context) {
if err := os.RemoveAll(p); err != nil { if err := os.RemoveAll(p); err != nil {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"path": p, "path": p,
"error": errors.WithStackIf(err), "error": err,
}).Warn("failed to remove server files during deletion process") }).Warn("failed to remove server files during deletion process")
} }
}(s.Filesystem().Path()) }(s.Filesystem().Path())
@@ -247,7 +247,9 @@ func deleteServer(c *gin.Context) {
// preventing any JWT generated before the current time from being used to connect to // preventing any JWT generated before the current time from being used to connect to
// the socket or send along commands. // the socket or send along commands.
func postServerDenyWSTokens(c *gin.Context) { func postServerDenyWSTokens(c *gin.Context) {
var data struct{ JTIs []string `json:"jtis"` } var data struct {
JTIs []string `json:"jtis"`
}
if err := c.BindJSON(&data); err != nil { if err := c.BindJSON(&data); err != nil {
return return

View File

@@ -2,8 +2,9 @@ package router
import ( import (
"context" "context"
"emperror.dev/errors" "github.com/apex/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/router/tokens" "github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
@@ -365,6 +366,68 @@ func postServerDecompressFiles(c *gin.Context) {
c.Status(http.StatusNoContent) c.Status(http.StatusNoContent)
} }
type chmodFile struct {
File string `json:"file"`
Mode string `json:"mode"`
}
func postServerChmodFile(c *gin.Context) {
s := GetServer(c.Param("server"))
var data struct {
Root string `json:"root"`
Files []chmodFile `json:"files"`
}
if err := c.BindJSON(&data); err != nil {
log.Debug(err.Error())
return
}
if len(data.Files) == 0 {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
"error": "No files to chmod were provided.",
})
return
}
g, ctx := errgroup.WithContext(context.Background())
// Loop over the array of files passed in and perform the move or rename action against each.
for _, p := range data.Files {
g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
mode, err := strconv.ParseUint(p.Mode, 8, 32)
if err != nil {
return err
}
if err := s.Filesystem().Chmod(path.Join(data.Root, p.File), os.FileMode(mode)); err != nil {
// Return nil if the error is an is not exists.
// NOTE: os.IsNotExist() does not work if the error is wrapped.
if errors.Is(err, os.ErrNotExist) {
return nil
}
return err
}
return nil
}
})
}
if err := g.Wait(); err != nil {
TrackedServerError(err, s).AbortFilesystemError(c)
return
}
c.Status(http.StatusNoContent)
}
func postServerUploadFiles(c *gin.Context) { func postServerUploadFiles(c *gin.Context) {
token := tokens.UploadPayload{} token := tokens.UploadPayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil { if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
@@ -422,12 +485,12 @@ func postServerUploadFiles(c *gin.Context) {
func handleFileUpload(p string, s *server.Server, header *multipart.FileHeader) error { func handleFileUpload(p string, s *server.Server, header *multipart.FileHeader) error {
file, err := header.Open() file, err := header.Open()
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer file.Close() defer file.Close()
if err := s.Filesystem().Writefile(p, file); err != nil { if err := s.Filesystem().Writefile(p, file); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil

View File

@@ -89,8 +89,8 @@ func postUpdateConfiguration(c *gin.Context) {
// //
// If you pass through manual locations in the API call this logic will be skipped. // If you pass through manual locations in the API call this logic will be skipped.
if strings.HasPrefix(cfg.Api.Ssl.KeyFile, "/etc/letsencrypt/live/") { if strings.HasPrefix(cfg.Api.Ssl.KeyFile, "/etc/letsencrypt/live/") {
cfg.Api.Ssl.KeyFile = ccopy.Api.Ssl.KeyFile cfg.Api.Ssl.KeyFile = strings.ToLower(ccopy.Api.Ssl.KeyFile)
cfg.Api.Ssl.CertificateFile = ccopy.Api.Ssl.CertificateFile cfg.Api.Ssl.CertificateFile = strings.ToLower(ccopy.Api.Ssl.CertificateFile)
} }
config.Set(&cfg) config.Set(&cfg)

View File

@@ -4,12 +4,12 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"emperror.dev/errors"
"encoding/hex" "encoding/hex"
"github.com/apex/log" "github.com/apex/log"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/mholt/archiver/v3" "github.com/mholt/archiver/v3"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/installer" "github.com/pterodactyl/wings/installer"
@@ -94,27 +94,40 @@ func postServerArchive(c *gin.Context) {
s := GetServer(c.Param("server")) s := GetServer(c.Param("server"))
go func(s *server.Server) { go func(s *server.Server) {
r := api.New()
// Attempt to get an archive of the server. This **WILL NOT** modify the source files of a server,
// this process is 100% safe and will not corrupt a server's files if it fails.
if err := s.Archiver.Archive(); err != nil { if err := s.Archiver.Archive(); err != nil {
s.Log().WithField("error", err).Error("failed to get archive for server") s.Log().WithField("error", err).Error("failed to get archive for server")
if err := r.SendArchiveStatus(s.Id(), false); err != nil {
if !api.IsRequestError(err) {
s.Log().WithField("error", err).Error("failed to notify panel of failed archive status")
return
}
s.Log().WithField("error", err.Error()).Error("panel returned an error when notifying it of a failed archive status")
return
}
s.Log().Info("successfully notified panel of failed archive status")
return return
} }
s.Log().Debug("successfully created server archive, notifying panel") s.Log().Debug("successfully created server archive, notifying panel")
r := api.New() if err := r.SendArchiveStatus(s.Id(), true); err != nil {
err := r.SendArchiveStatus(s.Id(), true)
if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
s.Log().WithField("error", err).Error("failed to notify panel of archive status") s.Log().WithField("error", err).Error("failed to notify panel of successful archive status")
return return
} }
s.Log().WithField("error", err.Error()).Error("panel returned an error when sending the archive status") s.Log().WithField("error", err.Error()).Error("panel returned an error when notifying it of a successful archive status")
return return
} }
s.Log().Debug("successfully notified panel of archive status") s.Log().Info("successfully notified panel of successful archive status")
}(s) }(s)
c.Status(http.StatusAccepted) c.Status(http.StatusAccepted)
@@ -140,8 +153,7 @@ func postTransfer(c *gin.Context) {
} }
l.Info("server transfer failed, notifying panel") l.Info("server transfer failed, notifying panel")
err := api.New().SendTransferFailure(serverID) if err := api.New().SendTransferFailure(serverID); err != nil {
if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
l.WithField("error", err).Error("failed to notify panel with transfer failure") l.WithField("error", err).Error("failed to notify panel with transfer failure")
return return
@@ -157,7 +169,7 @@ func postTransfer(c *gin.Context) {
// Make a new GET request to the URL the panel gave us. // Make a new GET request to the URL the panel gave us.
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
log.WithField("error", errors.WithStackIf(err)).Error("failed to create http request for archive transfer") log.WithField("error", err).Error("failed to create http request for archive transfer")
return return
} }
@@ -167,7 +179,7 @@ func postTransfer(c *gin.Context) {
// Execute the http request. // Execute the http request.
res, err := client.Do(req) res, err := client.Do(req)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to send archive http request") l.WithField("error", err).Error("failed to send archive http request")
return return
} }
defer res.Body.Close() defer res.Body.Close()
@@ -176,13 +188,11 @@ func postTransfer(c *gin.Context) {
if res.StatusCode != 200 { if res.StatusCode != 200 {
_, err := ioutil.ReadAll(res.Body) _, err := ioutil.ReadAll(res.Body)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).WithField("status", res.StatusCode).Error("failed read transfer response body") l.WithField("error", err).WithField("status", res.StatusCode).Error("failed read transfer response body")
return return
} }
l.WithField("error", errors.WithStackIf(err)).WithField("status", res.StatusCode).Error("failed to request server archive") l.WithField("error", err).WithField("status", res.StatusCode).Error("failed to request server archive")
return return
} }
@@ -190,16 +200,14 @@ func postTransfer(c *gin.Context) {
archivePath := filepath.Join(config.Get().System.ArchiveDirectory, serverID+".tar.gz") archivePath := filepath.Join(config.Get().System.ArchiveDirectory, serverID+".tar.gz")
// Check if the archive already exists and delete it if it does. // Check if the archive already exists and delete it if it does.
_, err = os.Stat(archivePath) if _, err = os.Stat(archivePath); err != nil {
if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
l.WithField("error", errors.WithStackIf(err)).Error("failed to stat archive file") l.WithField("error", err).Error("failed to stat archive file")
return return
} }
} else { } else {
if err := os.Remove(archivePath); err != nil { if err := os.Remove(archivePath); err != nil {
l.WithField("error", errors.WithStackIf(err)).Warn("failed to remove old archive file") l.WithField("error", err).Warn("failed to remove old archive file")
return return
} }
} }
@@ -207,8 +215,7 @@ func postTransfer(c *gin.Context) {
// Create the file. // Create the file.
file, err := os.Create(archivePath) file, err := os.Create(archivePath)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to open archive on disk") l.WithField("error", err).Error("failed to open archive on disk")
return return
} }
@@ -216,24 +223,35 @@ func postTransfer(c *gin.Context) {
buf := make([]byte, 1024*4) buf := make([]byte, 1024*4)
_, err = io.CopyBuffer(file, res.Body, buf) _, err = io.CopyBuffer(file, res.Body, buf)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to copy archive file to disk") l.WithField("error", err).Error("failed to copy archive file to disk")
return return
} }
// Close the file so it can be opened to verify the checksum. // Close the file so it can be opened to verify the checksum.
if err := file.Close(); err != nil { if err := file.Close(); err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to close archive file") l.WithField("error", err).Error("failed to close archive file")
return return
} }
l.WithField("server", serverID).Debug("server archive downloaded, computing checksum...") // Whenever the transfer fails or succeeds, delete the temporary transfer archive.
defer func() {
log.WithField("server", serverID).Debug("deleting temporary transfer archive..")
if err := os.Remove(archivePath); err != nil && !os.IsNotExist(err) {
l.WithFields(log.Fields{
"server": serverID,
"error": err,
}).Warn("failed to delete transfer archive")
} else {
l.Debug("deleted temporary transfer archive successfully")
}
}()
l.Debug("server archive downloaded, computing checksum...")
// Open the archive file for computing a checksum. // Open the archive file for computing a checksum.
file, err = os.Open(archivePath) file, err = os.Open(archivePath)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to open archive on disk") l.WithField("error", err).Error("failed to open archive on disk")
return return
} }
@@ -241,7 +259,7 @@ func postTransfer(c *gin.Context) {
hash := sha256.New() hash := sha256.New()
buf = make([]byte, 1024*4) buf = make([]byte, 1024*4)
if _, err := io.CopyBuffer(hash, file, buf); err != nil { if _, err := io.CopyBuffer(hash, file, buf); err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to copy archive file for checksum verification") l.WithField("error", err).Error("failed to copy archive file for checksum verification")
return return
} }
@@ -253,7 +271,7 @@ func postTransfer(c *gin.Context) {
// Close the file. // Close the file.
if err := file.Close(); err != nil { if err := file.Close(); err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to close archive file after calculating checksum") l.WithField("error", err).Error("failed to close archive file after calculating checksum")
return return
} }
@@ -269,7 +287,7 @@ func postTransfer(c *gin.Context) {
// Create a new server installer (note this does not execute the install script) // Create a new server installer (note this does not execute the install script)
i, err := installer.New(serverData) i, err := installer.New(serverData)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to validate received server data") l.WithField("error", err).Error("failed to validate received server data")
return return
} }
@@ -282,9 +300,9 @@ func postTransfer(c *gin.Context) {
return return
} }
// Un-archive the archive. That sounds weird.. // Un-archive the archive, that sounds weird..
if err := archiver.NewTarGz().Unarchive(archivePath, i.Server().Filesystem().Path()); err != nil { if err := archiver.NewTarGz().Unarchive(archivePath, i.Server().Filesystem().Path()); err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to extract server archive") l.WithField("error", err).Error("failed to extract server archive")
return return
} }
@@ -299,12 +317,11 @@ func postTransfer(c *gin.Context) {
err = api.New().SendTransferSuccess(serverID) err = api.New().SendTransferSuccess(serverID)
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
l.WithField("error", errors.WithStackIf(err)).Error("failed to notify panel of transfer success") l.WithField("error", err).Error("failed to notify panel of transfer success")
return return
} }
l.WithField("error", err.Error()).Error("panel responded with error after transfer success") l.WithField("error", err.Error()).Error("panel responded with error after transfer success")
return return
} }

View File

@@ -2,13 +2,13 @@ package websocket
import ( import (
"context" "context"
"emperror.dev/errors"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/gbrlsnchs/jwt/v3" "github.com/gbrlsnchs/jwt/v3"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"github.com/pterodactyl/wings/environment/docker" "github.com/pterodactyl/wings/environment/docker"
@@ -32,19 +32,18 @@ const (
) )
type Handler struct { type Handler struct {
sync.RWMutex sync.RWMutex `json:"-"`
Connection *websocket.Conn `json:"-"`
Connection *websocket.Conn jwt *tokens.WebsocketPayload
jwt *tokens.WebsocketPayload `json:"-"`
server *server.Server server *server.Server
uuid uuid.UUID uuid uuid.UUID
} }
var ( var (
ErrJwtNotPresent = errors.Sentinel("jwt: no jwt present") ErrJwtNotPresent = errors.New("jwt: no jwt present")
ErrJwtNoConnectPerm = errors.Sentinel("jwt: missing connect permission") ErrJwtNoConnectPerm = errors.New("jwt: missing connect permission")
ErrJwtUuidMismatch = errors.Sentinel("jwt: server uuid mismatch") ErrJwtUuidMismatch = errors.New("jwt: server uuid mismatch")
ErrJwtOnDenylist = errors.Sentinel("jwt: created too far in past (denylist)") ErrJwtOnDenylist = errors.New("jwt: created too far in past (denylist)")
) )
func IsJwtError(err error) bool { func IsJwtError(err error) bool {
@@ -108,7 +107,7 @@ func GetHandler(s *server.Server, w http.ResponseWriter, r *http.Request) (*Hand
u, err := uuid.NewRandom() u, err := uuid.NewRandom()
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
return &Handler{ return &Handler{
@@ -130,7 +129,6 @@ func (h *Handler) SendJson(v *Message) error {
Event: JwtErrorEvent, Event: JwtErrorEvent,
Args: []string{err.Error()}, Args: []string{err.Error()},
}) })
return nil return nil
} }
@@ -219,8 +217,11 @@ func (h *Handler) SendErrorJson(msg Message, err error, shouldLog ...bool) error
Event: ErrorEvent, Event: ErrorEvent,
Args: []string{"an unexpected error was encountered while handling this request"}, Args: []string{"an unexpected error was encountered while handling this request"},
} }
if isJWTError || (j != nil && j.HasPermission(PermissionReceiveErrors)) { if isJWTError || (j != nil && j.HasPermission(PermissionReceiveErrors)) {
wsm.Event = JwtErrorEvent if isJWTError {
wsm.Event = JwtErrorEvent
}
wsm.Args = []string{err.Error()} wsm.Args = []string{err.Error()}
} }
@@ -229,7 +230,7 @@ func (h *Handler) SendErrorJson(msg Message, err error, shouldLog ...bool) error
if !isJWTError && (len(shouldLog) == 0 || (len(shouldLog) == 1 && shouldLog[0] == true)) { if !isJWTError && (len(shouldLog) == 0 || (len(shouldLog) == 1 && shouldLog[0] == true)) {
h.server.Log().WithFields(log.Fields{"event": msg.Event, "error_identifier": u.String(), "error": err}). h.server.Log().WithFields(log.Fields{"event": msg.Event, "error_identifier": u.String(), "error": err}).
Error("failed to handle websocket process; an error was encountered processing an event") Errorf("error processing websocket event \"%s\"", msg.Event)
} }
return h.unsafeSendJson(wsm) return h.unsafeSendJson(wsm)
@@ -267,7 +268,6 @@ func (h *Handler) HandleInbound(m Message) error {
Event: JwtErrorEvent, Event: JwtErrorEvent,
Args: []string{err.Error()}, Args: []string{err.Error()},
}) })
return nil return nil
} }
} }
@@ -311,7 +311,7 @@ func (h *Handler) HandleInbound(m Message) error {
// On every authentication event, send the current server status back // On every authentication event, send the current server status back
// to the client. :) // to the client. :)
state := h.server.GetState() state := h.server.Environment.State()
h.SendJson(&Message{ h.SendJson(&Message{
Event: server.StatusEvent, Event: server.StatusEvent,
Args: []string{state}, Args: []string{state},
@@ -398,7 +398,7 @@ func (h *Handler) HandleInbound(m Message) error {
return nil return nil
} }
if h.server.GetState() == environment.ProcessOfflineState { if h.server.Environment.State() == environment.ProcessOfflineState {
return nil return nil
} }
@@ -406,7 +406,7 @@ func (h *Handler) HandleInbound(m Message) error {
// so that we can better handle this and only set the environment to booted once we're attached. // so that we can better handle this and only set the environment to booted once we're attached.
// //
// Or maybe just an IsBooted function? // Or maybe just an IsBooted function?
if h.server.GetState() == environment.ProcessStartingState { if h.server.Environment.State() == environment.ProcessStartingState {
if e, ok := h.server.Environment.(*docker.Environment); ok { if e, ok := h.server.Environment.(*docker.Environment); ok {
if !e.IsAttached() { if !e.IsAttached() {
return nil return nil

View File

@@ -2,9 +2,9 @@ package server
import ( import (
"crypto/sha256" "crypto/sha256"
"emperror.dev/errors"
"encoding/hex" "encoding/hex"
"github.com/mholt/archiver/v3" "github.com/mholt/archiver/v3"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
"io" "io"
@@ -41,7 +41,7 @@ func (a *Archiver) Exists() bool {
func (a *Archiver) Stat() (*filesystem.Stat, error) { func (a *Archiver) Stat() (*filesystem.Stat, error) {
s, err := os.Stat(a.Path()) s, err := os.Stat(a.Path())
if err != nil { if err != nil {
return nil, errors.WithStack(err) return nil, err
} }
return &filesystem.Stat{ return &filesystem.Stat{
@@ -58,7 +58,7 @@ func (a *Archiver) Archive() error {
var files []string var files []string
fileInfo, err := ioutil.ReadDir(path) fileInfo, err := ioutil.ReadDir(path)
if err != nil { if err != nil {
return errors.WithStack(err) return err
} }
for _, file := range fileInfo { for _, file := range fileInfo {
@@ -94,17 +94,17 @@ func (a *Archiver) DeleteIfExists() error {
return nil return nil
} }
return errors.WithStackIf(err) return err
} }
return errors.WrapIf(os.Remove(a.Path()), "archiver: failed to delete archive from system") return errors.WithMessage(os.Remove(a.Path()), "archiver: failed to delete archive from system")
} }
// Checksum computes a SHA256 checksum of the server's archive. // Checksum computes a SHA256 checksum of the server's archive.
func (a *Archiver) Checksum() (string, error) { func (a *Archiver) Checksum() (string, error) {
file, err := os.Open(a.Path()) file, err := os.Open(a.Path())
if err != nil { if err != nil {
return "", errors.WithStack(err) return "", err
} }
defer file.Close() defer file.Close()
@@ -112,7 +112,7 @@ func (a *Archiver) Checksum() (string, error) {
buf := make([]byte, 1024*4) buf := make([]byte, 1024*4)
if _, err := io.CopyBuffer(hash, file, buf); err != nil { if _, err := io.CopyBuffer(hash, file, buf); err != nil {
return "", errors.WithStack(err) return "", err
} }
return hex.EncodeToString(hash.Sum(nil)), nil return hex.EncodeToString(hash.Sum(nil)), nil

View File

@@ -2,8 +2,8 @@ package server
import ( import (
"bufio" "bufio"
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/server/backup" "github.com/pterodactyl/wings/server/backup"
"os" "os"
@@ -13,15 +13,14 @@ import (
// Notifies the panel of a backup's state and returns an error if one is encountered // Notifies the panel of a backup's state and returns an error if one is encountered
// while performing this action. // while performing this action.
func (s *Server) notifyPanelOfBackup(uuid string, ad *backup.ArchiveDetails, successful bool) error { func (s *Server) notifyPanelOfBackup(uuid string, ad *backup.ArchiveDetails, successful bool) error {
err := api.New().SendBackupStatus(uuid, ad.ToRequest(successful)) if err := api.New().SendBackupStatus(uuid, ad.ToRequest(successful)); err != nil {
if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
s.Log().WithFields(log.Fields{ s.Log().WithFields(log.Fields{
"backup": uuid, "backup": uuid,
"error": err, "error": err,
}).Error("failed to notify panel of backup status due to wings error") }).Error("failed to notify panel of backup status due to wings error")
return errors.WithStackIf(err) return err
} }
return errors.New(err.Error()) return errors.New(err.Error())
@@ -37,7 +36,7 @@ func (s *Server) getServerwideIgnoredFiles() ([]string, error) {
f, err := os.Open(path.Join(s.Filesystem().Path(), ".pteroignore")) f, err := os.Open(path.Join(s.Filesystem().Path(), ".pteroignore"))
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return nil, errors.WithStack(err) return nil, err
} }
} else { } else {
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
@@ -49,7 +48,7 @@ func (s *Server) getServerwideIgnoredFiles() ([]string, error) {
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return nil, errors.WithStack(err) return nil, err
} }
} }
@@ -79,7 +78,7 @@ func (s *Server) Backup(b backup.BackupInterface) error {
// Get the included files based on the root path and the ignored files provided. // Get the included files based on the root path and the ignored files provided.
inc, err := s.GetIncludedBackupFiles(b.Ignored()) inc, err := s.GetIncludedBackupFiles(b.Ignored())
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
ad, err := b.Generate(inc, s.Filesystem().Path()) ad, err := b.Generate(inc, s.Filesystem().Path())
@@ -89,6 +88,11 @@ func (s *Server) Backup(b backup.BackupInterface) error {
"backup": b.Identifier(), "backup": b.Identifier(),
"error": notifyError, "error": notifyError,
}).Warn("failed to notify panel of failed backup state") }).Warn("failed to notify panel of failed backup state")
} else {
s.Log().WithFields(log.Fields{
"backup": b.Identifier(),
"error": err,
}).Info("notified panel of failed backup state")
} }
s.Events().PublishJson(BackupCompletedEvent+":"+b.Identifier(), map[string]interface{}{ s.Events().PublishJson(BackupCompletedEvent+":"+b.Identifier(), map[string]interface{}{
@@ -99,15 +103,17 @@ func (s *Server) Backup(b backup.BackupInterface) error {
"file_size": 0, "file_size": 0,
}) })
return errors.WrapIf(err, "backup: error while generating server backup") return errors.WithMessage(err, "backup: error while generating server backup")
} }
// Try to notify the panel about the status of this backup. If for some reason this request // Try to notify the panel about the status of this backup. If for some reason this request
// fails, delete the archive from the daemon and return that error up the chain to the caller. // fails, delete the archive from the daemon and return that error up the chain to the caller.
if notifyError := s.notifyPanelOfBackup(b.Identifier(), ad, true); notifyError != nil { if notifyError := s.notifyPanelOfBackup(b.Identifier(), ad, true); notifyError != nil {
b.Remove() b.Remove()
s.Log().WithField("error", notifyError).Info("failed to notify panel of successful backup state")
return errors.WithStackIf(err) return err
} else {
s.Log().WithField("backup", b.Identifier()).Info("notified panel of successful backup state")
} }
// Emit an event over the socket so we can update the backup in realtime on // Emit an event over the socket so we can update the backup in realtime on

View File

@@ -3,9 +3,9 @@ package backup
import ( import (
"archive/tar" "archive/tar"
"context" "context"
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
gzip "github.com/klauspost/pgzip" gzip "github.com/klauspost/pgzip"
"github.com/pkg/errors"
"github.com/remeh/sizedwaitgroup" "github.com/remeh/sizedwaitgroup"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"io" "io"
@@ -26,7 +26,7 @@ type Archive struct {
func (a *Archive) Create(dst string, ctx context.Context) error { func (a *Archive) Create(dst string, ctx context.Context) error {
f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer f.Close() defer f.Close()
@@ -58,7 +58,7 @@ func (a *Archive) Create(dst string, ctx context.Context) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return errors.WithStackIf(ctx.Err()) return ctx.Err()
default: default:
return a.addToArchive(p, tw) return a.addToArchive(p, tw)
} }
@@ -75,7 +75,7 @@ func (a *Archive) Create(dst string, ctx context.Context) error {
log.WithField("location", dst).Warn("failed to delete corrupted backup archive") log.WithField("location", dst).Warn("failed to delete corrupted backup archive")
} }
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -91,7 +91,7 @@ func (a *Archive) addToArchive(p string, w *tar.Writer) error {
return nil return nil
} }
return errors.WithStackIf(err) return err
} }
defer f.Close() defer f.Close()
@@ -102,17 +102,15 @@ func (a *Archive) addToArchive(p string, w *tar.Writer) error {
return nil return nil
} }
return errors.WithStackIf(err) return err
} }
header := &tar.Header{ name := strings.TrimPrefix(p, a.TrimPrefix)
// Trim the long server path from the name of the file so that the resulting header, err := tar.FileInfoHeader(s, name)
// archive is exactly how the user would see it in the panel file manager. if err != nil {
Name: strings.TrimPrefix(p, a.TrimPrefix), return errors.WithMessage(err, "failed to get tar#FileInfoHeader for "+name)
Size: s.Size(),
Mode: int64(s.Mode()),
ModTime: s.ModTime(),
} }
header.Name = name
// These actions must occur sequentially, even if this function is called multiple // These actions must occur sequentially, even if this function is called multiple
// in parallel. You'll get some nasty panic's otherwise. // in parallel. You'll get some nasty panic's otherwise.
@@ -120,12 +118,12 @@ func (a *Archive) addToArchive(p string, w *tar.Writer) error {
defer a.Unlock() defer a.Unlock()
if err := w.WriteHeader(header); err != nil { if err := w.WriteHeader(header); err != nil {
return errors.WithStackIf(err) return err
} }
buf := make([]byte, 4*1024) buf := make([]byte, 4*1024)
if _, err := io.CopyBuffer(w, f, buf); err != nil { if _, err := io.CopyBuffer(w, io.LimitReader(f, header.Size), buf); err != nil {
return errors.WithStackIf(err) return errors.WithMessage(err, "failed to copy "+header.Name+" to archive")
} }
return nil return nil

View File

@@ -2,7 +2,6 @@ package backup
import ( import (
"crypto/sha1" "crypto/sha1"
"emperror.dev/errors"
"encoding/hex" "encoding/hex"
"github.com/apex/log" "github.com/apex/log"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
@@ -87,7 +86,7 @@ func (b *Backup) Path() string {
func (b *Backup) Size() (int64, error) { func (b *Backup) Size() (int64, error) {
st, err := os.Stat(b.Path()) st, err := os.Stat(b.Path())
if err != nil { if err != nil {
return 0, errors.WithStackIf(err) return 0, err
} }
return st.Size(), nil return st.Size(), nil
@@ -99,7 +98,7 @@ func (b *Backup) Checksum() ([]byte, error) {
f, err := os.Open(b.Path()) f, err := os.Open(b.Path())
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
defer f.Close() defer f.Close()
@@ -128,6 +127,7 @@ func (b *Backup) Details() *ArchiveDetails {
"backup": b.Identifier(), "backup": b.Identifier(),
"error": err, "error": err,
}).Error("failed to calculate checksum for backup") }).Error("failed to calculate checksum for backup")
return
} }
checksum = hex.EncodeToString(resp) checksum = hex.EncodeToString(resp)

View File

@@ -2,7 +2,7 @@ package backup
import ( import (
"context" "context"
"emperror.dev/errors" "errors"
"os" "os"
) )
@@ -24,7 +24,7 @@ func LocateLocal(uuid string) (*LocalBackup, os.FileInfo, error) {
st, err := os.Stat(b.Path()) st, err := os.Stat(b.Path())
if err != nil { if err != nil {
return nil, nil, errors.WithStackIf(err) return nil, nil, err
} }
if st.IsDir() { if st.IsDir() {
@@ -48,7 +48,7 @@ func (b *LocalBackup) Generate(included *IncludedFiles, prefix string) (*Archive
} }
if err := a.Create(b.Path(), context.Background()); err != nil { if err := a.Create(b.Path(), context.Background()); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
return b.Details(), nil return b.Details(), nil

View File

@@ -1,7 +1,7 @@
package backup package backup
import ( import (
"emperror.dev/errors" "errors"
"fmt" "fmt"
) )

View File

@@ -1,9 +1,7 @@
package backup package backup
import ( import (
"bytes"
"context" "context"
"emperror.dev/errors"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
@@ -11,7 +9,6 @@ import (
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"time"
) )
type S3Backup struct { type S3Backup struct {
@@ -31,20 +28,20 @@ func (s *S3Backup) Generate(included *IncludedFiles, prefix string) (*ArchiveDet
} }
if err := a.Create(s.Path(), context.Background()); err != nil { if err := a.Create(s.Path(), context.Background()); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
rc, err := os.Open(s.Path()) rc, err := os.Open(s.Path())
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
defer rc.Close() defer rc.Close()
if err := s.generateRemoteRequest(rc); err != nil { if err := s.generateRemoteRequest(rc); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
return s.Details(), err return s.Details(), nil
} }
// Removes a backup from the system. // Removes a backup from the system.
@@ -76,10 +73,12 @@ func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
return err return err
} }
log.WithFields(log.Fields{ l := log.WithFields(log.Fields{
"backup_id": s.Uuid, "backup_id": s.Uuid,
"adapter": "s3", "adapter": "s3",
}).Info("attempting to upload backup..") })
l.Info("attempting to upload backup..")
handlePart := func(part string, size int64) (string, error) { handlePart := func(part string, size int64) (string, error) {
r, err := http.NewRequest(http.MethodPut, part, nil) r, err := http.NewRequest(http.MethodPut, part, nil)
@@ -92,7 +91,7 @@ func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
r.Header.Add("Content-Type", "application/x-gzip") r.Header.Add("Content-Type", "application/x-gzip")
// Limit the reader to the size of the part. // Limit the reader to the size of the part.
r.Body = Reader{io.LimitReader(rc, size)} r.Body = Reader{Reader: io.LimitReader(rc, size)}
// This http request can block forever due to it not having a timeout, // This http request can block forever due to it not having a timeout,
// but we are uploading up to 5GB of data, so there is not really // but we are uploading up to 5GB of data, so there is not really
@@ -112,10 +111,6 @@ func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
return res.Header.Get("ETag"), nil return res.Header.Get("ETag"), nil
} }
// Start assembling the body that will be sent as apart of the CompleteMultipartUpload request.
var completeUploadBody bytes.Buffer
completeUploadBody.WriteString("<CompleteMultipartUpload>\n")
partCount := len(urls.Parts) partCount := len(urls.Parts)
for i, part := range urls.Parts { for i, part := range urls.Parts {
// Get the size for the current part. // Get the size for the current part.
@@ -129,67 +124,13 @@ func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
} }
// Attempt to upload the part. // Attempt to upload the part.
etag, err := handlePart(part, partSize) if _, err := handlePart(part, partSize); err != nil {
if err != nil { l.WithField("part_id", part).WithError(err).Warn("failed to upload part")
log.WithError(err).Warn("failed to upload part")
// Send an AbortMultipartUpload request.
if err := s.finishUpload(urls.AbortMultipartUpload, nil); err != nil {
log.WithError(err).Warn("failed to abort multipart backup upload")
}
return err return err
} }
// Add the part to the CompleteMultipartUpload body.
completeUploadBody.WriteString("\t<Part>\n")
completeUploadBody.WriteString("\t\t<ETag>\"" + etag + "\"</ETag>\n")
completeUploadBody.WriteString("\t\t<PartNumber>" + strconv.Itoa(i+1) + "</PartNumber>\n")
completeUploadBody.WriteString("\t</Part>\n")
}
completeUploadBody.WriteString("</CompleteMultipartUpload>")
// Send a CompleteMultipartUpload request.
if err := s.finishUpload(urls.CompleteMultipartUpload, &completeUploadBody); err != nil {
return err
} }
log.WithFields(log.Fields{ l.Info("backup has been successfully uploaded")
"backup_id": s.Uuid,
"adapter": "s3",
}).Info("backup has been successfully uploaded")
return nil
}
// finishUpload sends a requests to the specified url to either complete or abort the upload.
func (s *S3Backup) finishUpload(url string, body io.Reader) error {
r, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return err
}
// Create a new http client with a 10 second timeout.
c := &http.Client{
Timeout: 10 * time.Second,
}
res, err := c.Do(r)
if err != nil {
return err
}
defer res.Body.Close()
// Handle non-200 status codes.
if res.StatusCode != http.StatusOK {
// If no body was sent, we were aborting the upload.
if body == nil {
return fmt.Errorf("failed to abort S3 multipart upload, %d:%s", res.StatusCode, res.Status)
}
// If a body was sent we were completing the upload.
// TODO: Attempt to send abort request?
return fmt.Errorf("failed to complete S3 multipart upload, %d:%s", res.StatusCode, res.Status)
}
return nil return nil
} }

View File

@@ -2,9 +2,9 @@ package server
import ( import (
"context" "context"
"emperror.dev/errors"
"fmt" "fmt"
"github.com/mitchellh/colorstring" "github.com/mitchellh/colorstring"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
"sync" "sync"
@@ -12,7 +12,7 @@ import (
"time" "time"
) )
var ErrTooMuchConsoleData = errors.Sentinel("console is outputting too much data") var ErrTooMuchConsoleData = errors.New("console is outputting too much data")
type ConsoleThrottler struct { type ConsoleThrottler struct {
mu sync.Mutex mu sync.Mutex

View File

@@ -1,10 +1,10 @@
package server package server
import ( import (
"emperror.dev/errors"
"fmt" "fmt"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"strconv"
"sync" "sync"
"time" "time"
) )
@@ -45,11 +45,10 @@ func (s *Server) handleServerCrash() error {
// No point in doing anything here if the server isn't currently offline, there // No point in doing anything here if the server isn't currently offline, there
// is no reason to do a crash detection event. If the server crash detection is // is no reason to do a crash detection event. If the server crash detection is
// disabled we want to skip anything after this as well. // disabled we want to skip anything after this as well.
if s.GetState() != environment.ProcessOfflineState || !s.Config().CrashDetectionEnabled { if s.Environment.State() != environment.ProcessOfflineState || !s.Config().CrashDetectionEnabled {
if !s.Config().CrashDetectionEnabled { if !s.Config().CrashDetectionEnabled {
s.Log().Debug("server triggered crash detection but handler is disabled for server process") s.Log().Debug("server triggered crash detection but handler is disabled for server process")
s.PublishConsoleOutputFromDaemon("Aborting automatic restart, crash detection is disabled for this instance.")
s.PublishConsoleOutputFromDaemon("Server detected as crashed; crash detection is disabled for this instance.")
} }
return nil return nil
@@ -57,14 +56,13 @@ func (s *Server) handleServerCrash() error {
exitCode, oomKilled, err := s.Environment.ExitState() exitCode, oomKilled, err := s.Environment.ExitState()
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
// If the system is not configured to detect a clean exit code as a crash, and the // If the system is not configured to detect a clean exit code as a crash, and the
// crash is not the result of the program running out of memory, do nothing. // crash is not the result of the program running out of memory, do nothing.
if exitCode == 0 && !oomKilled && !config.Get().System.DetectCleanExitAsCrash { if exitCode == 0 && !oomKilled && !config.Get().System.CrashDetection.DetectCleanExitAsCrash {
s.Log().Debug("server exited with successful exit code; system is configured to not detect this as a crash") s.Log().Debug("server exited with successful exit code; system is configured to not detect this as a crash")
return nil return nil
} }
@@ -73,11 +71,14 @@ func (s *Server) handleServerCrash() error {
s.PublishConsoleOutputFromDaemon(fmt.Sprintf("Out of memory: %t", oomKilled)) s.PublishConsoleOutputFromDaemon(fmt.Sprintf("Out of memory: %t", oomKilled))
c := s.crasher.LastCrashTime() c := s.crasher.LastCrashTime()
// If the last crash time was within the last 60 seconds we do not want to perform timeout := config.Get().System.CrashDetection.Timeout
// an automatic reboot of the process. Return an error that can be handled.
if !c.IsZero() && c.Add(time.Second*60).After(time.Now()) {
s.PublishConsoleOutputFromDaemon("Aborting automatic reboot: last crash occurred less than 60 seconds ago.")
// If the last crash time was within the last `timeout` seconds we do not want to perform
// an automatic reboot of the process. Return an error that can be handled.
//
// If timeout is set to 0, always reboot the server (this is probably a terrible idea, but some people want it)
if timeout != 0 && !c.IsZero() && c.Add(time.Second*time.Duration(config.Get().System.CrashDetection.Timeout)).After(time.Now()) {
s.PublishConsoleOutputFromDaemon("Aborting automatic restart, last crash occurred less than " + strconv.Itoa(timeout) + " seconds ago.")
return &crashTooFrequent{} return &crashTooFrequent{}
} }

View File

@@ -1,9 +1,11 @@
package server package server
import "emperror.dev/errors" import (
"github.com/pkg/errors"
)
var ErrIsRunning = errors.Sentinel("server is running") var ErrIsRunning = errors.New("server is running")
var ErrSuspended = errors.Sentinel("server is currently in a suspended state") var ErrSuspended = errors.New("server is currently in a suspended state")
type crashTooFrequent struct { type crashTooFrequent struct {
} }

View File

@@ -1,7 +1,6 @@
package server package server
import ( import (
"emperror.dev/errors"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
"os" "os"
) )
@@ -13,12 +12,12 @@ func (s *Server) Filesystem() *filesystem.Filesystem {
// Ensures that the data directory for the server instance exists. // Ensures that the data directory for the server instance exists.
func (s *Server) EnsureDataDirectoryExists() error { func (s *Server) EnsureDataDirectoryExists() error {
if _, err := os.Stat(s.fs.Path()); err != nil && !os.IsNotExist(err) { if _, err := os.Stat(s.fs.Path()); err != nil && !os.IsNotExist(err) {
return errors.WithStackIf(err) return err
} else if err != nil { } else if err != nil {
// Create the server data directory because it does not currently exist // Create the server data directory because it does not currently exist
// on the system. // on the system.
if err := os.MkdirAll(s.fs.Path(), 0700); err != nil { if err := os.MkdirAll(s.fs.Path(), 0700); err != nil {
return errors.WithStackIf(err) return err
} }
if err := s.fs.Chown("/"); err != nil { if err := s.fs.Chown("/"); err != nil {

View File

@@ -2,7 +2,6 @@ package filesystem
import ( import (
"context" "context"
"emperror.dev/errors"
"fmt" "fmt"
"github.com/karrick/godirwalk" "github.com/karrick/godirwalk"
"github.com/pterodactyl/wings/server/backup" "github.com/pterodactyl/wings/server/backup"
@@ -27,7 +26,7 @@ func (fs *Filesystem) GetIncludedFiles(dir string, ignored []string) (*backup.In
i, err := ignore.CompileIgnoreLines(ignored...) i, err := ignore.CompileIgnoreLines(ignored...)
if err != nil { if err != nil {
return nil, errors.WithStack(err) return nil, err
} }
// Walk through all of the files and directories on a server. This callback only returns // Walk through all of the files and directories on a server. This callback only returns
@@ -65,7 +64,7 @@ func (fs *Filesystem) GetIncludedFiles(dir string, ignored []string) (*backup.In
}, },
}) })
return inc, errors.WithStackIf(err) return inc, err
} }
// Compresses all of the files matching the given paths in the specified directory. This function // Compresses all of the files matching the given paths in the specified directory. This function
@@ -141,7 +140,7 @@ func (fs *Filesystem) CompressFiles(dir string, paths []string) (os.FileInfo, er
d := path.Join(cleanedRootDir, fmt.Sprintf("archive-%s.tar.gz", strings.ReplaceAll(time.Now().Format(time.RFC3339), ":", ""))) d := path.Join(cleanedRootDir, fmt.Sprintf("archive-%s.tar.gz", strings.ReplaceAll(time.Now().Format(time.RFC3339), ":", "")))
if err := a.Create(d, context.Background()); err != nil { if err := a.Create(d, context.Background()); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
f, err := os.Stat(d) f, err := os.Stat(d)

View File

@@ -4,9 +4,9 @@ import (
"archive/tar" "archive/tar"
"archive/zip" "archive/zip"
"compress/gzip" "compress/gzip"
"emperror.dev/errors"
"fmt" "fmt"
"github.com/mholt/archiver/v3" "github.com/mholt/archiver/v3"
"github.com/pkg/errors"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@@ -47,10 +47,10 @@ func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (b
return false, ErrUnknownArchiveFormat return false, ErrUnknownArchiveFormat
} }
return false, errors.WithStackIf(err) return false, err
} }
return true, errors.WithStackIf(err) return true, err
} }
// Decompress a file in a given directory by using the archiver tool to infer the file // Decompress a file in a given directory by using the archiver tool to infer the file
@@ -60,12 +60,12 @@ func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (b
func (fs *Filesystem) DecompressFile(dir string, file string) error { func (fs *Filesystem) DecompressFile(dir string, file string) error {
source, err := fs.SafePath(filepath.Join(dir, file)) source, err := fs.SafePath(filepath.Join(dir, file))
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
// Make sure the file exists basically. // Make sure the file exists basically.
if _, err := os.Stat(source); err != nil { if _, err := os.Stat(source); err != nil {
return errors.WithStackIf(err) return err
} }
// Walk over all of the files spinning up an additional go-routine for each file we've encountered // Walk over all of the files spinning up an additional go-routine for each file we've encountered
@@ -93,17 +93,17 @@ func (fs *Filesystem) DecompressFile(dir string, file string) error {
p, err := fs.SafePath(filepath.Join(dir, name)) p, err := fs.SafePath(filepath.Join(dir, name))
if err != nil { if err != nil {
return errors.WrapIf(err, "failed to generate a safe path to server file") return errors.WithMessage(err, "failed to generate a safe path to server file")
} }
return errors.WrapIf(fs.Writefile(p, f), "could not extract file from archive") return errors.WithMessage(fs.Writefile(p, f), "could not extract file from archive")
}) })
if err != nil { if err != nil {
if strings.HasPrefix(err.Error(), "format ") { if strings.HasPrefix(err.Error(), "format ") {
return errors.WithStackIf(ErrUnknownArchiveFormat) return ErrUnknownArchiveFormat
} }
return errors.WithStackIf(err) return err
} }
return nil return nil

View File

@@ -1,7 +1,6 @@
package filesystem package filesystem
import ( import (
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/karrick/godirwalk" "github.com/karrick/godirwalk"
"sync" "sync"
@@ -97,6 +96,11 @@ func (fs *Filesystem) CachedUsage() int64 {
// This is primarily to avoid a bunch of I/O operations from piling up on the server, especially on servers // This is primarily to avoid a bunch of I/O operations from piling up on the server, especially on servers
// with a large amount of files. // with a large amount of files.
func (fs *Filesystem) DiskUsage(allowStaleValue bool) (int64, error) { func (fs *Filesystem) DiskUsage(allowStaleValue bool) (int64, error) {
// A disk check interval of 0 means this functionality is completely disabled.
if fs.diskCheckInterval == 0 {
return 0, nil
}
if !fs.lastLookupTime.Get().After(time.Now().Add(time.Second * fs.diskCheckInterval * -1)) { if !fs.lastLookupTime.Get().After(time.Now().Add(time.Second * fs.diskCheckInterval * -1)) {
// If we are now allowing a stale response go ahead and perform the lookup and return the fresh // If we are now allowing a stale response go ahead and perform the lookup and return the fresh
// value. This is a blocking operation to the calling process. // value. This is a blocking operation to the calling process.
@@ -153,7 +157,7 @@ func (fs *Filesystem) updateCachedDiskUsage() (int64, error) {
func (fs *Filesystem) DirectorySize(dir string) (int64, error) { func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
d, err := fs.SafePath(dir) d, err := fs.SafePath(dir)
if err != nil { if err != nil {
return 0, errors.WithStackIf(err) return 0, err
} }
var size int64 var size int64
@@ -184,7 +188,7 @@ func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
}, },
}) })
return size, errors.WithStackIf(err) return size, err
} }
// Helper function to determine if a server has space available for a file of a given size. // Helper function to determine if a server has space available for a file of a given size.
@@ -197,7 +201,7 @@ func (fs *Filesystem) hasSpaceFor(size int64) error {
s, err := fs.DiskUsage(true) s, err := fs.DiskUsage(true)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
if (s + size) > fs.MaxDisk() { if (s + size) > fs.MaxDisk() {

View File

@@ -1,16 +1,16 @@
package filesystem package filesystem
import ( import (
"emperror.dev/errors"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"os" "os"
"path/filepath" "path/filepath"
) )
var ErrIsDirectory = errors.Sentinel("filesystem: is a directory") var ErrIsDirectory = errors.New("filesystem: is a directory")
var ErrNotEnoughDiskSpace = errors.Sentinel("filesystem: not enough disk space") var ErrNotEnoughDiskSpace = errors.New("filesystem: not enough disk space")
var ErrUnknownArchiveFormat = errors.Sentinel("filesystem: unknown archive format") var ErrUnknownArchiveFormat = errors.New("filesystem: unknown archive format")
type BadPathResolutionError struct { type BadPathResolutionError struct {
path string path string
@@ -23,6 +23,7 @@ func (b *BadPathResolutionError) Error() string {
if r == "" { if r == "" {
r = "<empty>" r = "<empty>"
} }
return fmt.Sprintf("filesystem: server path [%s] resolves to a location outside the server root: %s", b.path, r) return fmt.Sprintf("filesystem: server path [%s] resolves to a location outside the server root: %s", b.path, r)
} }
@@ -57,7 +58,7 @@ func (fs *Filesystem) error(err error) *log.Entry {
// for the remainder of the directory. This is assuming an os.FileInfo struct was even returned. // for the remainder of the directory. This is assuming an os.FileInfo struct was even returned.
func (fs *Filesystem) handleWalkerError(err error, f os.FileInfo) error { func (fs *Filesystem) handleWalkerError(err error, f os.FileInfo) error {
if !IsBadPathResolutionError(err) { if !IsBadPathResolutionError(err) {
return errors.WithStackIf(err) return err
} }
if f != nil && f.IsDir() { if f != nil && f.IsDir() {

View File

@@ -2,9 +2,9 @@ package filesystem
import ( import (
"bufio" "bufio"
"emperror.dev/errors"
"github.com/gabriel-vasile/mimetype" "github.com/gabriel-vasile/mimetype"
"github.com/karrick/godirwalk" "github.com/karrick/godirwalk"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
"io" "io"
@@ -60,27 +60,27 @@ func (fs *Filesystem) Readfile(p string, w io.Writer) error {
} }
if st, err := os.Stat(cleaned); err != nil { if st, err := os.Stat(cleaned); err != nil {
return errors.WithStack(err) return err
} else if st.IsDir() { } else if st.IsDir() {
return errors.WithStack(ErrIsDirectory) return ErrIsDirectory
} }
f, err := os.Open(cleaned) f, err := os.Open(cleaned)
if err != nil { if err != nil {
return errors.WithStack(err) return err
} }
defer f.Close() defer f.Close()
_, err = bufio.NewReader(f).WriteTo(w) _, err = bufio.NewReader(f).WriteTo(w)
return errors.WithStack(err) return err
} }
// Writes a file to the system. If the file does not already exist one will be created. // Writes a file to the system. If the file does not already exist one will be created.
func (fs *Filesystem) Writefile(p string, r io.Reader) error { func (fs *Filesystem) Writefile(p string, r io.Reader) error {
cleaned, err := fs.SafePath(p) cleaned, err := fs.SafePath(p)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
var currentSize int64 var currentSize int64
@@ -88,19 +88,19 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
// to it and an empty file. We'll then write to it later on after this completes. // to it and an empty file. We'll then write to it later on after this completes.
if stat, err := os.Stat(cleaned); err != nil { if stat, err := os.Stat(cleaned); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return errors.WithStackIf(err) return err
} }
if err := os.MkdirAll(filepath.Dir(cleaned), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(cleaned), 0755); err != nil {
return errors.WithStackIf(err) return err
} }
if err := fs.Chown(filepath.Dir(cleaned)); err != nil { if err := fs.Chown(filepath.Dir(cleaned)); err != nil {
return errors.WithStackIf(err) return err
} }
} else { } else {
if stat.IsDir() { if stat.IsDir() {
return errors.WithStack(ErrIsDirectory) return ErrIsDirectory
} }
currentSize = stat.Size() currentSize = stat.Size()
@@ -119,7 +119,7 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
// truncate the existing file. // truncate the existing file.
file, err := o.open(cleaned, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) file, err := o.open(cleaned, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer file.Close() defer file.Close()
@@ -138,7 +138,7 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
func (fs *Filesystem) CreateDirectory(name string, p string) error { func (fs *Filesystem) CreateDirectory(name string, p string) error {
cleaned, err := fs.SafePath(path.Join(p, name)) cleaned, err := fs.SafePath(path.Join(p, name))
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
return os.MkdirAll(cleaned, 0755) return os.MkdirAll(cleaned, 0755)
@@ -148,12 +148,12 @@ func (fs *Filesystem) CreateDirectory(name string, p string) error {
func (fs *Filesystem) Rename(from string, to string) error { func (fs *Filesystem) Rename(from string, to string) error {
cleanedFrom, err := fs.SafePath(from) cleanedFrom, err := fs.SafePath(from)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
cleanedTo, err := fs.SafePath(to) cleanedTo, err := fs.SafePath(to)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
// If the target file or directory already exists the rename function will fail, so just // If the target file or directory already exists the rename function will fail, so just
@@ -171,7 +171,7 @@ func (fs *Filesystem) Rename(from string, to string) error {
// we're not at the root directory level. // we're not at the root directory level.
if d != fs.Path() { if d != fs.Path() {
if mkerr := os.MkdirAll(d, 0755); mkerr != nil { if mkerr := os.MkdirAll(d, 0755); mkerr != nil {
return errors.WrapIf(mkerr, "failed to create directory structure for file rename") return errors.WithMessage(mkerr, "failed to create directory structure for file rename")
} }
} }
@@ -185,7 +185,7 @@ func (fs *Filesystem) Rename(from string, to string) error {
func (fs *Filesystem) Chown(path string) error { func (fs *Filesystem) Chown(path string) error {
cleaned, err := fs.SafePath(path) cleaned, err := fs.SafePath(path)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
if fs.isTest { if fs.isTest {
@@ -197,7 +197,7 @@ func (fs *Filesystem) Chown(path string) error {
// Start by just chowning the initial path that we received. // Start by just chowning the initial path that we received.
if err := os.Chown(cleaned, uid, gid); err != nil { if err := os.Chown(cleaned, uid, gid); err != nil {
return errors.WithStackIf(err) return err
} }
// If this is not a directory we can now return from the function, there is nothing // If this is not a directory we can now return from the function, there is nothing
@@ -227,6 +227,23 @@ func (fs *Filesystem) Chown(path string) error {
}) })
} }
func (fs *Filesystem) Chmod(path string, mode os.FileMode) error {
cleaned, err := fs.SafePath(path)
if err != nil {
return err
}
if fs.isTest {
return nil
}
if err := os.Chmod(cleaned, mode); err != nil {
return err
}
return nil
}
// Begin looping up to 50 times to try and create a unique copy file name. This will take // Begin looping up to 50 times to try and create a unique copy file name. This will take
// an input of "file.txt" and generate "file copy.txt". If that name is already taken, it will // an input of "file.txt" and generate "file copy.txt". If that name is already taken, it will
// then try to write "file copy 2.txt" and so on, until reaching 50 loops. At that point we // then try to write "file copy 2.txt" and so on, until reaching 50 loops. At that point we
@@ -249,7 +266,7 @@ func (fs *Filesystem) findCopySuffix(dir string, name string, extension string)
// does exist, we'll just continue to the next loop and try again. // does exist, we'll just continue to the next loop and try again.
if _, err := fs.Stat(path.Join(dir, n)); err != nil { if _, err := fs.Stat(path.Join(dir, n)); err != nil {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
return "", errors.WithStackIf(err) return "", err
} }
break break
@@ -268,12 +285,12 @@ func (fs *Filesystem) findCopySuffix(dir string, name string, extension string)
func (fs *Filesystem) Copy(p string) error { func (fs *Filesystem) Copy(p string) error {
cleaned, err := fs.SafePath(p) cleaned, err := fs.SafePath(p)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
s, err := os.Stat(cleaned) s, err := os.Stat(cleaned)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} else if s.IsDir() || !s.Mode().IsRegular() { } else if s.IsDir() || !s.Mode().IsRegular() {
// If this is a directory or not a regular file, just throw a not-exist error // If this is a directory or not a regular file, just throw a not-exist error
// since anything calling this function should understand what that means. // since anything calling this function should understand what that means.
@@ -300,7 +317,7 @@ func (fs *Filesystem) Copy(p string) error {
source, err := os.Open(cleaned) source, err := os.Open(cleaned)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer source.Close() defer source.Close()

View File

@@ -2,7 +2,6 @@ package filesystem
import ( import (
"context" "context"
"emperror.dev/errors"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"os" "os"
"path/filepath" "path/filepath"
@@ -26,7 +25,7 @@ func (fs *Filesystem) SafePath(p string) (string, error) {
// is truly pointing to. // is truly pointing to.
ep, err := filepath.EvalSymlinks(r) ep, err := filepath.EvalSymlinks(r)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return "", errors.WithStackIf(err) return "", err
} else if os.IsNotExist(err) { } else if os.IsNotExist(err) {
// The requested directory doesn't exist, so at this point we need to iterate up the // The requested directory doesn't exist, so at this point we need to iterate up the
// path chain until we hit a directory that _does_ exist and can be validated. // path chain until we hit a directory that _does_ exist and can be validated.
@@ -138,5 +137,5 @@ func (fs *Filesystem) ParallelSafePath(paths []string) ([]string, error) {
} }
// Block until all of the routines finish and have returned a value. // Block until all of the routines finish and have returned a value.
return cleaned, errors.WithStackIf(g.Wait()) return cleaned, g.Wait()
} }

View File

@@ -2,8 +2,8 @@ package filesystem
import ( import (
"bytes" "bytes"
"emperror.dev/errors"
. "github.com/franela/goblin" . "github.com/franela/goblin"
"github.com/pkg/errors"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"

View File

@@ -1,10 +1,10 @@
package filesystem package filesystem
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/gabriel-vasile/mimetype" "github.com/gabriel-vasile/mimetype"
"os" "os"
"strconv"
"time" "time"
) )
@@ -19,16 +19,19 @@ func (s *Stat) MarshalJSON() ([]byte, error) {
Created string `json:"created"` Created string `json:"created"`
Modified string `json:"modified"` Modified string `json:"modified"`
Mode string `json:"mode"` Mode string `json:"mode"`
ModeBits string `json:"mode_bits"`
Size int64 `json:"size"` Size int64 `json:"size"`
Directory bool `json:"directory"` Directory bool `json:"directory"`
File bool `json:"file"` File bool `json:"file"`
Symlink bool `json:"symlink"` Symlink bool `json:"symlink"`
Mime string `json:"mime"` Mime string `json:"mime"`
}{ }{
Name: s.Info.Name(), Name: s.Info.Name(),
Created: s.CTime().Format(time.RFC3339), Created: s.CTime().Format(time.RFC3339),
Modified: s.Info.ModTime().Format(time.RFC3339), Modified: s.Info.ModTime().Format(time.RFC3339),
Mode: s.Info.Mode().String(), Mode: s.Info.Mode().String(),
// Using `&os.ModePerm` on the file's mode will cause the mode to only have the permission values, and nothing else.
ModeBits: strconv.FormatUint(uint64(s.Info.Mode()&os.ModePerm), 8),
Size: s.Info.Size(), Size: s.Info.Size(),
Directory: s.Info.IsDir(), Directory: s.Info.IsDir(),
File: !s.Info.IsDir(), File: !s.Info.IsDir(),
@@ -51,14 +54,14 @@ func (fs *Filesystem) Stat(p string) (*Stat, error) {
func (fs *Filesystem) unsafeStat(p string) (*Stat, error) { func (fs *Filesystem) unsafeStat(p string) (*Stat, error) {
s, err := os.Stat(p) s, err := os.Stat(p)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
var m *mimetype.MIME var m *mimetype.MIME
if !s.IsDir() { if !s.IsDir() {
m, err = mimetype.DetectFile(p) m, err = mimetype.DetectFile(p)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
} }

View File

@@ -4,12 +4,12 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
@@ -19,6 +19,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"time" "time"
) )
@@ -75,7 +76,7 @@ func (s *Server) Install(sync bool) error {
// Reinstalls a server's software by utilizing the install script for the server egg. This // Reinstalls a server's software by utilizing the install script for the server egg. This
// does not touch any existing files for the server, other than what the script modifies. // does not touch any existing files for the server, other than what the script modifies.
func (s *Server) Reinstall() error { func (s *Server) Reinstall() error {
if s.GetState() != environment.ProcessOfflineState { if s.Environment.State() != environment.ProcessOfflineState {
s.Log().Debug("waiting for server instance to enter a stopped state") s.Log().Debug("waiting for server instance to enter a stopped state")
if err := s.Environment.WaitForStop(10, true); err != nil { if err := s.Environment.WaitForStop(10, true); err != nil {
return err return err
@@ -90,7 +91,7 @@ func (s *Server) internalInstall() error {
script, err := api.New().GetInstallationScript(s.Id()) script, err := api.New().GetInstallationScript(s.Id())
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
return errors.WithStackIf(err) return err
} }
return errors.New(err.Error()) return errors.New(err.Error())
@@ -98,7 +99,7 @@ func (s *Server) internalInstall() error {
p, err := NewInstallationProcess(s, &script) p, err := NewInstallationProcess(s, &script)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
s.Log().Info("beginning installation process for server") s.Log().Info("beginning installation process for server")
@@ -130,7 +131,7 @@ func NewInstallationProcess(s *Server, script *api.InstallationScript) (*Install
s.installer.cancel = &cancel s.installer.cancel = &cancel
if c, err := environment.DockerClient(); err != nil { if c, err := environment.DockerClient(); err != nil {
return nil, errors.WithStackIf(err) return nil, err
} else { } else {
proc.client = c proc.client = c
proc.context = ctx proc.context = ctx
@@ -193,7 +194,7 @@ func (ip *InstallationProcess) RemoveContainer() {
}) })
if err != nil && !client.IsErrNotFound(err) { if err != nil && !client.IsErrNotFound(err) {
ip.Server.Log().WithField("error", errors.WithStackIf(err)).Warn("failed to delete server install container") ip.Server.Log().WithField("error", err).Warn("failed to delete server install container")
} }
} }
@@ -218,14 +219,14 @@ func (ip *InstallationProcess) Run() error {
}() }()
if err := ip.BeforeExecute(); err != nil { if err := ip.BeforeExecute(); err != nil {
return errors.WithStackIf(err) return err
} }
cid, err := ip.Execute() cid, err := ip.Execute()
if err != nil { if err != nil {
ip.RemoveContainer() ip.RemoveContainer()
return errors.WithStackIf(err) return err
} }
// If this step fails, log a warning but don't exit out of the process. This is completely // If this step fails, log a warning but don't exit out of the process. This is completely
@@ -248,12 +249,12 @@ func (ip *InstallationProcess) writeScriptToDisk() error {
// Make sure the temp directory root exists before trying to make a directory within it. The // Make sure the temp directory root exists before trying to make a directory within it. The
// ioutil.TempDir call expects this base to exist, it won't create it for you. // ioutil.TempDir call expects this base to exist, it won't create it for you.
if err := os.MkdirAll(ip.tempDir(), 0700); err != nil { if err := os.MkdirAll(ip.tempDir(), 0700); err != nil {
return errors.WrapIf(err, "could not create temporary directory for install process") return errors.WithMessage(err, "could not create temporary directory for install process")
} }
f, err := os.OpenFile(filepath.Join(ip.tempDir(), "install.sh"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) f, err := os.OpenFile(filepath.Join(ip.tempDir(), "install.sh"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil { if err != nil {
return errors.WrapIf(err, "failed to write server installation script to disk before mount") return errors.WithMessage(err, "failed to write server installation script to disk before mount")
} }
defer f.Close() defer f.Close()
@@ -265,7 +266,7 @@ func (ip *InstallationProcess) writeScriptToDisk() error {
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return errors.WithStackIf(err) return err
} }
w.Flush() w.Flush()
@@ -275,11 +276,62 @@ func (ip *InstallationProcess) writeScriptToDisk() error {
// Pulls the docker image to be used for the installation container. // Pulls the docker image to be used for the installation container.
func (ip *InstallationProcess) pullInstallationImage() error { func (ip *InstallationProcess) pullInstallationImage() error {
r, err := ip.client.ImagePull(ip.context, ip.Script.ContainerImage, types.ImagePullOptions{}) // Get a registry auth configuration from the config.
if err != nil { var registryAuth *config.RegistryConfiguration
return errors.WithStackIf(err) for registry, c := range config.Get().Docker.Registries {
if !strings.HasPrefix(ip.Script.ContainerImage, registry) {
continue
}
log.WithField("registry", registry).Debug("using authentication for registry")
registryAuth = &c
break
} }
// Get the ImagePullOptions.
imagePullOptions := types.ImagePullOptions{All: false}
if registryAuth != nil {
b64, err := registryAuth.Base64()
if err != nil {
log.WithError(err).Error("failed to get registry auth credentials")
}
// b64 is a string so if there is an error it will just be empty, not nil.
imagePullOptions.RegistryAuth = b64
}
r, err := ip.client.ImagePull(context.Background(), ip.Script.ContainerImage, imagePullOptions)
if err != nil {
images, ierr := ip.client.ImageList(context.Background(), types.ImageListOptions{})
if ierr != nil {
// Well damn, something has gone really wrong here, just go ahead and abort there
// isn't much anything we can do to try and self-recover from this.
return ierr
}
for _, img := range images {
for _, t := range img.RepoTags {
if t != ip.Script.ContainerImage {
continue
}
log.WithFields(log.Fields{
"image": ip.Script.ContainerImage,
"err": err.Error(),
}).Warn("unable to pull requested image from remote source, however the image exists locally")
// Okay, we found a matching container image, in that case just go ahead and return
// from this function, since there is nothing else we need to do here.
return nil
}
}
return err
}
defer r.Close()
log.WithField("image", ip.Script.ContainerImage).Debug("pulling docker image... this could take a bit of time")
// Block continuation until the image has been pulled successfully. // Block continuation until the image has been pulled successfully.
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
for scanner.Scan() { for scanner.Scan() {
@@ -287,7 +339,7 @@ func (ip *InstallationProcess) pullInstallationImage() error {
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -298,11 +350,11 @@ func (ip *InstallationProcess) pullInstallationImage() error {
// manner, if either one fails the error is returned. // manner, if either one fails the error is returned.
func (ip *InstallationProcess) BeforeExecute() error { func (ip *InstallationProcess) BeforeExecute() error {
if err := ip.writeScriptToDisk(); err != nil { if err := ip.writeScriptToDisk(); err != nil {
return errors.WrapIf(err, "failed to write installation script to disk") return errors.WithMessage(err, "failed to write installation script to disk")
} }
if err := ip.pullInstallationImage(); err != nil { if err := ip.pullInstallationImage(); err != nil {
return errors.WrapIf(err, "failed to pull updated installation container image for server") return errors.WithMessage(err, "failed to pull updated installation container image for server")
} }
opts := types.ContainerRemoveOptions{ opts := types.ContainerRemoveOptions{
@@ -312,7 +364,7 @@ func (ip *InstallationProcess) BeforeExecute() error {
if err := ip.client.ContainerRemove(ip.context, ip.Server.Id()+"_installer", opts); err != nil { if err := ip.client.ContainerRemove(ip.context, ip.Server.Id()+"_installer", opts); err != nil {
if !client.IsErrNotFound(err) { if !client.IsErrNotFound(err) {
return errors.WrapIf(err, "failed to remove existing install container for server") return errors.WithMessage(err, "failed to remove existing install container for server")
} }
} }
@@ -338,12 +390,12 @@ func (ip *InstallationProcess) AfterExecute(containerId string) error {
}) })
if err != nil && !client.IsErrNotFound(err) { if err != nil && !client.IsErrNotFound(err) {
return errors.WithStackIf(err) return err
} }
f, err := os.OpenFile(ip.GetLogPath(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) f, err := os.OpenFile(ip.GetLogPath(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer f.Close() defer f.Close()
@@ -372,15 +424,15 @@ func (ip *InstallationProcess) AfterExecute(containerId string) error {
| ------------------------------ | ------------------------------
`) `)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
if err := tmpl.Execute(f, ip); err != nil { if err := tmpl.Execute(f, ip); err != nil {
return errors.WithStackIf(err) return err
} }
if _, err := io.Copy(f, reader); err != nil { if _, err := io.Copy(f, reader); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -448,7 +500,7 @@ func (ip *InstallationProcess) Execute() (string, error) {
r, err := ip.client.ContainerCreate(ip.context, conf, hostConf, nil, ip.Server.Id()+"_installer") r, err := ip.client.ContainerCreate(ip.context, conf, hostConf, nil, ip.Server.Id()+"_installer")
if err != nil { if err != nil {
return "", errors.WithStackIf(err) return "", err
} }
ip.Server.Log().WithField("container_id", r.ID).Info("running installation script for server in container") ip.Server.Log().WithField("container_id", r.ID).Info("running installation script for server in container")
@@ -468,7 +520,7 @@ func (ip *InstallationProcess) Execute() (string, error) {
select { select {
case err := <-eChan: case err := <-eChan:
if err != nil { if err != nil {
return "", errors.WithStackIf(err) return "", err
} }
case <-sChan: case <-sChan:
} }
@@ -487,7 +539,7 @@ func (ip *InstallationProcess) StreamOutput(id string) error {
}) })
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
defer reader.Close() defer reader.Close()
@@ -500,7 +552,7 @@ func (ip *InstallationProcess) StreamOutput(id string) error {
if err := s.Err(); err != nil { if err := s.Err(); err != nil {
ip.Server.Log().WithFields(log.Fields{ ip.Server.Log().WithFields(log.Fields{
"container_id": id, "container_id": id,
"error": errors.WithStackIf(err), "error": err,
}).Warn("error processing scanner line in installation output for server") }).Warn("error processing scanner line in installation output for server")
} }
@@ -515,7 +567,7 @@ func (s *Server) SyncInstallState(successful bool) error {
err := api.New().SendInstallationStatus(s.Id(), successful) err := api.New().SendInstallationStatus(s.Id(), successful)
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
return errors.WithStackIf(err) return err
} }
return errors.New(err.Error()) return errors.New(err.Error())

View File

@@ -1,7 +1,6 @@
package server package server
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/apex/log" "github.com/apex/log"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
@@ -77,7 +76,7 @@ func (s *Server) StartEventListeners() {
s.Environment.SetState(environment.ProcessRunningState) s.Environment.SetState(environment.ProcessRunningState)
} }
s.Log().WithField("error", errors.WithStackIf(err)).Error("failed to terminate environment after triggering throttle") s.Log().WithField("error", err).Error("failed to terminate environment after triggering throttle")
} }
}() }()
} }
@@ -106,7 +105,7 @@ func (s *Server) StartEventListeners() {
stats := func(e events.Event) { stats := func(e events.Event) {
st := new(environment.Stats) st := new(environment.Stats)
if err := json.Unmarshal([]byte(e.Data), st); err != nil { if err := json.Unmarshal([]byte(e.Data), st); err != nil {
s.Log().WithField("error", errors.WithStackIf(err)).Warn("failed to unmarshal server environment stats") s.Log().WithField("error", err).Warn("failed to unmarshal server environment stats")
return return
} }
@@ -152,7 +151,7 @@ func (s *Server) onConsoleOutput(data string) {
processConfiguration := s.ProcessConfiguration() processConfiguration := s.ProcessConfiguration()
// Check if the server is currently starting. // Check if the server is currently starting.
if s.GetState() == environment.ProcessStartingState { if s.Environment.State() == environment.ProcessStartingState {
// Check if we should strip ansi color codes. // Check if we should strip ansi color codes.
if processConfiguration.Startup.StripAnsi { if processConfiguration.Startup.StripAnsi {
// Strip ansi color codes from the data string. // Strip ansi color codes from the data string.

View File

@@ -1,12 +1,12 @@
package server package server
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/creasty/defaults" "github.com/creasty/defaults"
"github.com/gammazero/workerpool" "github.com/gammazero/workerpool"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
@@ -35,7 +35,7 @@ func LoadDirectory() error {
configs, err := api.New().GetServers() configs, err := api.New().GetServers()
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
return errors.WithStackIf(err) return err
} }
return errors.New(err.Error()) return errors.New(err.Error())
@@ -89,12 +89,12 @@ func LoadDirectory() error {
func FromConfiguration(data api.ServerConfigurationResponse) (*Server, error) { func FromConfiguration(data api.ServerConfigurationResponse) (*Server, error) {
cfg := Configuration{} cfg := Configuration{}
if err := defaults.Set(&cfg); err != nil { if err := defaults.Set(&cfg); err != nil {
return nil, errors.WrapIf(err, "failed to set struct defaults for server configuration") return nil, errors.WithMessage(err, "failed to set struct defaults for server configuration")
} }
s := new(Server) s := new(Server)
if err := defaults.Set(s); err != nil { if err := defaults.Set(s); err != nil {
return nil, errors.WrapIf(err, "failed to set struct defaults for server") return nil, errors.WithMessage(err, "failed to set struct defaults for server")
} }
s.cfg = cfg s.cfg = cfg

View File

@@ -49,7 +49,9 @@ func (s *Server) customMounts() []environment.Mount {
mounted := false mounted := false
for _, allowed := range config.Get().AllowedMounts { for _, allowed := range config.Get().AllowedMounts {
if !strings.HasPrefix(source, allowed) { // Check if the source path is included in the allowed mounts list.
// filepath.Clean will strip all trailing slashes (unless the path is a root directory).
if !strings.HasPrefix(source, filepath.Clean(allowed)) {
continue continue
} }

View File

@@ -2,7 +2,7 @@ package server
import ( import (
"context" "context"
"emperror.dev/errors" "github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
@@ -80,13 +80,13 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
// time than that passes an error will be propagated back up the chain and this // time than that passes an error will be propagated back up the chain and this
// request will be aborted. // request will be aborted.
if err := s.powerLock.Acquire(ctx, 1); err != nil { if err := s.powerLock.Acquire(ctx, 1); err != nil {
return errors.WrapIf(err, "could not acquire lock on power state") return errors.WithMessage(err, "could not acquire lock on power state")
} }
} else { } else {
// If no wait duration was provided we will attempt to immediately acquire the lock // If no wait duration was provided we will attempt to immediately acquire the lock
// and bail out with a context deadline error if it is not acquired immediately. // and bail out with a context deadline error if it is not acquired immediately.
if ok := s.powerLock.TryAcquire(1); !ok { if ok := s.powerLock.TryAcquire(1); !ok {
return errors.WrapIf(context.DeadlineExceeded, "could not acquire lock on power state") return errors.WithMessage(context.DeadlineExceeded, "could not acquire lock on power state")
} }
} }
@@ -104,7 +104,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
switch action { switch action {
case PowerActionStart: case PowerActionStart:
if s.GetState() != environment.ProcessOfflineState { if s.Environment.State() != environment.ProcessOfflineState {
return ErrIsRunning return ErrIsRunning
} }
@@ -149,7 +149,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
func (s *Server) onBeforeStart() error { func (s *Server) onBeforeStart() error {
s.Log().Info("syncing server configuration with panel") s.Log().Info("syncing server configuration with panel")
if err := s.Sync(); err != nil { if err := s.Sync(); err != nil {
return errors.WrapIf(err, "unable to sync server data from Panel instance") return errors.WithMessage(err, "unable to sync server data from Panel instance")
} }
// Disallow start & restart if the server is suspended. Do this check after performing a sync // Disallow start & restart if the server is suspended. Do this check after performing a sync
@@ -185,7 +185,7 @@ func (s *Server) onBeforeStart() error {
s.PublishConsoleOutputFromDaemon("Ensuring file permissions are set correctly, this could take a few seconds...") s.PublishConsoleOutputFromDaemon("Ensuring file permissions are set correctly, this could take a few seconds...")
// Ensure all of the server file permissions are set correctly before booting the process. // Ensure all of the server file permissions are set correctly before booting the process.
if err := s.Filesystem().Chown("/"); err != nil { if err := s.Filesystem().Chown("/"); err != nil {
return errors.WrapIf(err, "failed to chown root server directory during pre-boot process") return errors.WithMessage(err, "failed to chown root server directory during pre-boot process")
} }
} }

View File

@@ -2,9 +2,9 @@ package server
import ( import (
"context" "context"
"emperror.dev/errors"
"fmt" "fmt"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
@@ -115,7 +115,7 @@ func (s *Server) Sync() error {
cfg, err := api.New().GetServerConfiguration(s.Id()) cfg, err := api.New().GetServerConfiguration(s.Id())
if err != nil { if err != nil {
if !api.IsRequestError(err) { if !api.IsRequestError(err) {
return errors.WithStackIf(err) return err
} }
if err.(*api.RequestError).Status == "404" { if err.(*api.RequestError).Status == "404" {
@@ -131,7 +131,7 @@ func (s *Server) Sync() error {
func (s *Server) SyncWithConfiguration(cfg api.ServerConfigurationResponse) error { func (s *Server) SyncWithConfiguration(cfg api.ServerConfigurationResponse) error {
// Update the data structure and persist it to the disk. // Update the data structure and persist it to the disk.
if err := s.UpdateDataStructure(cfg.Settings); err != nil { if err := s.UpdateDataStructure(cfg.Settings); err != nil {
return errors.WithStackIf(err) return err
} }
s.Lock() s.Lock()
@@ -171,7 +171,7 @@ func (s *Server) IsBootable() bool {
func (s *Server) CreateEnvironment() error { func (s *Server) CreateEnvironment() error {
// Ensure the data directory exists before getting too far through this process. // Ensure the data directory exists before getting too far through this process.
if err := s.EnsureDataDirectoryExists(); err != nil { if err := s.EnsureDataDirectoryExists(); err != nil {
return errors.WithStackIf(err) return err
} }
return s.Environment.Create() return s.Environment.Create()

View File

@@ -1,7 +1,6 @@
package server package server
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
@@ -22,14 +21,14 @@ func CachedServerStates() (map[string]string, error) {
// Open the states file. // Open the states file.
f, err := os.OpenFile(config.Get().System.GetStatesPath(), os.O_RDONLY|os.O_CREATE, 0644) f, err := os.OpenFile(config.Get().System.GetStatesPath(), os.O_RDONLY|os.O_CREATE, 0644)
if err != nil { if err != nil {
return nil, errors.WithStackIf(err) return nil, err
} }
defer f.Close() defer f.Close()
// Convert the json object to a map. // Convert the json object to a map.
states := map[string]string{} states := map[string]string{}
if err := json.NewDecoder(f).Decode(&states); err != nil && err != io.EOF { if err := json.NewDecoder(f).Decode(&states); err != nil && err != io.EOF {
return nil, errors.WithStackIf(err) return nil, err
} }
return states, nil return states, nil
@@ -40,13 +39,13 @@ func saveServerStates() error {
// Get the states of all servers on the daemon. // Get the states of all servers on the daemon.
states := map[string]string{} states := map[string]string{}
for _, s := range GetServers().All() { for _, s := range GetServers().All() {
states[s.Id()] = s.GetState() states[s.Id()] = s.Environment.State()
} }
// Convert the map to a json object. // Convert the map to a json object.
data, err := json.Marshal(states) data, err := json.Marshal(states)
if err != nil { if err != nil {
return errors.WithStackIf(err) return err
} }
stateMutex.Lock() stateMutex.Lock()
@@ -54,7 +53,7 @@ func saveServerStates() error {
// Write the data to the file // Write the data to the file
if err := ioutil.WriteFile(config.Get().System.GetStatesPath(), data, 0644); err != nil { if err := ioutil.WriteFile(config.Get().System.GetStatesPath(), data, 0644); err != nil {
return errors.WithStackIf(err) return err
} }
return nil return nil
@@ -107,7 +106,7 @@ func (s *Server) OnStateChange() {
// automatically attempt to start the process back up for the user. This is done in a // automatically attempt to start the process back up for the user. This is done in a
// separate thread as to not block any actions currently taking place in the flow // separate thread as to not block any actions currently taking place in the flow
// that called this function. // that called this function.
if (prevState == environment.ProcessStartingState || prevState == environment.ProcessRunningState) && s.GetState() == environment.ProcessOfflineState { if (prevState == environment.ProcessStartingState || prevState == environment.ProcessRunningState) && s.Environment.State() == environment.ProcessOfflineState {
s.Log().Info("detected server as entering a crashed state; running crash handler") s.Log().Info("detected server as entering a crashed state; running crash handler")
go func(server *Server) { go func(server *Server) {
@@ -115,6 +114,7 @@ func (s *Server) OnStateChange() {
if IsTooFrequentCrashError(err) { if IsTooFrequentCrashError(err) {
server.Log().Info("did not restart server after crash; occurred too soon after the last") server.Log().Info("did not restart server after crash; occurred too soon after the last")
} else { } else {
s.PublishConsoleOutputFromDaemon("Server crash was detected but an error occurred while handling it.")
server.Log().WithField("error", err).Error("failed to handle server crash") server.Log().WithField("error", err).Error("failed to handle server crash")
} }
} }
@@ -133,7 +133,7 @@ func (s *Server) GetState() string {
// environment state, it is simply the tracked state from this daemon instance, and // environment state, it is simply the tracked state from this daemon instance, and
// not the response from Docker. // not the response from Docker.
func (s *Server) IsRunning() bool { func (s *Server) IsRunning() bool {
st := s.GetState() st := s.Environment.State()
return st == environment.ProcessRunningState || st == environment.ProcessStartingState return st == environment.ProcessRunningState || st == environment.ProcessStartingState
} }

View File

@@ -1,10 +1,10 @@
package server package server
import ( import (
"emperror.dev/errors"
"encoding/json" "encoding/json"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
) )
@@ -18,7 +18,7 @@ import (
func (s *Server) UpdateDataStructure(data []byte) error { func (s *Server) UpdateDataStructure(data []byte) error {
src := new(Configuration) src := new(Configuration)
if err := json.Unmarshal(data, src); err != nil { if err := json.Unmarshal(data, src); err != nil {
return errors.WithStackIf(err) return err
} }
// Don't allow obviously corrupted data to pass through into this function. If the UUID // Don't allow obviously corrupted data to pass through into this function. If the UUID
@@ -47,7 +47,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
// Merge the new data object that we have received with the existing server data object // Merge the new data object that we have received with the existing server data object
// and then save it to the disk so it is persistent. // and then save it to the disk so it is persistent.
if err := mergo.Merge(&c, src, mergo.WithOverride); err != nil { if err := mergo.Merge(&c, src, mergo.WithOverride); err != nil {
return errors.WithStackIf(err) return err
} }
// Don't explode if we're setting CPU limits to 0. Mergo sees that as an empty value // Don't explode if we're setting CPU limits to 0. Mergo sees that as an empty value
@@ -65,7 +65,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
// request is going to be boolean. Allegedly. // request is going to be boolean. Allegedly.
if v, err := jsonparser.GetBoolean(data, "container", "oom_disabled"); err != nil { if v, err := jsonparser.GetBoolean(data, "container", "oom_disabled"); err != nil {
if err != jsonparser.KeyPathNotFoundError { if err != jsonparser.KeyPathNotFoundError {
return errors.WithStackIf(err) return err
} }
} else { } else {
c.Build.OOMDisabled = v c.Build.OOMDisabled = v
@@ -74,7 +74,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
// Mergo also cannot handle this boolean value. // Mergo also cannot handle this boolean value.
if v, err := jsonparser.GetBoolean(data, "suspended"); err != nil { if v, err := jsonparser.GetBoolean(data, "suspended"); err != nil {
if err != jsonparser.KeyPathNotFoundError { if err != jsonparser.KeyPathNotFoundError {
return errors.WithStackIf(err) return err
} }
} else { } else {
c.Suspended = v c.Suspended = v
@@ -82,7 +82,7 @@ func (s *Server) UpdateDataStructure(data []byte) error {
if v, err := jsonparser.GetBoolean(data, "skip_egg_scripts"); err != nil { if v, err := jsonparser.GetBoolean(data, "skip_egg_scripts"); err != nil {
if err != jsonparser.KeyPathNotFoundError { if err != jsonparser.KeyPathNotFoundError {
return errors.WithStackIf(err) return err
} }
} else { } else {
c.SkipEggScripts = v c.SkipEggScripts = v
@@ -143,7 +143,7 @@ func (s *Server) SyncWithEnvironment() {
} else { } else {
// Checks if the server is now in a suspended state. If so and a server process is currently running it // Checks if the server is now in a suspended state. If so and a server process is currently running it
// will be gracefully stopped (and terminated if it refuses to stop). // will be gracefully stopped (and terminated if it refuses to stop).
if s.GetState() != environment.ProcessOfflineState { if s.Environment.State() != environment.ProcessOfflineState {
s.Log().Info("server suspended with running process state, terminating now") s.Log().Info("server suspended with running process state, terminating now")
go func(s *Server) { go func(s *Server) {

View File

@@ -1,7 +1,6 @@
package sftp package sftp
import ( import (
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"github.com/pkg/sftp" "github.com/pkg/sftp"
@@ -58,14 +57,14 @@ func (fs FileSystem) Fileread(request *sftp.Request) (io.ReaderAt, error) {
if _, err := os.Stat(p); os.IsNotExist(err) { if _, err := os.Stat(p); os.IsNotExist(err) {
return nil, sftp.ErrSshFxNoSuchFile return nil, sftp.ErrSshFxNoSuchFile
} else if err != nil { } else if err != nil {
fs.logger.WithField("error", errors.WithStackIf(err)).Error("error while processing file stat") fs.logger.WithField("error", err).Error("error while processing file stat")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }
file, err := os.Open(p) file, err := os.Open(p)
if err != nil { if err != nil {
fs.logger.WithField("source", p).WithField("error", errors.WithStackIf(err)).Error("could not open file for reading") fs.logger.WithField("source", p).WithField("error", err).Error("could not open file for reading")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }
@@ -108,7 +107,7 @@ func (fs FileSystem) Filewrite(request *sftp.Request) (io.WriterAt, error) {
if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
l.WithFields(log.Fields{ l.WithFields(log.Fields{
"path": filepath.Dir(p), "path": filepath.Dir(p),
"error": errors.WithStackIf(err), "error": err,
}).Error("error making path for file") }).Error("error making path for file")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
@@ -116,7 +115,7 @@ func (fs FileSystem) Filewrite(request *sftp.Request) (io.WriterAt, error) {
file, err := os.Create(p) file, err := os.Create(p)
if err != nil { if err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to create file") l.WithField("error", err).Error("failed to create file")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }
@@ -124,7 +123,7 @@ func (fs FileSystem) Filewrite(request *sftp.Request) (io.WriterAt, error) {
// Not failing here is intentional. We still made the file, it is just owned incorrectly // Not failing here is intentional. We still made the file, it is just owned incorrectly
// and will likely cause some issues. // and will likely cause some issues.
if err := os.Chown(p, fs.User.Uid, fs.User.Gid); err != nil { if err := os.Chown(p, fs.User.Uid, fs.User.Gid); err != nil {
l.WithField("error", errors.WithStackIf(err)).Warn("failed to set permissions on file") l.WithField("error", err).Warn("failed to set permissions on file")
} }
return file, nil return file, nil
@@ -133,7 +132,7 @@ func (fs FileSystem) Filewrite(request *sftp.Request) (io.WriterAt, error) {
// If the stat error isn't about the file not existing, there is some other issue // If the stat error isn't about the file not existing, there is some other issue
// at play and we need to go ahead and bail out of the process. // at play and we need to go ahead and bail out of the process.
if statErr != nil { if statErr != nil {
l.WithField("error", errors.WithStackIf(statErr)).Error("encountered error performing file stat") l.WithField("error", statErr).Error("encountered error performing file stat")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }
@@ -159,14 +158,14 @@ func (fs FileSystem) Filewrite(request *sftp.Request) (io.WriterAt, error) {
return nil, sftp.ErrSSHFxNoSuchFile return nil, sftp.ErrSSHFxNoSuchFile
} }
l.WithField("flags", request.Flags).WithField("error", errors.WithStackIf(err)).Error("failed to open existing file on system") l.WithField("flags", request.Flags).WithField("error", err).Error("failed to open existing file on system")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }
// Not failing here is intentional. We still made the file, it is just owned incorrectly // Not failing here is intentional. We still made the file, it is just owned incorrectly
// and will likely cause some issues. // and will likely cause some issues.
if err := os.Chown(p, fs.User.Uid, fs.User.Gid); err != nil { if err := os.Chown(p, fs.User.Uid, fs.User.Gid); err != nil {
l.WithField("error", errors.WithStackIf(err)).Warn("error chowning file") l.WithField("error", err).Warn("error chowning file")
} }
return file, nil return file, nil
@@ -220,7 +219,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
return sftp.ErrSSHFxNoSuchFile return sftp.ErrSSHFxNoSuchFile
} }
l.WithField("error", errors.WithStackIf(err)).Error("failed to perform setstat on item") l.WithField("error", err).Error("failed to perform setstat on item")
return sftp.ErrSSHFxFailure return sftp.ErrSSHFxFailure
} }
return nil return nil
@@ -234,7 +233,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
return sftp.ErrSSHFxNoSuchFile return sftp.ErrSSHFxNoSuchFile
} }
l.WithField("target", target).WithField("error", errors.WithStackIf(err)).Error("failed to rename file") l.WithField("target", target).WithField("error", err).Error("failed to rename file")
return sftp.ErrSshFxFailure return sftp.ErrSshFxFailure
} }
@@ -246,7 +245,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
} }
if err := os.RemoveAll(p); err != nil { if err := os.RemoveAll(p); err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to remove directory") l.WithField("error", err).Error("failed to remove directory")
return sftp.ErrSshFxFailure return sftp.ErrSshFxFailure
} }
@@ -258,7 +257,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
} }
if err := os.MkdirAll(p, 0755); err != nil { if err := os.MkdirAll(p, 0755); err != nil {
l.WithField("error", errors.WithStackIf(err)).Error("failed to create directory") l.WithField("error", err).Error("failed to create directory")
return sftp.ErrSshFxFailure return sftp.ErrSshFxFailure
} }
@@ -270,7 +269,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
} }
if err := os.Symlink(p, target); err != nil { if err := os.Symlink(p, target); err != nil {
l.WithField("target", target).WithField("error", errors.WithStackIf(err)).Error("failed to create symlink") l.WithField("target", target).WithField("error", err).Error("failed to create symlink")
return sftp.ErrSshFxFailure return sftp.ErrSshFxFailure
} }
@@ -286,7 +285,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
return sftp.ErrSSHFxNoSuchFile return sftp.ErrSSHFxNoSuchFile
} }
l.WithField("error", errors.WithStackIf(err)).Error("failed to remove a file") l.WithField("error", err).Error("failed to remove a file")
return sftp.ErrSshFxFailure return sftp.ErrSshFxFailure
} }
@@ -305,7 +304,7 @@ func (fs FileSystem) Filecmd(request *sftp.Request) error {
// and will likely cause some issues. There is no logical check for if the file was removed // and will likely cause some issues. There is no logical check for if the file was removed
// because both of those cases (Rmdir, Remove) have an explicit return rather than break. // because both of those cases (Rmdir, Remove) have an explicit return rather than break.
if err := os.Chown(fileLocation, fs.User.Uid, fs.User.Gid); err != nil { if err := os.Chown(fileLocation, fs.User.Uid, fs.User.Gid); err != nil {
l.WithField("error", errors.WithStackIf(err)).Warn("error chowning file") l.WithField("error", err).Warn("error chowning file")
} }
return sftp.ErrSshFxOk return sftp.ErrSshFxOk
@@ -327,7 +326,7 @@ func (fs FileSystem) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
files, err := ioutil.ReadDir(p) files, err := ioutil.ReadDir(p)
if err != nil { if err != nil {
fs.logger.WithField("error", errors.WithStackIf(err)).Error("error while listing directory") fs.logger.WithField("error", err).Error("error while listing directory")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }
@@ -342,7 +341,7 @@ func (fs FileSystem) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, sftp.ErrSshFxNoSuchFile return nil, sftp.ErrSshFxNoSuchFile
} else if err != nil { } else if err != nil {
fs.logger.WithField("source", p).WithField("error", errors.WithStackIf(err)).Error("error performing stat on file") fs.logger.WithField("source", p).WithField("error", err).Error("error performing stat on file")
return nil, sftp.ErrSshFxFailure return nil, sftp.ErrSshFxFailure
} }

View File

@@ -1,8 +1,8 @@
package sftp package sftp
import ( import (
"emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
@@ -28,14 +28,14 @@ func Initialize(config config.SystemConfiguration) error {
} }
if err := New(s); err != nil { if err := New(s); err != nil {
return errors.WithStackIf(err) return err
} }
// Initialize the SFTP server in a background thread since this is // Initialize the SFTP server in a background thread since this is
// a long running operation. // a long running operation.
go func(s *Server) { go func(s *Server) {
if err := s.Initialize(); err != nil { if err := s.Initialize(); err != nil {
log.WithField("subsystem", "sftp").WithField("error", errors.WithStackIf(err)).Error("failed to initialize SFTP subsystem") log.WithField("subsystem", "sftp").WithField("error", err).Error("failed to initialize SFTP subsystem")
} }
}(s) }(s)