Compare commits

...

22 Commits

Author SHA1 Message Date
Dane Everitt
9ea991d86b Bump for release 2020-05-03 21:38:22 -07:00
Dane Everitt
1b2eb50a32 Update CHANGELOG.md 2020-05-03 21:37:36 -07:00
Dane Everitt
fab5d36917 Remove redundant code 2020-05-03 21:37:22 -07:00
Dane Everitt
ee184768b8 Remove unused code 2020-05-03 21:31:02 -07:00
Dane Everitt
2e055cf630 Unsubscribe any open event listeners when deleting a server 2020-05-03 21:30:16 -07:00
Dane Everitt
fab489d264 Check for server existence when connecting to a websocket 2020-05-03 21:30:07 -07:00
Dane Everitt
7f93e5f9d5 Merge branch 'develop' of https://github.com/pterodactyl/wings into develop 2020-05-03 21:04:23 -07:00
Dane Everitt
ac011214f7 Ensure the temp directory exists before trying to make a directory in it 2020-05-03 21:04:16 -07:00
Dane Everitt
58262aa252 Merge pull request #29 from pterodactyl/add/dockerfile
Add dockerfile
2020-05-03 20:44:04 -07:00
Dane Everitt
eba5aa8cbe Merge pull request #26 from pterodactyl/add/data_folder
add data folder on first startup
2020-05-03 20:43:03 -07:00
Dane Everitt
b2797ed292 Abstract out shared backup functionality 2020-05-02 15:02:02 -07:00
Dane Everitt
507d0100cf Hilariously rough code to get a backup pushed into S3 2020-04-26 17:20:26 -07:00
Dane Everitt
91d12ab9a7 More abstract support for backups & misc code cleanup in that area 2020-04-26 16:43:18 -07:00
Dane Everitt
1e2da95d26 Support data coming from the panel better 2020-04-26 16:21:58 -07:00
Michael Parker
2828eaed32 update dockerfile
add build flags
add upx for application compression
2020-04-26 18:12:01 -04:00
Michael Parker
12d43a9f49 add trailing /
add trailing to signify a folder.
2020-04-25 22:38:18 -04:00
Michael Parker
00ed6f3985 update for new defaults. 2020-04-25 22:37:00 -04:00
Michael Parker
377cae4d48 Add docker files
Add Dockerfile
Add docker-compose.yml
2020-04-25 22:37:00 -04:00
Michael Parker
9c5855663c return errors
Actually return errors for stat and mkdir.
2020-04-14 22:06:19 -04:00
Michael (Parker) Parker
da093e7cf7 Update config/config.go
remove extra logging.

Co-Authored-By: Lance Pioch <git@lance.sh>
2020-04-14 13:07:10 -04:00
Michael Parker
df9c4835c4 fix folder adding
Sorry dane I missed that I nested those.
2020-04-14 00:03:16 -04:00
Michael Parker
65102966a1 add data folder on startup
Instead of making users create the data folder create it for them on startup if it doesn't exist.
2020-04-13 18:17:00 -04:00
19 changed files with 427 additions and 202 deletions

1
.gitignore vendored
View File

@@ -46,3 +46,4 @@ test_*/
!.gitkeep !.gitkeep
debug debug
data/.states.json data/.states.json
.DS_Store

View File

@@ -1,5 +1,16 @@
# Changelog # Changelog
## v1.0.0-beta.3
### Fixed
* Daemon will no longer crash if someone requests a websocket for a deleted server.
* Temporary directories are now created properly if missing during the server installation process.
### Added
* Added support for using Amazon S3 as a backup location for archives.
### Changed
* Memory overhead for containers is now 5/10/15% higher than the passed limit to account for JVM heap and prevent crashing.
## v1.0.0-alpha.2 ## v1.0.0-alpha.2
### Added ### Added
* Ability to run an installation process for a server and notify the panel when completed. * Ability to run an installation process for a server and notify the panel when completed.

14
Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
# ----------------------------------
# Pterodactyl Panel Dockerfile
# ----------------------------------
FROM golang:1.14-alpine
COPY . /go/wings/
WORKDIR /go/wings/
RUN apk add --no-cache upx \
&& go build -ldflags="-s -w" \
&& upx --brute wings
FROM alpine:latest
COPY --from=0 /go/wings/wings /usr/bin/
CMD ["wings","--config", "/var/lib/pterodactyl/config.yml"]

View File

@@ -213,17 +213,6 @@ func rootCmdRun(*cobra.Command, []string) {
zap.S().Fatalw("failed to configure HTTP server", zap.Error(err)) zap.S().Fatalw("failed to configure HTTP server", zap.Error(err))
} }
} }
// r := &Router{
// token: c.AuthenticationToken,
// upgrader: websocket.Upgrader{
// // Ensure that the websocket request is originating from the Panel itself,
// // and not some other location.
// CheckOrigin: func(r *http.Request) bool {
// return r.Header.Get("Origin") == c.PanelLocation
// },
// },
// }
} }
// Execute calls cobra to handle cli commands // Execute calls cobra to handle cli commands

View File

@@ -0,0 +1,26 @@
version: '3'
services:
daemon:
build: .
restart: always
hostname: daemon
ports:
- "8080:8080"
- "2022:2022"
tty: true
environment:
- "DEBUG=false"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/var/lib/docker/containers/:/var/lib/docker/containers/"
- "/var/lib/pterodactyl/:/var/lib/pterodactyl/"
- "/srv/daemon-data/:/srv/daemon-data/"
- "/tmp/pterodactyl/:/tmp/pterodactyl/"
- "/etc/timezone:/etc/timezone:ro"
## Required for ssl if you user let's encrypt. uncomment to use.
## - "/etc/letsencrypt/:/etc/letsencrypt/"
networks:
default:
ipam:
config:
- subnet: 172.21.0.0/16

12
go.mod
View File

@@ -17,6 +17,7 @@ require (
github.com/Microsoft/go-winio v0.4.7 // indirect github.com/Microsoft/go-winio v0.4.7 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/aws/aws-sdk-go v1.30.14 // indirect
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/buger/jsonparser v0.0.0-20191204142016-1a29609e0929 github.com/buger/jsonparser v0.0.0-20191204142016-1a29609e0929
github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249 github.com/cobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249
@@ -55,16 +56,17 @@ require (
github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94
github.com/smartystreets/goconvey v1.6.4 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/spf13/cobra v0.0.7 github.com/spf13/cobra v0.0.7
github.com/stretchr/testify v1.5.1 // indirect github.com/stretchr/objx v0.2.0 // indirect
github.com/yuin/goldmark v1.1.30 // indirect
go.uber.org/atomic v1.5.1 // indirect go.uber.org/atomic v1.5.1 // indirect
go.uber.org/multierr v1.4.0 // indirect go.uber.org/multierr v1.4.0 // indirect
go.uber.org/zap v1.13.0 go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 // indirect golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 // indirect
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect
golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290 // indirect golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/ini.v1 v1.51.0 gopkg.in/ini.v1 v1.51.0
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8

15
go.sum
View File

@@ -21,6 +21,8 @@ github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.30.14 h1:vZfX2b/fknc9wKcytbLWykM7in5k6dbQ8iHTJDUP1Ng=
github.com/aws/aws-sdk-go v1.30.14/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -80,6 +82,7 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
@@ -124,6 +127,8 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -250,6 +255,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -271,6 +277,7 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= 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=
@@ -300,6 +307,8 @@ golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -317,9 +326,12 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -342,6 +354,8 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@@ -362,6 +376,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290 h1:NXNmtp0ToD36cui5IqWy95LC4Y6vT/4y3RnPxlQPinU= golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290 h1:NXNmtp0ToD36cui5IqWy95LC4Y6vT/4y3RnPxlQPinU=
golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools/gopls v0.1.3/go.mod h1:vrCQzOKxvuiZLjCKSmbbov04oeBQQOb4VQqwYK2PWIY= golang.org/x/tools/gopls v0.1.3/go.mod h1:vrCQzOKxvuiZLjCKSmbbov04oeBQQOb4VQqwYK2PWIY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -58,7 +58,7 @@ func ServerExists(c *gin.Context) {
u, err := uuid.Parse(c.Param("server")) u, err := uuid.Parse(c.Param("server"))
if err != nil || GetServer(u.String()) == nil { if err != nil || GetServer(u.String()) == nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{ c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested server does not exist.", "error": "The resource you requested does not exist.",
}) })
return return
} }

View File

@@ -20,12 +20,12 @@ func Configure() *gin.Engine {
// This route is special it sits above all of the other requests because we are // This route is special it sits above all of the other requests because we are
// using a JWT to authorize access to it, therefore it needs to be publicly // using a JWT to authorize access to it, therefore it needs to be publicly
// accessible. // accessible.
router.GET("/api/servers/:server/ws", getServerWebsocket) router.GET("/api/servers/:server/ws", ServerExists, getServerWebsocket)
// This request is called by another daemon when a server is going to be transferred out. // This request is called by another daemon when a server is going to be transferred out.
// This request does not need the AuthorizationMiddleware as the panel should never call it // This request does not need the AuthorizationMiddleware as the panel should never call it
// and requests are authenticated through a JWT the panel issues to the other daemon. // and requests are authenticated through a JWT the panel issues to the other daemon.
router.GET("/api/servers/:server/archive", getServerArchive) router.GET("/api/servers/:server/archive", ServerExists, getServerArchive)
// All of the routes beyond this mount will use an authorization middleware // All of the routes beyond this mount will use an authorization middleware
// and will not be accessible without the correct Authorization header provided. // and will not be accessible without the correct Authorization header provided.

View File

@@ -178,6 +178,9 @@ func deleteServer(c *gin.Context) {
zap.S().Warnw("failed to delete server archive during deletion process", zap.String("server", s.Uuid), zap.Error(err)) zap.S().Warnw("failed to delete server archive during deletion process", zap.String("server", s.Uuid), zap.Error(err))
} }
// Unsubscribe all of the event listeners.
s.Events().UnsubscribeAll()
// Destroy the environment; in Docker this will handle a running container and // Destroy the environment; in Docker this will handle a running container and
// forcibly terminate it before removing the container, so we do not need to handle // forcibly terminate it before removing the container, so we do not need to handle
// that here. // that here.

View File

@@ -1,6 +1,8 @@
package router package router
import ( import (
"errors"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
"github.com/pterodactyl/wings/server/backup" "github.com/pterodactyl/wings/server/backup"
@@ -12,14 +14,33 @@ import (
func postServerBackup(c *gin.Context) { func postServerBackup(c *gin.Context) {
s := GetServer(c.Param("server")) s := GetServer(c.Param("server"))
data := &backup.LocalBackup{} data := &backup.Request{}
c.BindJSON(&data) c.BindJSON(&data)
go func(b *backup.LocalBackup, serv *server.Server) { var adapter backup.BackupInterface
if err := serv.BackupLocal(b); err != nil { var err error
switch data.Adapter {
case backup.LocalBackupAdapter:
adapter, err = data.NewLocalBackup()
case backup.S3BackupAdapter:
adapter, err = data.NewS3Backup()
default:
err = errors.New(fmt.Sprintf("unknown backup adapter [%s] provided", data.Adapter))
return
}
if err != nil {
TrackedServerError(err, s).AbortWithServerError(c)
return
}
go func(b backup.BackupInterface, serv *server.Server) {
if err := serv.Backup(b); err != nil {
zap.S().Errorw("failed to generate backup for server", zap.Error(err)) zap.S().Errorw("failed to generate backup for server", zap.Error(err))
} }
}(data, s) }(adapter, s)
c.Status(http.StatusAccepted) c.Status(http.StatusAccepted)
} }
@@ -40,4 +61,4 @@ func deleteServerBackup(c *gin.Context) {
} }
c.Status(http.StatusNoContent) c.Status(http.StatusNoContent)
} }

View File

@@ -34,42 +34,61 @@ func (s *Server) notifyPanelOfBackup(uuid string, ad *backup.ArchiveDetails, suc
return nil return nil
} }
// Performs a server backup and then emits the event over the server websocket. We // Get all of the ignored files for a server based on its .pteroignore file in the root.
// let the actual backup system handle notifying the panel of the status, but that func (s *Server) getServerwideIgnoredFiles() ([]string, error) {
// won't emit a websocket event. var ignored []string
func (s *Server) BackupLocal(b *backup.LocalBackup) error {
f, err := os.Open(path.Join(s.Filesystem.Path(), ".pteroignore"))
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
} else {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
// Only include non-empty lines, for the sake of clarity...
if t := scanner.Text(); t != "" {
ignored = append(ignored, t)
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
}
return ignored, nil
}
// Get the backup files to include when generating it.
func (s *Server) GetIncludedBackupFiles(ignored []string) (*backup.IncludedFiles, error) {
// If no ignored files are present in the request, check for a .pteroignore file in the root // If no ignored files are present in the request, check for a .pteroignore file in the root
// of the server files directory, and use that to generate the backup. // of the server files directory, and use that to generate the backup.
if len(b.IgnoredFiles) == 0 { if len(ignored) == 0 {
f, err := os.Open(path.Join(s.Filesystem.Path(), ".pteroignore")) if i, err := s.getServerwideIgnoredFiles(); err != nil {
if err != nil { zap.S().Warnw("failed to retrieve server ignored files", zap.String("server", s.Uuid), zap.Error(err))
if !os.IsNotExist(err) {
zap.S().Warnw("failed to open .pteroignore file in server directory", zap.String("server", s.Uuid), zap.Error(errors.WithStack(err)))
}
} else { } else {
scanner := bufio.NewScanner(f) ignored = i
for scanner.Scan() {
// Only include non-empty lines, for the sake of clarity...
if t := scanner.Text(); t != "" {
b.IgnoredFiles = append(b.IgnoredFiles, t)
}
}
if err := scanner.Err(); err != nil {
zap.S().Warnw("failed to scan .pteroignore file for lines", zap.String("server", s.Uuid), zap.Error(errors.WithStack(err)))
}
} }
} }
// 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.Filesystem.GetIncludedFiles(s.Filesystem.Path(), b.IgnoredFiles) return s.Filesystem.GetIncludedFiles(s.Filesystem.Path(), ignored)
}
// Performs a server backup and then emits the event over the server websocket. We
// let the actual backup system handle notifying the panel of the status, but that
// won't emit a websocket event.
func (s *Server) Backup(b backup.BackupInterface) error {
// Get the included files based on the root path and the ignored files provided.
inc, err := s.GetIncludedBackupFiles(b.Ignored())
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
if err := b.Backup(inc, s.Filesystem.Path()); err != nil { if err := b.Generate(inc, s.Filesystem.Path()); err != nil {
if notifyError := s.notifyPanelOfBackup(b.Identifier(), &backup.ArchiveDetails{}, false); notifyError != nil { if notifyError := s.notifyPanelOfBackup(b.Identifier(), &backup.ArchiveDetails{}, false); notifyError != nil {
zap.S().Warnw("failed to notify panel of failed backup state", zap.String("backup", b.Uuid), zap.Error(err)) zap.S().Warnw("failed to notify panel of failed backup state", zap.String("backup", b.Identifier()), zap.Error(err))
} }
return errors.WithStack(err) return errors.WithStack(err)
@@ -86,11 +105,11 @@ func (s *Server) BackupLocal(b *backup.LocalBackup) error {
// 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
// the frontend for the server. // the frontend for the server.
s.Events().PublishJson(BackupCompletedEvent+":"+b.Uuid, map[string]interface{}{ s.Events().PublishJson(BackupCompletedEvent+":"+b.Identifier(), map[string]interface{}{
"uuid": b.Uuid, "uuid": b.Identifier(),
"sha256_hash": ad.Checksum, "sha256_hash": ad.Checksum,
"file_size": ad.Size, "file_size": ad.Size,
}) })
return nil return nil
} }

View File

@@ -1,16 +1,58 @@
package backup package backup
import ( import (
"crypto/sha256"
"encoding/hex"
"github.com/pkg/errors"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config"
"go.uber.org/zap"
"io"
"os"
"path"
"sync"
) )
type Backup interface { const (
LocalBackupAdapter = "wings"
S3BackupAdapter = "s3"
)
type ArchiveDetails struct {
Checksum string `json:"checksum"`
Size int64 `json:"size"`
}
// Returns a request object.
func (ad *ArchiveDetails) ToRequest(successful bool) api.BackupRequest {
return api.BackupRequest{
Checksum: ad.Checksum,
Size: ad.Size,
Successful: successful,
}
}
type Backup struct {
// The UUID of this backup object. This must line up with a backup from
// the panel instance.
Uuid string `json:"uuid"`
// An array of files to ignore when generating this backup. This should be
// compatible with a standard .gitignore structure.
IgnoredFiles []string `json:"ignored_files"`
}
// noinspection GoNameStartsWithPackageName
type BackupInterface interface {
// Returns the UUID of this backup as tracked by the panel instance. // Returns the UUID of this backup as tracked by the panel instance.
Identifier() string Identifier() string
// Generates a backup in whatever the configured source for the specific // Generates a backup in whatever the configured source for the specific
// implementation is. // implementation is.
Backup(*IncludedFiles, string) error Generate(*IncludedFiles, string) error
// Returns the ignored files for this backup instance.
Ignored() []string
// Returns a SHA256 checksum for the generated backup. // Returns a SHA256 checksum for the generated backup.
Checksum() ([]byte, error) Checksum() ([]byte, error)
@@ -25,18 +67,85 @@ type Backup interface {
// Returns details about the archive. // Returns details about the archive.
Details() *ArchiveDetails Details() *ArchiveDetails
// Removes a backup file.
Remove() error
} }
type ArchiveDetails struct { func (b *Backup) Identifier() string {
Checksum string `json:"checksum"` return b.Uuid
Size int64 `json:"size"`
} }
// Returns a request object. // Returns the path for this specific backup.
func (ad *ArchiveDetails) ToRequest(successful bool) api.BackupRequest { func (b *Backup) Path() string {
return api.BackupRequest{ return path.Join(config.Get().System.BackupDirectory, b.Identifier()+".tar.gz")
Checksum: ad.Checksum, }
Size: ad.Size,
Successful: successful, // Return the size of the generated backup.
func (b *Backup) Size() (int64, error) {
st, err := os.Stat(b.Path())
if err != nil {
return 0, errors.WithStack(err)
} }
return st.Size(), nil
}
// Returns the SHA256 checksum of a backup.
func (b *Backup) Checksum() ([]byte, error) {
h := sha256.New()
f, err := os.Open(b.Path())
if err != nil {
return []byte{}, errors.WithStack(err)
}
defer f.Close()
if _, err := io.Copy(h, f); err != nil {
return []byte{}, errors.WithStack(err)
}
return h.Sum(nil), nil
}
// Returns details of the archive by utilizing two go-routines to get the checksum and
// the size of the archive.
func (b *Backup) Details() *ArchiveDetails {
wg := sync.WaitGroup{}
wg.Add(2)
var checksum string
// Calculate the checksum for the file.
go func() {
defer wg.Done()
resp, err := b.Checksum()
if err != nil {
zap.S().Errorw("failed to calculate checksum for backup", zap.String("backup", b.Uuid), zap.Error(err))
}
checksum = hex.EncodeToString(resp)
}()
var sz int64
go func() {
defer wg.Done()
if s, err := b.Size(); err != nil {
return
} else {
sz = s
}
}()
wg.Wait()
return &ArchiveDetails{
Checksum: checksum,
Size: sz,
}
}
func (b *Backup) Ignored() []string {
return b.IgnoredFiles
} }

View File

@@ -2,35 +2,24 @@ package backup
import ( import (
"context" "context"
"crypto/sha256"
"encoding/hex"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/pterodactyl/wings/config"
"go.uber.org/zap"
"io"
"os" "os"
"path"
"sync"
) )
type LocalBackup struct { type LocalBackup struct {
// The UUID of this backup object. This must line up with a backup from Backup
// the panel instance.
Uuid string `json:"uuid"`
// An array of files to ignore when generating this backup. This should be
// compatible with a standard .gitignore structure.
IgnoredFiles []string `json:"ignored_files"`
} }
var _ Backup = (*LocalBackup)(nil) var _ BackupInterface = (*LocalBackup)(nil)
// Locates the backup for a server and returns the local path. This will obviously only // Locates the backup for a server and returns the local path. This will obviously only
// work if the backup was created as a local backup. // work if the backup was created as a local backup.
func LocateLocal(uuid string) (*LocalBackup, os.FileInfo, error) { func LocateLocal(uuid string) (*LocalBackup, os.FileInfo, error) {
b := &LocalBackup{ b := &LocalBackup{
Uuid: uuid, Backup{
IgnoredFiles: nil, Uuid: uuid,
IgnoredFiles: nil,
},
} }
st, err := os.Stat(b.Path()) st, err := os.Stat(b.Path())
@@ -45,32 +34,6 @@ func LocateLocal(uuid string) (*LocalBackup, os.FileInfo, error) {
return b, st, nil return b, st, nil
} }
func (b *LocalBackup) Identifier() string {
return b.Uuid
}
// Returns the path for this specific backup.
func (b *LocalBackup) Path() string {
return path.Join(config.Get().System.BackupDirectory, b.Uuid+".tar.gz")
}
// Returns the SHA256 checksum of a backup.
func (b *LocalBackup) Checksum() ([]byte, error) {
h := sha256.New()
f, err := os.Open(b.Path())
if err != nil {
return []byte{}, errors.WithStack(err)
}
defer f.Close()
if _, err := io.Copy(h, f); err != nil {
return []byte{}, errors.WithStack(err)
}
return h.Sum(nil), nil
}
// Removes a backup from the system. // Removes a backup from the system.
func (b *LocalBackup) Remove() error { func (b *LocalBackup) Remove() error {
return os.Remove(b.Path()) return os.Remove(b.Path())
@@ -78,7 +41,7 @@ func (b *LocalBackup) Remove() error {
// Generates a backup of the selected files and pushes it to the defined location // Generates a backup of the selected files and pushes it to the defined location
// for this instance. // for this instance.
func (b *LocalBackup) Backup(included *IncludedFiles, prefix string) error { func (b *LocalBackup) Generate(included *IncludedFiles, prefix string) error {
a := &Archive{ a := &Archive{
TrimPrefix: prefix, TrimPrefix: prefix,
Files: included, Files: included,
@@ -88,67 +51,3 @@ func (b *LocalBackup) Backup(included *IncludedFiles, prefix string) error {
return err return err
} }
// Return the size of the generated backup.
func (b *LocalBackup) Size() (int64, error) {
st, err := os.Stat(b.Path())
if err != nil {
return 0, errors.WithStack(err)
}
return st.Size(), nil
}
// Returns details of the archive by utilizing two go-routines to get the checksum and
// the size of the archive.
func (b *LocalBackup) Details() *ArchiveDetails {
wg := sync.WaitGroup{}
wg.Add(2)
var checksum string
// Calculate the checksum for the file.
go func() {
defer wg.Done()
resp, err := b.Checksum()
if err != nil {
zap.S().Errorw("failed to calculate checksum for backup", zap.String("backup", b.Uuid), zap.Error(err))
}
checksum = hex.EncodeToString(resp)
}()
var sz int64
go func() {
defer wg.Done()
st, err := os.Stat(b.Path())
if err != nil {
return
}
sz = st.Size()
}()
wg.Wait()
return &ArchiveDetails{
Checksum: checksum,
Size: sz,
}
}
// Ensures that the local backup destination for files exists.
func (b *LocalBackup) ensureLocalBackupLocation() error {
d := config.Get().System.BackupDirectory
if _, err := os.Stat(d); err != nil {
if !os.IsNotExist(err) {
return errors.WithStack(err)
}
return os.MkdirAll(d, 0700)
}
return nil
}

View File

@@ -0,0 +1,46 @@
package backup
import (
"fmt"
"github.com/pkg/errors"
)
type Request struct {
Adapter string `json:"adapter"`
Uuid string `json:"uuid"`
IgnoredFiles []string `json:"ignored_files"`
PresignedUrl string `json:"presigned_url"`
}
// Generates a new local backup struct.
func (r *Request) NewLocalBackup() (*LocalBackup, error) {
if r.Adapter != LocalBackupAdapter {
return nil, errors.New(fmt.Sprintf("cannot create local backup using [%s] adapter", r.Adapter))
}
return &LocalBackup{
Backup{
Uuid: r.Uuid,
IgnoredFiles: r.IgnoredFiles,
},
}, nil
}
// Generates a new S3 backup struct.
func (r *Request) NewS3Backup() (*S3Backup, error) {
if r.Adapter != S3BackupAdapter {
return nil, errors.New(fmt.Sprintf("cannot create s3 backup using [%s] adapter", r.Adapter))
}
if len(r.PresignedUrl) == 0 {
return nil, errors.New("a valid presigned S3 upload URL must be provided to use the [s3] adapter")
}
return &S3Backup{
Backup: Backup{
Uuid: r.Uuid,
IgnoredFiles: r.IgnoredFiles,
},
PresignedUrl: r.PresignedUrl,
}, nil
}

View File

@@ -1,37 +1,84 @@
package backup package backup
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
)
type S3Backup struct { type S3Backup struct {
// The UUID of this backup object. This must line up with a backup from Backup
// the panel instance.
Uuid string
// An array of files to ignore when generating this backup. This should be // The pre-signed upload endpoint for the generated backup. This must be
// compatible with a standard .gitignore structure. // provided otherwise this request will fail. This allows us to keep all
IgnoredFiles []string // of the keys off the daemon instances and the panel can handle generating
// the credentials for us.
PresignedUrl string
} }
var _ Backup = (*S3Backup)(nil) var _ BackupInterface = (*S3Backup)(nil)
func (s *S3Backup) Identifier() string { func (s *S3Backup) Generate(included *IncludedFiles, prefix string) error {
return s.Uuid defer s.Remove()
a := &Archive{
TrimPrefix: prefix,
Files: included,
}
if err := a.Create(s.Path(), context.Background()); err != nil {
return err
}
fmt.Println(s.PresignedUrl)
r, err := http.NewRequest(http.MethodPut, s.PresignedUrl, nil)
if err != nil {
return err
}
if sz, err := s.Size(); err != nil {
return err
} else {
r.ContentLength = sz
r.Header.Add("Content-Length", strconv.Itoa(int(sz)))
r.Header.Add("Content-Type", "application/x-gzip")
}
var rc io.ReadCloser
if f, err := os.Open(s.Path()); err != nil {
return err
} else {
rc = f
}
defer rc.Close()
r.Body = rc
resp, err := http.DefaultClient.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
io.Copy(os.Stdout, resp.Body)
return fmt.Errorf("failed to put S3 object, %d:%s", resp.StatusCode, resp.Status)
}
return nil
} }
func (s *S3Backup) Backup(included *IncludedFiles, prefix string) error { // Removes a backup from the system.
panic("implement me") func (s *S3Backup) Remove() error {
} return os.Remove(s.Path())
func (s *S3Backup) Checksum() ([]byte, error) {
return []byte(""), nil
}
func (s *S3Backup) Size() (int64, error) {
return 0, nil
}
func (s *S3Backup) Path() string {
return ""
} }
func (s *S3Backup) Details() *ArchiveDetails { func (s *S3Backup) Details() *ArchiveDetails {
return &ArchiveDetails{} return &ArchiveDetails{
Checksum: "checksum",
Size: 1024,
}
} }

View File

@@ -23,8 +23,9 @@ type Event struct {
} }
type EventBus struct { type EventBus struct {
sync.RWMutex
subscribers map[string][]chan Event subscribers map[string][]chan Event
mu sync.Mutex
} }
// Returns the server's emitter instance. // Returns the server's emitter instance.
@@ -40,8 +41,8 @@ func (s *Server) Events() *EventBus {
// Publish data to a given topic. // Publish data to a given topic.
func (e *EventBus) Publish(topic string, data string) { func (e *EventBus) Publish(topic string, data string) {
e.mu.Lock() e.RLock()
defer e.mu.Unlock() defer e.RUnlock()
t := topic t := topic
// Some of our topics for the socket support passing a more specific namespace, // Some of our topics for the socket support passing a more specific namespace,
@@ -79,8 +80,8 @@ func (e *EventBus) PublishJson(topic string, data interface{}) error {
// Subscribe to an emitter topic using a channel. // Subscribe to an emitter topic using a channel.
func (e *EventBus) Subscribe(topic string, ch chan Event) { func (e *EventBus) Subscribe(topic string, ch chan Event) {
e.mu.Lock() e.Lock()
defer e.mu.Unlock() defer e.Unlock()
if p, ok := e.subscribers[topic]; ok { if p, ok := e.subscribers[topic]; ok {
e.subscribers[topic] = append(p, ch) e.subscribers[topic] = append(p, ch)
@@ -91,8 +92,8 @@ func (e *EventBus) Subscribe(topic string, ch chan Event) {
// Unsubscribe a channel from a topic. // Unsubscribe a channel from a topic.
func (e *EventBus) Unsubscribe(topic string, ch chan Event) { func (e *EventBus) Unsubscribe(topic string, ch chan Event) {
e.mu.Lock() e.Lock()
defer e.mu.Unlock() defer e.Unlock()
if _, ok := e.subscribers[topic]; ok { if _, ok := e.subscribers[topic]; ok {
for i := range e.subscribers[topic] { for i := range e.subscribers[topic] {
@@ -102,3 +103,18 @@ func (e *EventBus) Unsubscribe(topic string, ch chan Event) {
} }
} }
} }
// Removes all of the event listeners for the server. This is used when a server
// is being deleted to avoid a bunch of de-reference errors cropping up. Obviously
// should also check elsewhere and handle a server reference going nil, but this
// won't hurt.
func (e *EventBus) UnsubscribeAll() {
e.Lock()
defer e.Unlock()
// Loop over all of the subscribers and just remove all of the events
// for them.
for t := range e.subscribers {
e.subscribers[t] = make([]chan Event, 0)
}
}

View File

@@ -15,6 +15,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"sync" "sync"
) )
@@ -131,6 +132,12 @@ func (ip *InstallationProcess) Run() error {
// Writes the installation script to a temporary file on the host machine so that it // Writes the installation script to a temporary file on the host machine so that it
// can be properly mounted into the installation container and then executed. // can be properly mounted into the installation container and then executed.
func (ip *InstallationProcess) writeScriptToDisk() (string, error) { func (ip *InstallationProcess) writeScriptToDisk() (string, error) {
// 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.
if err := os.MkdirAll(path.Join(os.TempDir(), "pterodactyl/"), 0700); err != nil {
return "", errors.WithStack(err)
}
d, err := ioutil.TempDir("", "pterodactyl/") d, err := ioutil.TempDir("", "pterodactyl/")
if err != nil { if err != nil {
return "", errors.WithStack(err) return "", errors.WithStack(err)

View File

@@ -2,5 +2,5 @@ package system
const ( const (
// The current version of this software. // The current version of this software.
Version = "0.0.1" Version = "1.0.0-beta.3"
) )