Merge pull request #5 from pterodactyl/feature/docker-environment
Feature/docker environment
This commit is contained in:
		
						commit
						4ef7bef4fb
					
				| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
echo "Provisioning development environment for Pterodactyl go daemon."
 | 
			
		||||
cp /home/ubuntu/go/github.com/pterodactyl/wings.go/.dev/vagrant/motd.txt /etc/motd
 | 
			
		||||
cp /home/vagrant/go/github.com/pterodactyl/wings.go/.dev/vagrant/motd.txt /etc/motd
 | 
			
		||||
 | 
			
		||||
chown -R ubuntu:ubuntu /home/ubuntu/go
 | 
			
		||||
chown -R ubuntu:ubuntu /srv
 | 
			
		||||
chown -R vagrant:vagrant /home/vagrant/go
 | 
			
		||||
chown -R vagrant:vagrant /srv
 | 
			
		||||
 | 
			
		||||
echo "Update apt repositories"
 | 
			
		||||
sudo add-apt-repository ppa:longsleep/golang-backports
 | 
			
		||||
| 
						 | 
				
			
			@ -13,24 +13,28 @@ apt-get update > /dev/null
 | 
			
		|||
echo "Install docker"
 | 
			
		||||
curl -sSL https://get.docker.com/ | sh
 | 
			
		||||
systemctl enable docker
 | 
			
		||||
usermod -aG docker ubuntu
 | 
			
		||||
usermod -aG docker vagrant
 | 
			
		||||
 | 
			
		||||
echo "Install go"
 | 
			
		||||
apt-get install -y golang-go
 | 
			
		||||
echo "export GOPATH=/home/ubuntu/go" >> /home/ubuntu/.profile
 | 
			
		||||
echo "export GOPATH=/home/vagrant/go" >> /home/vagrant/.profile
 | 
			
		||||
export GOPATH=/go
 | 
			
		||||
echo 'export PATH=$PATH:$GOPATH/bin' >> /home/ubuntu/.profile
 | 
			
		||||
echo 'export PATH=$PATH:$GOPATH/bin' >> /home/vagrant/.profile
 | 
			
		||||
 | 
			
		||||
echo "Install go dep"
 | 
			
		||||
sudo -H -u ubuntu bash -c 'go get -u github.com/golang/dep/cmd/dep'
 | 
			
		||||
sudo -H -u vagrant bash -c 'go get -u github.com/golang/dep/cmd/dep'
 | 
			
		||||
 | 
			
		||||
echo "Install delve for debugging"
 | 
			
		||||
sudo -H -u ubuntu bash -c 'go get -u github.com/derekparker/delve/cmd/dlv'
 | 
			
		||||
sudo -H -u vagrant bash -c 'go get -u github.com/derekparker/delve/cmd/dlv'
 | 
			
		||||
 | 
			
		||||
echo "Install additional dependencies"
 | 
			
		||||
apt-get -y install mercurial #tar unzip make gcc g++ python > /dev/null
 | 
			
		||||
 | 
			
		||||
echo "Install ctop for fancy container monitoring"
 | 
			
		||||
wget https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-linux-amd64 -O /usr/local/bin/ctop
 | 
			
		||||
chmod +x /usr/local/bin/ctop
 | 
			
		||||
 | 
			
		||||
echo "   ------------"
 | 
			
		||||
echo "Gopath is /home/ubuntu/go"
 | 
			
		||||
echo "The project is mounted to /home/ubuntu/go/src/github.com/pterodactyl/wings.go"
 | 
			
		||||
echo "Gopath is /home/vagrant/go"
 | 
			
		||||
echo "The project is mounted to /home/vagrant/go/src/github.com/pterodactyl/wings"
 | 
			
		||||
echo "Provisioning is completed."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -39,6 +39,8 @@ wings.exe
 | 
			
		|||
# IDE/Editor files (VS Code)
 | 
			
		||||
/.vscode
 | 
			
		||||
 | 
			
		||||
# test files
 | 
			
		||||
test_*/
 | 
			
		||||
 | 
			
		||||
# Keep all gitkeep files (This needs to stay at the bottom)
 | 
			
		||||
!.gitkeep
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								.travis.yml
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -6,23 +6,17 @@ go:
 | 
			
		|||
services:
 | 
			
		||||
  - docker
 | 
			
		||||
 | 
			
		||||
addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    sources:
 | 
			
		||||
      - sourceline: 'ppa:masterminds/glide'
 | 
			
		||||
    packages:
 | 
			
		||||
      - glide
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
- mkdir -p $GOPATH/bin
 | 
			
		||||
 | 
			
		||||
# Install other tools
 | 
			
		||||
# Install used tools
 | 
			
		||||
- go get github.com/golang/dep/cmd/dep
 | 
			
		||||
- go get github.com/mitchellh/gox
 | 
			
		||||
- go get github.com/haya14busa/goverage
 | 
			
		||||
- go get github.com/schrej/godacov
 | 
			
		||||
 | 
			
		||||
# Install project dependencies with glide
 | 
			
		||||
- glide install
 | 
			
		||||
# Install project dependencies with dep
 | 
			
		||||
- dep ensure
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
- make cross-build
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										156
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										156
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1,15 +1,6 @@
 | 
			
		|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/Azure/go-ansiterm"
 | 
			
		||||
  packages = [
 | 
			
		||||
    ".",
 | 
			
		||||
    "winterm"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/Microsoft/go-winio"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
| 
						 | 
				
			
			@ -17,22 +8,10 @@
 | 
			
		|||
  version = "v0.4.7"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/Nvveen/Gotty"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/StackExchange/wmi"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/containerd/continuity"
 | 
			
		||||
  packages = ["pathdriver"]
 | 
			
		||||
  revision = "d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371"
 | 
			
		||||
  version = "1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/davecgh/go-spew"
 | 
			
		||||
| 
						 | 
				
			
			@ -40,40 +19,45 @@
 | 
			
		|||
  revision = "346938d642f2ec3594ed81d874461961cd0faa76"
 | 
			
		||||
  version = "v1.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/docker/distribution"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "digestset",
 | 
			
		||||
    "reference"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "83389a148052d74ac602f5f1d62f86ff2f3c4aa5"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/docker/docker"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "api",
 | 
			
		||||
    "api/types",
 | 
			
		||||
    "api/types/blkiodev",
 | 
			
		||||
    "api/types/container",
 | 
			
		||||
    "api/types/events",
 | 
			
		||||
    "api/types/filters",
 | 
			
		||||
    "api/types/image",
 | 
			
		||||
    "api/types/mount",
 | 
			
		||||
    "api/types/network",
 | 
			
		||||
    "api/types/registry",
 | 
			
		||||
    "api/types/strslice",
 | 
			
		||||
    "api/types/swarm",
 | 
			
		||||
    "api/types/swarm/runtime",
 | 
			
		||||
    "api/types/time",
 | 
			
		||||
    "api/types/versions",
 | 
			
		||||
    "opts",
 | 
			
		||||
    "pkg/archive",
 | 
			
		||||
    "pkg/fileutils",
 | 
			
		||||
    "pkg/homedir",
 | 
			
		||||
    "pkg/idtools",
 | 
			
		||||
    "pkg/ioutils",
 | 
			
		||||
    "pkg/jsonmessage",
 | 
			
		||||
    "pkg/longpath",
 | 
			
		||||
    "pkg/mount",
 | 
			
		||||
    "pkg/pools",
 | 
			
		||||
    "pkg/stdcopy",
 | 
			
		||||
    "pkg/system",
 | 
			
		||||
    "pkg/term",
 | 
			
		||||
    "pkg/term/windows"
 | 
			
		||||
    "api/types/volume",
 | 
			
		||||
    "client"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "fe8aac6f5ae413a967adb0adad0b54abdfb825c4"
 | 
			
		||||
  revision = "e3831a62a3052472d7252049bc59835d5d7dc8bd"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/docker/go-connections"
 | 
			
		||||
  packages = ["nat"]
 | 
			
		||||
  packages = [
 | 
			
		||||
    "nat",
 | 
			
		||||
    "sockets",
 | 
			
		||||
    "tlsconfig"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d"
 | 
			
		||||
  version = "v0.3.0"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,12 +73,6 @@
 | 
			
		|||
  revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
 | 
			
		||||
  version = "v1.4.7"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/fsouza/go-dockerclient"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "2ff310040c161b75fa19fb9b287a90a6e03c0012"
 | 
			
		||||
  version = "1.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/gin-contrib/sse"
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +110,18 @@
 | 
			
		|||
  revision = "925541529c1fa6821df4e44ce2723319eb2be768"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/google/jsonapi"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "46d3ced0434461be12e555852e2f1a9ed382e139"
 | 
			
		||||
  version = "1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/gorilla/websocket"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
 | 
			
		||||
  version = "v1.2.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/hashicorp/hcl"
 | 
			
		||||
| 
						 | 
				
			
			@ -139,6 +129,7 @@
 | 
			
		|||
    ".",
 | 
			
		||||
    "hcl/ast",
 | 
			
		||||
    "hcl/parser",
 | 
			
		||||
    "hcl/printer",
 | 
			
		||||
    "hcl/scanner",
 | 
			
		||||
    "hcl/strconv",
 | 
			
		||||
    "hcl/token",
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +137,7 @@
 | 
			
		|||
    "json/scanner",
 | 
			
		||||
    "json/token"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
 | 
			
		||||
  revision = "f40e974e75af4e271d97ce0fc917af5898ae7bda"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/inconshreveable/mousetrap"
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +145,18 @@
 | 
			
		|||
  revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
 | 
			
		||||
  version = "v1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/lestrrat-go/file-rotatelogs"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "9df8b44f21785240553882138c5df2e9cc1db910"
 | 
			
		||||
  version = "v2.1.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/lestrrat/go-strftime"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "ba3bf9c1d0421aa146564a632931730344f1f9f1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/magiconair/properties"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +173,7 @@
 | 
			
		|||
  branch = "master"
 | 
			
		||||
  name = "github.com/mitchellh/mapstructure"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "a4e142e9c047c904fa2f1e144d9a84e6133024bc"
 | 
			
		||||
  revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/opencontainers/go-digest"
 | 
			
		||||
| 
						 | 
				
			
			@ -187,15 +190,6 @@
 | 
			
		|||
  revision = "d60099175f88c47cd379c4738d158884749ed235"
 | 
			
		||||
  version = "v1.0.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/opencontainers/runc"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "libcontainer/system",
 | 
			
		||||
    "libcontainer/user"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "baf6536d6259209c3edfa2b22237af82942d3dfa"
 | 
			
		||||
  version = "v0.1.1"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/pelletier/go-toml"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
| 
						 | 
				
			
			@ -214,29 +208,26 @@
 | 
			
		|||
  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/rifflock/lfshook"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "bf539943797a1f34c1f502d07de419b5238ae6c6"
 | 
			
		||||
  version = "v2.3"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/shirou/gopsutil"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "cpu",
 | 
			
		||||
    "host",
 | 
			
		||||
    "internal/common",
 | 
			
		||||
    "mem",
 | 
			
		||||
    "net",
 | 
			
		||||
    "process"
 | 
			
		||||
    "internal/common"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "543a05cce094293c7747322720256bee15d88a12"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "github.com/shirou/w32"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b"
 | 
			
		||||
  revision = "5776ff9c7c5d063d574ef53d740f75c68b448e53"
 | 
			
		||||
  version = "v2.18.02"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/sirupsen/logrus"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
 | 
			
		||||
  version = "v1.0.4"
 | 
			
		||||
  revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
 | 
			
		||||
  version = "v1.0.5"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/spf13/afero"
 | 
			
		||||
| 
						 | 
				
			
			@ -256,8 +247,8 @@
 | 
			
		|||
[[projects]]
 | 
			
		||||
  name = "github.com/spf13/cobra"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
 | 
			
		||||
  version = "v0.0.1"
 | 
			
		||||
  revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
 | 
			
		||||
  version = "v0.0.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
| 
						 | 
				
			
			@ -274,8 +265,8 @@
 | 
			
		|||
[[projects]]
 | 
			
		||||
  name = "github.com/spf13/viper"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
 | 
			
		||||
  version = "v1.0.0"
 | 
			
		||||
  revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
 | 
			
		||||
  version = "v1.0.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "github.com/stretchr/testify"
 | 
			
		||||
| 
						 | 
				
			
			@ -293,16 +284,17 @@
 | 
			
		|||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/crypto"
 | 
			
		||||
  packages = ["ssh/terminal"]
 | 
			
		||||
  revision = "432090b8f568c018896cd8a0fb0345872bbac6ce"
 | 
			
		||||
  revision = "12892e8c234f4fe6f6803f052061de9057903bb2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/net"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "context",
 | 
			
		||||
    "context/ctxhttp"
 | 
			
		||||
    "context/ctxhttp",
 | 
			
		||||
    "proxy"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb"
 | 
			
		||||
  revision = "b68f30494add4df6bd8ef5e82803f308e7f7c59c"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
| 
						 | 
				
			
			@ -311,10 +303,9 @@
 | 
			
		|||
    "unix",
 | 
			
		||||
    "windows"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
 | 
			
		||||
  revision = "378d26f46672a356c46195c28f61bdb4c0a781dd"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "master"
 | 
			
		||||
  name = "golang.org/x/text"
 | 
			
		||||
  packages = [
 | 
			
		||||
    "internal/gen",
 | 
			
		||||
| 
						 | 
				
			
			@ -324,7 +315,8 @@
 | 
			
		|||
    "unicode/cldr",
 | 
			
		||||
    "unicode/norm"
 | 
			
		||||
  ]
 | 
			
		||||
  revision = "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1"
 | 
			
		||||
  revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
 | 
			
		||||
  version = "v0.3.0"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  name = "gopkg.in/go-playground/validator.v8"
 | 
			
		||||
| 
						 | 
				
			
			@ -333,14 +325,14 @@
 | 
			
		|||
  version = "v8.18.2"
 | 
			
		||||
 | 
			
		||||
[[projects]]
 | 
			
		||||
  branch = "v2"
 | 
			
		||||
  name = "gopkg.in/yaml.v2"
 | 
			
		||||
  packages = ["."]
 | 
			
		||||
  revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
 | 
			
		||||
  revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
 | 
			
		||||
  version = "v2.2.1"
 | 
			
		||||
 | 
			
		||||
[solve-meta]
 | 
			
		||||
  analyzer-name = "dep"
 | 
			
		||||
  analyzer-version = 1
 | 
			
		||||
  inputs-digest = "3f1bdf5882e27f292b13d4b95c9f51eb1a7609af61ccda0fae50a806b0a2ba4f"
 | 
			
		||||
  inputs-digest = "8e17495db05ff2c85d228a20157c41c223e428d28217e14f89ab7b764a8706dd"
 | 
			
		||||
  solver-name = "gps-cdcl"
 | 
			
		||||
  solver-version = 1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										41
									
								
								Gopkg.toml
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								Gopkg.toml
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
# Gopkg.toml example
 | 
			
		||||
#
 | 
			
		||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
 | 
			
		||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
 | 
			
		||||
# for detailed Gopkg.toml documentation.
 | 
			
		||||
#
 | 
			
		||||
# required = ["github.com/user/thing/cmd/thing"]
 | 
			
		||||
| 
						 | 
				
			
			@ -25,33 +25,38 @@
 | 
			
		|||
#   unused-packages = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[[constraint]]
 | 
			
		||||
#  branch = "develop"
 | 
			
		||||
#  name = "github.com/pterodactyl/wings"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/google/jsonapi"
 | 
			
		||||
  version = "~1.0.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/gorilla/websocket"
 | 
			
		||||
  version = "~1.2.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/shirou/gopsutil"
 | 
			
		||||
  version = "2.17.6"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/sirupsen/logrus"
 | 
			
		||||
  version = "~1.0.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/gin-gonic/gin"
 | 
			
		||||
  version = "1.2.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/lestrrat/go-file-rotatelogs"
 | 
			
		||||
  name = "github.com/lestrrat-go/file-rotatelogs"
 | 
			
		||||
  version = "2.1.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/rifflock/lfshook"
 | 
			
		||||
  version = "2.2.0"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/shirou/gopsutil"
 | 
			
		||||
  revision = "543a05cce094293c7747322720256bee15d88a12"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/sirupsen/logrus"
 | 
			
		||||
  version = "1.0.4"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/spf13/cobra"
 | 
			
		||||
  version = "0.0.1"
 | 
			
		||||
  version = "0.0.2"
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/spf13/viper"
 | 
			
		||||
| 
						 | 
				
			
			@ -64,3 +69,11 @@
 | 
			
		|||
[prune]
 | 
			
		||||
  go-tests = true
 | 
			
		||||
  unused-packages = true
 | 
			
		||||
 | 
			
		||||
[[constraint]]
 | 
			
		||||
  name = "github.com/docker/docker"
 | 
			
		||||
  revision = "e3831a62a3052472d7252049bc59835d5d7dc8bd"
 | 
			
		||||
 | 
			
		||||
[[override]]
 | 
			
		||||
  name = "github.com/docker/distribution"
 | 
			
		||||
  branch = "master"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
Vagrant.configure("2") do |cfg|
 | 
			
		||||
    cfg.vm.box = "ubuntu/xenial64"
 | 
			
		||||
    cfg.vm.box = "bento/ubuntu-16.04"
 | 
			
		||||
 | 
			
		||||
    cfg.vm.synced_folder "./", "/home/ubuntu/go/src/github.com/pterodactyl/wings"
 | 
			
		||||
    cfg.vm.synced_folder "./", "/home/vagrant/go/src/github.com/pterodactyl/wings"
 | 
			
		||||
 | 
			
		||||
    cfg.vm.provision :shell, path: ".dev/vagrant/provision.sh"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								api/api.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								api/api.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -2,7 +2,6 @@ package api
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
| 
						 | 
				
			
			@ -41,8 +40,7 @@ func (api *InternalAPI) Listen() {
 | 
			
		|||
	l := fmt.Sprintf("%s:%d", viper.GetString(config.APIHost), viper.GetInt(config.APIPort))
 | 
			
		||||
	api.router.Run(l)
 | 
			
		||||
 | 
			
		||||
	logrus.Info("Now listening on %s", l)
 | 
			
		||||
	logrus.Fatal(http.ListenAndServe(l, nil))
 | 
			
		||||
	logrus.Info("API Server is now listening on %s", l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register routes for v1 of the API. This API should be fully backwards compatable with
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +51,7 @@ func (api *InternalAPI) Listen() {
 | 
			
		|||
func (api *InternalAPI) register() {
 | 
			
		||||
	v1 := api.router.Group("/api/v1")
 | 
			
		||||
	{
 | 
			
		||||
		v1.GET("/", AuthHandler(""), GetIndex)
 | 
			
		||||
		v1.GET("", AuthHandler(""), GetIndex)
 | 
			
		||||
		//v1.PATCH("/config", AuthHandler("c:config"), PatchConfiguration)
 | 
			
		||||
 | 
			
		||||
		v1.GET("/servers", AuthHandler("c:list"), handleGetServers)
 | 
			
		||||
| 
						 | 
				
			
			@ -61,15 +59,15 @@ func (api *InternalAPI) register() {
 | 
			
		|||
 | 
			
		||||
		v1ServerRoutes := v1.Group("/servers/:server")
 | 
			
		||||
		{
 | 
			
		||||
			v1ServerRoutes.GET("/", AuthHandler("s:get"), handleGetServer)
 | 
			
		||||
			v1ServerRoutes.PATCH("/", AuthHandler("s:config"), handlePatchServer)
 | 
			
		||||
			v1ServerRoutes.DELETE("/", AuthHandler("g:server:delete"), handleDeleteServer)
 | 
			
		||||
			v1ServerRoutes.GET("", AuthHandler("s:get"), handleGetServer)
 | 
			
		||||
			v1ServerRoutes.PATCH("", AuthHandler("s:config"), handlePatchServer)
 | 
			
		||||
			v1ServerRoutes.DELETE("", AuthHandler("g:server:delete"), handleDeleteServer)
 | 
			
		||||
			v1ServerRoutes.POST("/reinstall", AuthHandler("s:install-server"), handlePostServerReinstall)
 | 
			
		||||
			v1ServerRoutes.POST("/rebuild", AuthHandler("g:server:rebuild"), handlePostServerRebuild)
 | 
			
		||||
			v1ServerRoutes.POST("/password", AuthHandler(""), handlePostServerPassword)
 | 
			
		||||
			v1ServerRoutes.POST("/power", AuthHandler("s:power"), handlePostServerPower)
 | 
			
		||||
			v1ServerRoutes.POST("/command", AuthHandler("s:command"), handlePostServerCommand)
 | 
			
		||||
			v1ServerRoutes.GET("/log", AuthHandler("s:console"), handleGetServerLog)
 | 
			
		||||
			v1ServerRoutes.GET("/log", AuthHandler("s:console"), handleGetConsole)
 | 
			
		||||
			v1ServerRoutes.POST("/suspend", AuthHandler(""), handlePostServerSuspend)
 | 
			
		||||
			v1ServerRoutes.POST("/unsuspend", AuthHandler(""), handlePostServerUnsuspend)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										33
									
								
								api/auth.go
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								api/auth.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -2,8 +2,12 @@ package api
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
    "strings"
 | 
			
		||||
 | 
			
		||||
    "strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/google/jsonapi"
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
	"github.com/pterodactyl/wings/control"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
| 
						 | 
				
			
			@ -62,15 +66,25 @@ func (a *authorizationManager) HasPermission(permission string) bool {
 | 
			
		|||
 | 
			
		||||
// AuthHandler returns a HandlerFunc that checks request authentication
 | 
			
		||||
// permission is a permission string describing the required permission to access the route
 | 
			
		||||
//
 | 
			
		||||
// The AuthHandler looks for an access token header (defined in accessTokenHeader)
 | 
			
		||||
// or a `token` request parameter
 | 
			
		||||
func AuthHandler(permission string) gin.HandlerFunc {
 | 
			
		||||
	return func(c *gin.Context) {
 | 
			
		||||
		requestToken := c.Request.Header.Get(accessTokenHeader)
 | 
			
		||||
		if requestToken != "" && strings.HasPrefix(requestToken, "Baerer ") {
 | 
			
		||||
            requestToken = requestToken[7:]
 | 
			
		||||
        } else {
 | 
			
		||||
			requestToken = c.Query("token")
 | 
			
		||||
		}
 | 
			
		||||
		requestServer := c.Param("server")
 | 
			
		||||
		var server control.Server
 | 
			
		||||
 | 
			
		||||
		if requestToken == "" && permission != "" {
 | 
			
		||||
			log.Debug("Token missing in request.")
 | 
			
		||||
			c.JSON(http.StatusBadRequest, responseError{"Missing required " + accessTokenHeader + " header."})
 | 
			
		||||
			sendErrors(c, http.StatusUnauthorized, &jsonapi.ErrorObject{
 | 
			
		||||
				Title:  "Missing required " + accessTokenHeader + " header or token param.",
 | 
			
		||||
				Status: strconv.Itoa(http.StatusUnauthorized),
 | 
			
		||||
			})
 | 
			
		||||
			c.Abort()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +104,7 @@ func AuthHandler(permission string) gin.HandlerFunc {
 | 
			
		|||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.JSON(http.StatusForbidden, responseError{"You do not have permission to perform this action."})
 | 
			
		||||
		sendForbidden(c)
 | 
			
		||||
		c.Abort()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -107,16 +121,3 @@ func GetContextAuthManager(c *gin.Context) AuthorizationManager {
 | 
			
		|||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetContextServer returns a control.Server contained in a gin.Context
 | 
			
		||||
// or null
 | 
			
		||||
func GetContextServer(c *gin.Context) control.Server {
 | 
			
		||||
	server, exists := c.Get(contextVarAuth)
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if server, ok := server.(control.Server); ok {
 | 
			
		||||
		return server
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,6 @@ import (
 | 
			
		|||
	//"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	//"github.com/pterodactyl/wings/constants"
 | 
			
		||||
	//"github.com/shirou/gopsutil/host"
 | 
			
		||||
 | 
			
		||||
	"github.com/shirou/gopsutil/cpu"
 | 
			
		||||
	//"github.com/shirou/gopsutil/host"
 | 
			
		||||
	//"github.com/shirou/gopsutil/mem"
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +125,7 @@ type incomingConfiguration struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// handlePatchConfig handles PATCH /config
 | 
			
		||||
func handlePatchConfig(c *gin.Context) {
 | 
			
		||||
func PatchConfiguration(c *gin.Context) {
 | 
			
		||||
	// reqBody, err := ioutil.ReadAll(c.Request.Body)
 | 
			
		||||
	// if err != nil {
 | 
			
		||||
	// 	log.WithError(err).Error("Failed to read input.")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,25 +3,29 @@ package api
 | 
			
		|||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/google/jsonapi"
 | 
			
		||||
	"github.com/pterodactyl/wings/control"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GET /servers
 | 
			
		||||
// TODO: make jsonapi compliant
 | 
			
		||||
func handleGetServers(c *gin.Context) {
 | 
			
		||||
	servers := control.GetServers()
 | 
			
		||||
	c.JSON(http.StatusOK, servers)
 | 
			
		||||
	sendData(c, servers)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// POST /servers
 | 
			
		||||
// TODO: make jsonapi compliant
 | 
			
		||||
func handlePostServers(c *gin.Context) {
 | 
			
		||||
	server := control.ServerStruct{}
 | 
			
		||||
	if err := c.BindJSON(&server); err != nil {
 | 
			
		||||
		log.WithField("server", server).WithError(err).Error("Failed to parse server request.")
 | 
			
		||||
		c.Status(http.StatusBadRequest)
 | 
			
		||||
		sendErrors(c, http.StatusBadRequest, &jsonapi.ErrorObject{
 | 
			
		||||
			Status: strconv.Itoa(http.StatusBadRequest),
 | 
			
		||||
			Title:  "The passed server object is invalid.",
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var srv control.Server
 | 
			
		||||
| 
						 | 
				
			
			@ -30,10 +34,14 @@ func handlePostServers(c *gin.Context) {
 | 
			
		|||
		if _, ok := err.(control.ErrServerExists); ok {
 | 
			
		||||
			log.WithError(err).Error("Cannot create server, it already exists.")
 | 
			
		||||
			c.Status(http.StatusBadRequest)
 | 
			
		||||
			sendErrors(c, http.StatusConflict, &jsonapi.ErrorObject{
 | 
			
		||||
				Status: strconv.Itoa(http.StatusConflict),
 | 
			
		||||
				Title:  "A server with this ID already exists.",
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		log.WithField("server", server).WithError(err).Error("Failed to create server.")
 | 
			
		||||
		c.Status(http.StatusInternalServerError)
 | 
			
		||||
		sendInternalError(c, "Failed to create the server", "")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	go func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,19 +51,22 @@ func handlePostServers(c *gin.Context) {
 | 
			
		|||
		}
 | 
			
		||||
		env.Create()
 | 
			
		||||
	}()
 | 
			
		||||
	c.JSON(http.StatusOK, srv)
 | 
			
		||||
	sendDataStatus(c, http.StatusCreated, srv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GET /servers/:server
 | 
			
		||||
// TODO: make jsonapi compliant
 | 
			
		||||
func handleGetServer(c *gin.Context) {
 | 
			
		||||
	id := c.Param("server")
 | 
			
		||||
	server := control.GetServer(id)
 | 
			
		||||
	if server == nil {
 | 
			
		||||
		c.Status(http.StatusNotFound)
 | 
			
		||||
		sendErrors(c, http.StatusNotFound, &jsonapi.ErrorObject{
 | 
			
		||||
			Code:   strconv.Itoa(http.StatusNotFound),
 | 
			
		||||
			Title:  "Server not found.",
 | 
			
		||||
			Detail: "The requested Server with the id " + id + " couldn't be found.",
 | 
			
		||||
		})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.JSON(http.StatusOK, server)
 | 
			
		||||
	sendData(c, server)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PATCH /servers/:server
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +75,6 @@ func handlePatchServer(c *gin.Context) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// DELETE /servers/:server
 | 
			
		||||
// TODO: make jsonapi compliant
 | 
			
		||||
func handleDeleteServer(c *gin.Context) {
 | 
			
		||||
	id := c.Param("server")
 | 
			
		||||
	server := control.GetServer(id)
 | 
			
		||||
| 
						 | 
				
			
			@ -75,18 +85,21 @@ func handleDeleteServer(c *gin.Context) {
 | 
			
		|||
 | 
			
		||||
	env, err := server.Environment()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", server).Error("Failed to delete server.")
 | 
			
		||||
		sendInternalError(c, "The server could not be deleted.", "")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err := env.Destroy(); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to delete server, the environment couldn't be destroyed.")
 | 
			
		||||
		sendInternalError(c, "The server could not be deleted.", "The server environment couldn't be destroyed.")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := control.DeleteServer(id); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to delete server.")
 | 
			
		||||
		c.Status(http.StatusInternalServerError)
 | 
			
		||||
		sendInternalError(c, "The server could not be deleted.", "")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	c.Status(http.StatusOK)
 | 
			
		||||
	c.Status(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handlePostServerReinstall(c *gin.Context) {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +115,6 @@ func handlePostServerRebuild(c *gin.Context) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// POST /servers/:server/power
 | 
			
		||||
// TODO: make jsonapi compliant
 | 
			
		||||
func handlePostServerPower(c *gin.Context) {
 | 
			
		||||
	server := getServerFromContext(c)
 | 
			
		||||
	if server == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +124,7 @@ func handlePostServerPower(c *gin.Context) {
 | 
			
		|||
 | 
			
		||||
	auth := GetContextAuthManager(c)
 | 
			
		||||
	if auth == nil {
 | 
			
		||||
		c.Status(http.StatusInternalServerError)
 | 
			
		||||
		sendInternalError(c, "An internal error occured.", "")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +132,7 @@ func handlePostServerPower(c *gin.Context) {
 | 
			
		|||
	case "start":
 | 
			
		||||
		{
 | 
			
		||||
			if !auth.HasPermission("s:power:start") {
 | 
			
		||||
				c.Status(http.StatusForbidden)
 | 
			
		||||
				sendForbidden(c)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			server.Start()
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +140,7 @@ func handlePostServerPower(c *gin.Context) {
 | 
			
		|||
	case "stop":
 | 
			
		||||
		{
 | 
			
		||||
			if !auth.HasPermission("s:power:stop") {
 | 
			
		||||
				c.Status(http.StatusForbidden)
 | 
			
		||||
				sendForbidden(c)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			server.Stop()
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +148,7 @@ func handlePostServerPower(c *gin.Context) {
 | 
			
		|||
	case "restart":
 | 
			
		||||
		{
 | 
			
		||||
			if !auth.HasPermission("s:power:restart") {
 | 
			
		||||
				c.Status(http.StatusForbidden)
 | 
			
		||||
				sendForbidden(c)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			server.Restart()
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +156,7 @@ func handlePostServerPower(c *gin.Context) {
 | 
			
		|||
	case "kill":
 | 
			
		||||
		{
 | 
			
		||||
			if !auth.HasPermission("s:power:kill") {
 | 
			
		||||
				c.Status(http.StatusForbidden)
 | 
			
		||||
				sendForbidden(c)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			server.Kill()
 | 
			
		||||
| 
						 | 
				
			
			@ -157,15 +169,16 @@ func handlePostServerPower(c *gin.Context) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// POST /servers/:server/command
 | 
			
		||||
// TODO: make jsonapi compliant
 | 
			
		||||
func handlePostServerCommand(c *gin.Context) {
 | 
			
		||||
	server := getServerFromContext(c)
 | 
			
		||||
	cmd := c.Query("command")
 | 
			
		||||
	server.Exec(cmd)
 | 
			
		||||
	c.Status(204)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleGetServerLog(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
func handleGetConsole(c *gin.Context) {
 | 
			
		||||
	server := getServerFromContext(c)
 | 
			
		||||
	server.Websockets().Upgrade(c.Writer, c.Request)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handlePostServerSuspend(c *gin.Context) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								api/routes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								api/routes.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
package api
 | 
			
		||||
 | 
			
		||||
func (api *InternalAPI) RegisterRoutes() {
 | 
			
		||||
	// Register routes for v1 of the API. This API should be fully backwards compatable with
 | 
			
		||||
	// the existing Nodejs Daemon API.
 | 
			
		||||
	v1 := api.router.Group("/v1")
 | 
			
		||||
	{
 | 
			
		||||
		v1.GET("", AuthHandler(""), GetIndex)
 | 
			
		||||
		v1.PATCH("/config", AuthHandler("c:config"), PatchConfiguration)
 | 
			
		||||
 | 
			
		||||
		v1.GET("/servers", AuthHandler("c:list"), handleGetServers)
 | 
			
		||||
		v1.POST("/servers", AuthHandler("c:create"), handlePostServers)
 | 
			
		||||
 | 
			
		||||
		v1ServerRoutes := v1.Group("/servers/:server")
 | 
			
		||||
		{
 | 
			
		||||
			v1ServerRoutes.GET("", AuthHandler("s:get"), handleGetServer)
 | 
			
		||||
			v1ServerRoutes.PATCH("", AuthHandler("s:config"), handlePatchServer)
 | 
			
		||||
			v1ServerRoutes.DELETE("", AuthHandler("g:server:delete"), handleDeleteServer)
 | 
			
		||||
			v1ServerRoutes.POST("/reinstall", AuthHandler("s:install-server"), handlePostServerReinstall)
 | 
			
		||||
			v1ServerRoutes.POST("/rebuild", AuthHandler("g:server:rebuild"), handlePostServerRebuild)
 | 
			
		||||
			v1ServerRoutes.POST("/password", AuthHandler(""), handlePostServerPassword)
 | 
			
		||||
			v1ServerRoutes.POST("/power", AuthHandler("s:power"), handlePostServerPower)
 | 
			
		||||
			v1ServerRoutes.POST("/command", AuthHandler("s:command"), handlePostServerCommand)
 | 
			
		||||
			v1ServerRoutes.GET("/console", AuthHandler("s:console"), handleGetConsole)
 | 
			
		||||
			v1ServerRoutes.GET("/log", AuthHandler("s:console"), handleGetServerLog)
 | 
			
		||||
			v1ServerRoutes.POST("/suspend", AuthHandler(""), handlePostServerSuspend)
 | 
			
		||||
			v1ServerRoutes.POST("/unsuspend", AuthHandler(""), handlePostServerUnsuspend)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//v1ServerFileRoutes := v1.Group("/servers/:server/files")
 | 
			
		||||
		//{
 | 
			
		||||
		//	v1ServerFileRoutes.GET("/file/:file", AuthHandler("s:files:read"), handleGetFile)
 | 
			
		||||
		//	v1ServerFileRoutes.GET("/stat/:file", AuthHandler("s:files:"), handleGetFileStat)
 | 
			
		||||
		//	v1ServerFileRoutes.GET("/dir/:directory", AuthHandler("s:files:get"), handleGetDirectory)
 | 
			
		||||
		//
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/dir/:directory", AuthHandler("s:files:create"), handlePostFilesFolder)
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/file/:file", AuthHandler("s:files:post"), handlePostFile)
 | 
			
		||||
		//
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/copy/:file", AuthHandler("s:files:copy"), handlePostFileCopy)
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/move/:file", AuthHandler("s:files:move"), handlePostFileMove)
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/rename/:file", AuthHandler("s:files:move"), handlePostFileMove)
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/compress/:file", AuthHandler("s:files:compress"), handlePostFileCompress)
 | 
			
		||||
		//	v1ServerFileRoutes.POST("/decompress/:file", AuthHandler("s:files:decompress"), handlePostFileDecompress)
 | 
			
		||||
		//
 | 
			
		||||
		//	v1ServerFileRoutes.DELETE("/file/:file", AuthHandler("s:files:delete"), handleDeleteFile)
 | 
			
		||||
		//
 | 
			
		||||
		//	v1ServerFileRoutes.GET("/download/:token", handleGetDownloadFile)
 | 
			
		||||
		//}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								api/utils.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								api/utils.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,10 +1,45 @@
 | 
			
		|||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/google/jsonapi"
 | 
			
		||||
	"github.com/pterodactyl/wings/control"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getServerFromContext(context *gin.Context) control.Server {
 | 
			
		||||
	return control.GetServer(context.Param("server"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendErrors(c *gin.Context, s int, err ...*jsonapi.ErrorObject) {
 | 
			
		||||
	c.Status(s)
 | 
			
		||||
	c.Header("Content-Type", "application/json")
 | 
			
		||||
	jsonapi.MarshalErrors(c.Writer, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendInternalError(c *gin.Context, title string, detail string) {
 | 
			
		||||
	sendErrors(c, http.StatusInternalServerError, &jsonapi.ErrorObject{
 | 
			
		||||
		Status: strconv.Itoa(http.StatusInternalServerError),
 | 
			
		||||
		Title:  title,
 | 
			
		||||
		Detail: detail,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendForbidden(c *gin.Context) {
 | 
			
		||||
	sendErrors(c, http.StatusForbidden, &jsonapi.ErrorObject{
 | 
			
		||||
		Title:  "The provided token has insufficient permissions to perform this action.",
 | 
			
		||||
		Status: strconv.Itoa(http.StatusForbidden),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendData(c *gin.Context, payload interface{}) {
 | 
			
		||||
	sendDataStatus(c, http.StatusOK, payload)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendDataStatus(c *gin.Context, status int, payload interface{}) {
 | 
			
		||||
	c.Status(status)
 | 
			
		||||
	c.Header("Content-Type", "application/json")
 | 
			
		||||
	jsonapi.MarshalPayload(c.Writer, payload)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										120
									
								
								api/websockets/collection.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								api/websockets/collection.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
package websockets
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	writeWait = 10 * time.Second
 | 
			
		||||
	pongWait  = 60 * time.Second
 | 
			
		||||
 | 
			
		||||
	pingPeriod = pongWait * 9 / 10
 | 
			
		||||
 | 
			
		||||
	maxMessageSize = 512
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var wsupgrader = websocket.Upgrader{
 | 
			
		||||
	ReadBufferSize:  1024,
 | 
			
		||||
	WriteBufferSize: 1024,
 | 
			
		||||
	CheckOrigin: func(r *http.Request) bool {
 | 
			
		||||
		return true
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type websocketMap map[*Socket]bool
 | 
			
		||||
 | 
			
		||||
type Collection struct {
 | 
			
		||||
	sockets websocketMap
 | 
			
		||||
 | 
			
		||||
	Broadcast chan Message
 | 
			
		||||
 | 
			
		||||
	register   chan *Socket
 | 
			
		||||
	unregister chan *Socket
 | 
			
		||||
	close      chan bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//var _ io.Writer = &Collection{}
 | 
			
		||||
 | 
			
		||||
func NewCollection() *Collection {
 | 
			
		||||
	return &Collection{
 | 
			
		||||
		Broadcast:  make(chan Message),
 | 
			
		||||
		register:   make(chan *Socket),
 | 
			
		||||
		unregister: make(chan *Socket),
 | 
			
		||||
		close:      make(chan bool),
 | 
			
		||||
		sockets:    make(websocketMap),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Collection) Upgrade(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	socket, err := wsupgrader.Upgrade(w, r, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to upgrade to websocket")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	s := &Socket{
 | 
			
		||||
		collection: c,
 | 
			
		||||
		conn:       socket,
 | 
			
		||||
		send:       make(chan []byte, 256),
 | 
			
		||||
	}
 | 
			
		||||
	c.register <- s
 | 
			
		||||
 | 
			
		||||
	go s.readPump()
 | 
			
		||||
	go s.writePump()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Collection) Add(s *Socket) {
 | 
			
		||||
	c.register <- s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Collection) Remove(s *Socket) {
 | 
			
		||||
	c.unregister <- s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Collection) Run() {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		for s := range c.sockets {
 | 
			
		||||
			close(s.send)
 | 
			
		||||
			delete(c.sockets, s)
 | 
			
		||||
		}
 | 
			
		||||
		close(c.register)
 | 
			
		||||
		close(c.unregister)
 | 
			
		||||
		close(c.Broadcast)
 | 
			
		||||
		close(c.close)
 | 
			
		||||
	}()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case s := <-c.register:
 | 
			
		||||
			c.sockets[s] = true
 | 
			
		||||
		case s := <-c.unregister:
 | 
			
		||||
			if _, ok := c.sockets[s]; ok {
 | 
			
		||||
				delete(c.sockets, s)
 | 
			
		||||
				close(s.send)
 | 
			
		||||
			}
 | 
			
		||||
		case m := <-c.Broadcast:
 | 
			
		||||
			b, err := json.Marshal(m)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.WithError(err).Error("Failed to encode websocket message.")
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for s := range c.sockets {
 | 
			
		||||
				select {
 | 
			
		||||
				case s.send <- b:
 | 
			
		||||
				default:
 | 
			
		||||
					close(s.send)
 | 
			
		||||
					delete(c.sockets, s)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case <-c.close:
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Collection) Close() {
 | 
			
		||||
	c.close <- true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								api/websockets/message.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								api/websockets/message.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
package websockets
 | 
			
		||||
 | 
			
		||||
type MessageType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MessageTypeProc    MessageType = "proc"
 | 
			
		||||
	MessageTypeConsole MessageType = "console"
 | 
			
		||||
	MessageTypeStatus  MessageType = "status"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Message is a message that can be sent using a websocket in JSON format
 | 
			
		||||
type Message struct {
 | 
			
		||||
	// Type is the type of a websocket message
 | 
			
		||||
	Type MessageType `json:"type"`
 | 
			
		||||
	// Payload is the payload of the message
 | 
			
		||||
	// The payload needs to support encoding in JSON
 | 
			
		||||
	Payload interface{} `json:"payload"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProcPayload struct {
 | 
			
		||||
	Memory   int   `json:"memory"`
 | 
			
		||||
	CPUCores []int `json:"cpu_cores"`
 | 
			
		||||
	CPUTotal int   `json:"cpu_total"`
 | 
			
		||||
	Disk     int   `json:"disk"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ConsoleSource string
 | 
			
		||||
type ConsoleLevel string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ConsoleSourceWings  ConsoleSource = "wings"
 | 
			
		||||
	ConsoleSourceServer ConsoleSource = "server"
 | 
			
		||||
 | 
			
		||||
	ConsoleLevelPlain ConsoleLevel = "plain"
 | 
			
		||||
	ConsoleLevelInfo  ConsoleLevel = "info"
 | 
			
		||||
	ConsoleLevelWarn  ConsoleLevel = "warn"
 | 
			
		||||
	ConsoleLevelError ConsoleLevel = "error"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ConsolePayload struct {
 | 
			
		||||
	// Source is the source of the console line, either ConsoleSourceWings or ConsoleSourceServer
 | 
			
		||||
	Source ConsoleSource `json:"source"`
 | 
			
		||||
	// Level is the level of the message.
 | 
			
		||||
	// Use one of plain, info, warn or error. If omitted the default is plain.
 | 
			
		||||
	Level ConsoleLevel `json:"level,omitempty"`
 | 
			
		||||
	// Line is the actual line to print to the console.
 | 
			
		||||
	Line string `json:"line"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Collection) Log(l ConsoleLevel, m string) {
 | 
			
		||||
	c.Broadcast <- Message{
 | 
			
		||||
		Type: MessageTypeConsole,
 | 
			
		||||
		Payload: ConsolePayload{
 | 
			
		||||
			Source: ConsoleSourceWings,
 | 
			
		||||
			Level:  l,
 | 
			
		||||
			Line:   m,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										81
									
								
								api/websockets/socket.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								api/websockets/socket.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
package websockets
 | 
			
		||||
 | 
			
		||||
import "github.com/gorilla/websocket"
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Socket struct {
 | 
			
		||||
	collection *Collection
 | 
			
		||||
 | 
			
		||||
	conn *websocket.Conn
 | 
			
		||||
 | 
			
		||||
	send chan []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Socket) readPump() {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		s.collection.unregister <- s
 | 
			
		||||
		s.conn.Close()
 | 
			
		||||
	}()
 | 
			
		||||
	s.conn.SetReadLimit(maxMessageSize)
 | 
			
		||||
	s.conn.SetReadDeadline(time.Now().Add(pongWait))
 | 
			
		||||
	s.conn.SetPongHandler(func(string) error {
 | 
			
		||||
		s.conn.SetReadDeadline(time.Now().Add(pongWait))
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	for {
 | 
			
		||||
		t, m, err := s.conn.ReadMessage()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
 | 
			
		||||
				log.WithError(err).Debug("Websocket closed unexpectedly.")
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// Handle websocket responses somehow
 | 
			
		||||
		if t == websocket.TextMessage {
 | 
			
		||||
			log.Debug("Received websocket message: " + string(m))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Socket) writePump() {
 | 
			
		||||
	ticker := time.NewTicker(pingPeriod)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		ticker.Stop()
 | 
			
		||||
		s.conn.Close()
 | 
			
		||||
	}()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case m, ok := <-s.send:
 | 
			
		||||
			s.conn.SetWriteDeadline(time.Now().Add(writeWait))
 | 
			
		||||
			if !ok {
 | 
			
		||||
				// The collection closed the channel
 | 
			
		||||
				s.conn.WriteMessage(websocket.CloseMessage, []byte{})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			w, err := s.conn.NextWriter(websocket.TextMessage)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			w.Write([]byte{'['})
 | 
			
		||||
			w.Write(m)
 | 
			
		||||
			n := len(s.send) - 1
 | 
			
		||||
			for i := 0; i < n; i++ {
 | 
			
		||||
				w.Write([]byte{','})
 | 
			
		||||
				w.Write(<-s.send)
 | 
			
		||||
			}
 | 
			
		||||
			w.Write([]byte{']'})
 | 
			
		||||
			if err := w.Close(); err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		case <-ticker.C:
 | 
			
		||||
			s.conn.SetWriteDeadline(time.Now().Add(writeWait))
 | 
			
		||||
			if err := s.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								command/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								command/root.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
package command
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/api"
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
	"github.com/pterodactyl/wings/control"
 | 
			
		||||
	"github.com/pterodactyl/wings/utils"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RootCommand is the root command of wings
 | 
			
		||||
var RootCommand = &cobra.Command{
 | 
			
		||||
	Use:     "wings",
 | 
			
		||||
	Short:   "Wings is the next generation server control daemon for Pterodactyl",
 | 
			
		||||
	Long:    "Wings is the next generation server control daemon for Pterodactyl",
 | 
			
		||||
	Run:     run,
 | 
			
		||||
	Version: constants.Version,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var configPath string
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	RootCommand.Flags().StringVarP(&configPath, "config", "c", "./config.yml", "Allows to set the path of the configuration file.")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute registers the RootCommand
 | 
			
		||||
func Execute() {
 | 
			
		||||
	RootCommand.Execute()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func run(cmd *cobra.Command, args []string) {
 | 
			
		||||
	utils.InitLogging()
 | 
			
		||||
	log.Info("Loading configuration...")
 | 
			
		||||
	if err := config.LoadConfiguration(configPath); err != nil {
 | 
			
		||||
		log.WithError(err).Fatal("Could not locate a suitable config.yml file. Aborting startup.")
 | 
			
		||||
		log.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	utils.ConfigureLogging()
 | 
			
		||||
 | 
			
		||||
	log.Info(`                     ____`)
 | 
			
		||||
	log.Info(`__ Pterodactyl _____/___/_______ _______ ______`)
 | 
			
		||||
	log.Info(`\_____\    \/\/    /   /       /  __   /   ___/`)
 | 
			
		||||
	log.Info(`   \___\          /   /   /   /  /_/  /___   /`)
 | 
			
		||||
	log.Info(`        \___/\___/___/___/___/___    /______/`)
 | 
			
		||||
	log.Info(`                            /_______/ v` + constants.Version)
 | 
			
		||||
	log.Info()
 | 
			
		||||
 | 
			
		||||
	log.Info("Configuration loaded successfully.")
 | 
			
		||||
 | 
			
		||||
	log.Info("Loading configured servers.")
 | 
			
		||||
	if err := control.LoadServerConfigurations(filepath.Join(viper.GetString(config.DataPath), constants.ServersPath)); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to load configured servers.")
 | 
			
		||||
	}
 | 
			
		||||
	if amount := len(control.GetServers()); amount > 0 {
 | 
			
		||||
		log.Info("Loaded " + strconv.Itoa(amount) + " server(s).")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Info("Starting API server.")
 | 
			
		||||
	api := &api.InternalAPI{}
 | 
			
		||||
	api.Listen()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ import (
 | 
			
		|||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const configFile = "../config.yml.example"
 | 
			
		||||
const configFile = "../config.example.yml"
 | 
			
		||||
 | 
			
		||||
func TestLoadConfiguraiton(t *testing.T) {
 | 
			
		||||
	err := LoadConfiguration(configFile)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ const (
 | 
			
		|||
 | 
			
		||||
	// DataPath is a string containing the path where data should
 | 
			
		||||
	// be stored on the system
 | 
			
		||||
	DataPath = "datapath"
 | 
			
		||||
	DataPath = "data"
 | 
			
		||||
 | 
			
		||||
	// APIHost is a string containing the interface ip address
 | 
			
		||||
	// on what the api should listen on
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,3 +26,10 @@ const ServerDataPath = "data"
 | 
			
		|||
 | 
			
		||||
// JSONIndent is the indent to use with the json.MarshalIndent() function.
 | 
			
		||||
const JSONIndent = "  "
 | 
			
		||||
 | 
			
		||||
// DockerContainerPrefix is the prefix used for naming Docker containers.
 | 
			
		||||
// It's also used to prefix the hostnames of the docker containers.
 | 
			
		||||
const DockerContainerPrefix = "ptdl-"
 | 
			
		||||
 | 
			
		||||
// WSMaxMessages is the maximum number of messages that are sent in one transfer.
 | 
			
		||||
const WSMaxMessages = 10
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										33
									
								
								control/console_handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								control/console_handler.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/api/websockets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ConsoleHandler struct {
 | 
			
		||||
	Websockets  *websockets.Collection
 | 
			
		||||
	HandlerFunc *func(string)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ io.Writer = ConsoleHandler{}
 | 
			
		||||
 | 
			
		||||
func (c ConsoleHandler) Write(b []byte) (n int, e error) {
 | 
			
		||||
	l := make([]byte, len(b))
 | 
			
		||||
	copy(l, b)
 | 
			
		||||
	line := string(l)
 | 
			
		||||
	m := websockets.Message{
 | 
			
		||||
		Type: websockets.MessageTypeConsole,
 | 
			
		||||
		Payload: websockets.ConsolePayload{
 | 
			
		||||
			Line:   line,
 | 
			
		||||
			Level:  websockets.ConsoleLevelPlain,
 | 
			
		||||
			Source: websockets.ConsoleSourceServer,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	c.Websockets.Broadcast <- m
 | 
			
		||||
	if c.HandlerFunc != nil {
 | 
			
		||||
		(*c.HandlerFunc)(line)
 | 
			
		||||
	}
 | 
			
		||||
	return len(b), nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,220 +0,0 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
 | 
			
		||||
	"github.com/fsouza/go-dockerclient"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type dockerEnvironment struct {
 | 
			
		||||
	baseEnvironment
 | 
			
		||||
 | 
			
		||||
	client    *docker.Client
 | 
			
		||||
	container *docker.Container
 | 
			
		||||
	context   context.Context
 | 
			
		||||
 | 
			
		||||
	containerInput  io.Writer
 | 
			
		||||
	containerOutput io.Writer
 | 
			
		||||
	closeWaiter     docker.CloseWaiter
 | 
			
		||||
 | 
			
		||||
	server *ServerStruct
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ensure DockerEnvironment implements Environment
 | 
			
		||||
var _ Environment = &dockerEnvironment{}
 | 
			
		||||
 | 
			
		||||
// NewDockerEnvironment creates a new docker enviornment
 | 
			
		||||
// instance and connects to the docker client on the host system
 | 
			
		||||
// If the container is already running it will try to reattach
 | 
			
		||||
// to the running container
 | 
			
		||||
func NewDockerEnvironment(server *ServerStruct) (Environment, error) {
 | 
			
		||||
	env := dockerEnvironment{}
 | 
			
		||||
 | 
			
		||||
	env.server = server
 | 
			
		||||
 | 
			
		||||
	client, err := docker.NewClient("unix:///var/run/docker.sock")
 | 
			
		||||
	env.client = client
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).Fatal("Failed to connect to docker.")
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if env.server.DockerContainer.ID != "" {
 | 
			
		||||
		if err := env.checkContainerExists(); err != nil {
 | 
			
		||||
			log.WithError(err).Error("Failed to find the container with stored id, removing id.")
 | 
			
		||||
			env.server.DockerContainer.ID = ""
 | 
			
		||||
			env.server.Save()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &env, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) checkContainerExists() error {
 | 
			
		||||
	container, err := env.client.InspectContainer(env.server.DockerContainer.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	env.container = container
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) attach() error {
 | 
			
		||||
	pr, pw := io.Pipe()
 | 
			
		||||
 | 
			
		||||
	success := make(chan struct{})
 | 
			
		||||
	w, err := env.client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
 | 
			
		||||
		Container:    env.server.DockerContainer.ID,
 | 
			
		||||
		InputStream:  pr,
 | 
			
		||||
		OutputStream: os.Stdout,
 | 
			
		||||
		Stdin:        true,
 | 
			
		||||
		Stdout:       true,
 | 
			
		||||
		Stream:       true,
 | 
			
		||||
		Success:      success,
 | 
			
		||||
	})
 | 
			
		||||
	env.closeWaiter = w
 | 
			
		||||
	env.containerInput = pw
 | 
			
		||||
 | 
			
		||||
	<-success
 | 
			
		||||
	close(success)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create creates the docker container for the environment and applies all
 | 
			
		||||
// settings to it
 | 
			
		||||
func (env *dockerEnvironment) Create() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Creating docker environment")
 | 
			
		||||
	// Split image repository and tag to feed it to the library
 | 
			
		||||
	imageParts := strings.Split(env.server.Service().DockerImage, ":")
 | 
			
		||||
	imageRepoParts := strings.Split(imageParts[0], "/")
 | 
			
		||||
	if len(imageRepoParts) >= 3 {
 | 
			
		||||
		// Handle possibly required authentication
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Pull docker image
 | 
			
		||||
	var pullImageOpts = docker.PullImageOptions{
 | 
			
		||||
		Repository: imageParts[0],
 | 
			
		||||
	}
 | 
			
		||||
	if len(imageParts) >= 2 {
 | 
			
		||||
		pullImageOpts.Tag = imageParts[1]
 | 
			
		||||
	}
 | 
			
		||||
	log.WithField("image", env.server.service.DockerImage).Debug("Pulling docker image")
 | 
			
		||||
	err := env.client.PullImage(pullImageOpts, docker.AuthConfiguration{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", env.server.ID).Error("Failed to create docker environment")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.MkdirAll(env.server.dataPath(), constants.DefaultFolderPerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create docker container
 | 
			
		||||
	// TODO: apply cpu, io, disk limits.
 | 
			
		||||
	containerConfig := &docker.Config{
 | 
			
		||||
		Image:     env.server.Service().DockerImage,
 | 
			
		||||
		Cmd:       strings.Split(env.server.StartupCommand, " "),
 | 
			
		||||
		OpenStdin: true,
 | 
			
		||||
	}
 | 
			
		||||
	containerHostConfig := &docker.HostConfig{
 | 
			
		||||
		Memory:     env.server.Settings.Memory,
 | 
			
		||||
		MemorySwap: env.server.Settings.Swap,
 | 
			
		||||
		Binds:      []string{env.server.dataPath() + ":/home/container"},
 | 
			
		||||
	}
 | 
			
		||||
	createContainerOpts := docker.CreateContainerOptions{
 | 
			
		||||
		Name:       "ptdl-" + env.server.UUIDShort(),
 | 
			
		||||
		Config:     containerConfig,
 | 
			
		||||
		HostConfig: containerHostConfig,
 | 
			
		||||
		Context:    env.context,
 | 
			
		||||
	}
 | 
			
		||||
	container, err := env.client.CreateContainer(createContainerOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", env.server.ID).Error("Failed to create docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	env.server.DockerContainer.ID = container.ID
 | 
			
		||||
	env.server.Save()
 | 
			
		||||
	env.container = container
 | 
			
		||||
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Docker environment created")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy removes the environment's docker container
 | 
			
		||||
func (env *dockerEnvironment) Destroy() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Destroying docker environment")
 | 
			
		||||
	if _, err := env.client.InspectContainer(env.server.DockerContainer.ID); err != nil {
 | 
			
		||||
		if _, ok := err.(*docker.NoSuchContainer); ok {
 | 
			
		||||
			log.WithField("server", env.server.ID).Debug("Container not found, docker environment destroyed already.")
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		log.WithError(err).WithField("server", env.server.ID).Error("Could not destroy docker environment")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err := env.client.RemoveContainer(docker.RemoveContainerOptions{
 | 
			
		||||
		ID: env.server.DockerContainer.ID,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", env.server.ID).Error("Failed to destroy docker environment")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Docker environment destroyed")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) Exists() bool {
 | 
			
		||||
	if env.container != nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	env.checkContainerExists()
 | 
			
		||||
	return env.container != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start starts the environment's docker container
 | 
			
		||||
func (env *dockerEnvironment) Start() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Starting service in docker environment")
 | 
			
		||||
	if err := env.attach(); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to attach to docker container")
 | 
			
		||||
	}
 | 
			
		||||
	if err := env.client.StartContainer(env.container.ID, nil); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to start docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop stops the environment's docker container
 | 
			
		||||
func (env *dockerEnvironment) Stop() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Stopping service in docker environment")
 | 
			
		||||
	if err := env.client.StopContainer(env.container.ID, 20000); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to stop docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) Kill() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Killing service in docker environment")
 | 
			
		||||
	if err := env.client.KillContainer(docker.KillContainerOptions{
 | 
			
		||||
		ID: env.container.ID,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to kill docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exec sends commands to the standard input of the docker container
 | 
			
		||||
func (env *dockerEnvironment) Exec(command string) error {
 | 
			
		||||
	log.Debug("Command: " + command)
 | 
			
		||||
	_, err := env.containerInput.Write([]byte(command + "\n"))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										245
									
								
								control/environment_docker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								control/environment_docker.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,245 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/api/types"
 | 
			
		||||
	"github.com/docker/docker/api/types/container"
 | 
			
		||||
	"github.com/docker/docker/client"
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type dockerEnvironment struct {
 | 
			
		||||
	baseEnvironment
 | 
			
		||||
 | 
			
		||||
	client   *client.Client
 | 
			
		||||
	hires    types.HijackedResponse
 | 
			
		||||
	attached bool
 | 
			
		||||
 | 
			
		||||
	server *ServerStruct
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ensure DockerEnvironment implements Environment
 | 
			
		||||
var _ Environment = &dockerEnvironment{}
 | 
			
		||||
 | 
			
		||||
// NewDockerEnvironment creates a new docker enviornment
 | 
			
		||||
// instance and connects to the docker client on the host system
 | 
			
		||||
// If the container is already running it will try to reattach
 | 
			
		||||
// to the running container
 | 
			
		||||
func NewDockerEnvironment(server *ServerStruct) (Environment, error) {
 | 
			
		||||
	env := dockerEnvironment{}
 | 
			
		||||
 | 
			
		||||
	env.server = server
 | 
			
		||||
	env.attached = false
 | 
			
		||||
 | 
			
		||||
	cli, err := client.NewEnvClient()
 | 
			
		||||
	env.client = cli
 | 
			
		||||
	ctx := context.TODO()
 | 
			
		||||
	cli.NegotiateAPIVersion(ctx)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).Fatal("Failed to connect to docker.")
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if env.server.DockerContainer.ID != "" {
 | 
			
		||||
		if err := env.inspectContainer(ctx); err != nil {
 | 
			
		||||
			log.WithError(err).Error("Failed to find the container with stored id, removing id.")
 | 
			
		||||
			env.server.DockerContainer.ID = ""
 | 
			
		||||
			env.server.Save()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &env, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) inspectContainer(ctx context.Context) error {
 | 
			
		||||
	_, err := env.client.ContainerInspect(ctx, env.server.DockerContainer.ID)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) attach() error {
 | 
			
		||||
	if env.attached {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cw := ConsoleHandler{
 | 
			
		||||
		Websockets: env.server.websockets,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	env.hires, err = env.client.ContainerAttach(context.TODO(), env.server.DockerContainer.ID,
 | 
			
		||||
		types.ContainerAttachOptions{
 | 
			
		||||
			Stdin:  true,
 | 
			
		||||
			Stdout: true,
 | 
			
		||||
			Stderr: true,
 | 
			
		||||
			Stream: true,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithField("server", env.server.ID).WithError(err).Error("Failed to attach to docker container.")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	env.attached = true
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer env.hires.Close()
 | 
			
		||||
		defer func() {
 | 
			
		||||
			env.attached = false
 | 
			
		||||
		}()
 | 
			
		||||
		io.Copy(cw, env.hires.Reader)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create creates the docker container for the environment and applies all
 | 
			
		||||
// settings to it
 | 
			
		||||
func (env *dockerEnvironment) Create() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Creating docker environment")
 | 
			
		||||
 | 
			
		||||
	ctx := context.TODO()
 | 
			
		||||
 | 
			
		||||
	if err := env.pullImage(ctx); err != nil {
 | 
			
		||||
		log.WithError(err).WithField("image", env.server.GetService().DockerImage).WithField("server", env.server.ID).Error("Failed to pull docker image.")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.MkdirAll(env.server.dataPath(), constants.DefaultFolderPerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create docker container
 | 
			
		||||
	// TODO: apply cpu, io, disk limits.
 | 
			
		||||
 | 
			
		||||
	containerConfig := &container.Config{
 | 
			
		||||
		Image:        env.server.GetService().DockerImage,
 | 
			
		||||
		Cmd:          strings.Split(env.server.StartupCommand, " "),
 | 
			
		||||
		AttachStdin:  true,
 | 
			
		||||
		OpenStdin:    true,
 | 
			
		||||
		AttachStdout: true,
 | 
			
		||||
		AttachStderr: true,
 | 
			
		||||
		Tty:          true,
 | 
			
		||||
		Hostname:     constants.DockerContainerPrefix + env.server.UUIDShort(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerHostConfig := &container.HostConfig{
 | 
			
		||||
		Resources: container.Resources{
 | 
			
		||||
			Memory:     env.server.Settings.Memory,
 | 
			
		||||
			MemorySwap: env.server.Settings.Swap,
 | 
			
		||||
		},
 | 
			
		||||
		// TODO: Allow custom binds via some kind of settings in the service
 | 
			
		||||
		Binds: []string{env.server.dataPath() + ":/home/container"},
 | 
			
		||||
		// TODO: Add port bindings
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerHostConfig.Memory = 0
 | 
			
		||||
 | 
			
		||||
	container, err := env.client.ContainerCreate(ctx, containerConfig, containerHostConfig, nil, constants.DockerContainerPrefix+env.server.UUIDShort())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", env.server.ID).Error("Failed to create docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	env.server.DockerContainer.ID = container.ID
 | 
			
		||||
	env.server.Save()
 | 
			
		||||
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Docker environment created")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy removes the environment's docker container
 | 
			
		||||
func (env *dockerEnvironment) Destroy() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Destroying docker environment")
 | 
			
		||||
 | 
			
		||||
	ctx := context.TODO()
 | 
			
		||||
 | 
			
		||||
	if err := env.inspectContainer(ctx); err != nil {
 | 
			
		||||
		log.WithError(err).Debug("Container not found error")
 | 
			
		||||
		log.WithField("server", env.server.ID).Debug("Container not found, docker environment destroyed already.")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := env.client.ContainerRemove(ctx, env.server.DockerContainer.ID, types.ContainerRemoveOptions{}); err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", env.server.ID).Error("Failed to destroy docker environment")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Docker environment destroyed")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) Exists() bool {
 | 
			
		||||
	if err := env.inspectContainer(context.TODO()); err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start starts the environment's docker container
 | 
			
		||||
func (env *dockerEnvironment) Start() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Starting service in docker environment")
 | 
			
		||||
	if err := env.attach(); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to attach to docker container")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := env.client.ContainerStart(context.TODO(), env.server.DockerContainer.ID, types.ContainerStartOptions{}); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to start docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop stops the environment's docker container
 | 
			
		||||
func (env *dockerEnvironment) Stop() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Stopping service in docker environment")
 | 
			
		||||
 | 
			
		||||
	// TODO: Decide after what timeout to kill the container, currently 30 seconds
 | 
			
		||||
	timeout := 30 * time.Second
 | 
			
		||||
	if err := env.client.ContainerStop(context.TODO(), env.server.DockerContainer.ID, &timeout); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to stop docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) Kill() error {
 | 
			
		||||
	log.WithField("server", env.server.ID).Debug("Killing service in docker environment")
 | 
			
		||||
 | 
			
		||||
	if err := env.client.ContainerKill(context.TODO(), env.server.DockerContainer.ID, "KILL"); err != nil {
 | 
			
		||||
		log.WithError(err).Error("Failed to kill docker container")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exec sends commands to the standard input of the docker container
 | 
			
		||||
func (env *dockerEnvironment) Exec(command string) error {
 | 
			
		||||
	log.Debug("Command: " + command)
 | 
			
		||||
	_, err := env.hires.Conn.Write([]byte(command + "\n"))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (env *dockerEnvironment) pullImage(ctx context.Context) error {
 | 
			
		||||
	// Split image repository and tag
 | 
			
		||||
	//imageParts := strings.Split(env.server.GetService().DockerImage, ":")
 | 
			
		||||
	//imageRepoParts := strings.Split(imageParts[0], "/")
 | 
			
		||||
	//if len(imageRepoParts) >= 3 {
 | 
			
		||||
	// TODO: Handle possibly required authentication
 | 
			
		||||
	//}
 | 
			
		||||
 | 
			
		||||
	// Pull docker image
 | 
			
		||||
	log.WithField("image", env.server.GetService().DockerImage).Debug("Pulling docker image")
 | 
			
		||||
 | 
			
		||||
	rc, err := env.client.ImagePull(ctx, env.server.GetService().DockerImage, types.ImagePullOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer rc.Close()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,19 +1,28 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	docker "github.com/fsouza/go-dockerclient"
 | 
			
		||||
	"github.com/pterodactyl/wings/api/websockets"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/api/types"
 | 
			
		||||
	"github.com/docker/docker/client"
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func testServer() *ServerStruct {
 | 
			
		||||
	viper.SetDefault(config.DataPath, "./test_data")
 | 
			
		||||
	return &ServerStruct{
 | 
			
		||||
		ID: "testuuid-something-something",
 | 
			
		||||
		service: &service{
 | 
			
		||||
		Service: &Service{
 | 
			
		||||
			DockerImage: "alpine:latest",
 | 
			
		||||
		},
 | 
			
		||||
		StartupCommand: "/bin/ash echo hello && sleep 100",
 | 
			
		||||
		websockets:     websockets.NewCollection(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +42,7 @@ func TestNewDockerEnvironmentExisting(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	assert.Nil(t, err)
 | 
			
		||||
	assert.NotNil(t, env)
 | 
			
		||||
	assert.NotNil(t, env.container)
 | 
			
		||||
	assert.NotNil(t, env.server.DockerContainer)
 | 
			
		||||
 | 
			
		||||
	eenv.Destroy()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,12 +54,9 @@ func TestCreateDockerEnvironment(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	a := assert.New(t)
 | 
			
		||||
	a.Nil(err)
 | 
			
		||||
	a.NotNil(env.container)
 | 
			
		||||
	a.Equal(env.container.Name, "ptdl_testuuid")
 | 
			
		||||
	a.NotNil(env.server.DockerContainer.ID)
 | 
			
		||||
 | 
			
		||||
	if err := env.client.RemoveContainer(docker.RemoveContainerOptions{
 | 
			
		||||
		ID: env.container.ID,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
	if err := env.client.ContainerRemove(context.TODO(), env.server.DockerContainer.ID, types.ContainerRemoveOptions{}); err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -61,10 +67,10 @@ func TestDestroyDockerEnvironment(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	err := env.Destroy()
 | 
			
		||||
 | 
			
		||||
	_, ierr := env.client.InspectContainer(env.container.ID)
 | 
			
		||||
	_, ierr := env.client.ContainerInspect(context.TODO(), env.server.DockerContainer.ID)
 | 
			
		||||
 | 
			
		||||
	assert.Nil(t, err)
 | 
			
		||||
	assert.IsType(t, ierr, &docker.NoSuchContainer{})
 | 
			
		||||
	assert.True(t, client.IsErrNotFound(ierr))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestStartDockerEnvironment(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,15 +78,13 @@ func TestStartDockerEnvironment(t *testing.T) {
 | 
			
		|||
	env.Create()
 | 
			
		||||
	err := env.Start()
 | 
			
		||||
 | 
			
		||||
	i, ierr := env.client.InspectContainer(env.container.ID)
 | 
			
		||||
	i, ierr := env.client.ContainerInspect(context.TODO(), env.server.DockerContainer.ID)
 | 
			
		||||
 | 
			
		||||
	assert.Nil(t, err)
 | 
			
		||||
	assert.Nil(t, ierr)
 | 
			
		||||
	assert.True(t, i.State.Running)
 | 
			
		||||
 | 
			
		||||
	env.client.KillContainer(docker.KillContainerOptions{
 | 
			
		||||
		ID: env.container.ID,
 | 
			
		||||
	})
 | 
			
		||||
	env.client.ContainerKill(context.TODO(), env.server.DockerContainer.ID, "KILL")
 | 
			
		||||
	env.Destroy()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,15 +94,13 @@ func TestStopDockerEnvironment(t *testing.T) {
 | 
			
		|||
	env.Start()
 | 
			
		||||
	err := env.Stop()
 | 
			
		||||
 | 
			
		||||
	i, ierr := env.client.InspectContainer(env.container.ID)
 | 
			
		||||
	i, ierr := env.client.ContainerInspect(context.TODO(), env.server.DockerContainer.ID)
 | 
			
		||||
 | 
			
		||||
	assert.Nil(t, err)
 | 
			
		||||
	assert.Nil(t, ierr)
 | 
			
		||||
	assert.False(t, i.State.Running)
 | 
			
		||||
 | 
			
		||||
	env.client.KillContainer(docker.KillContainerOptions{
 | 
			
		||||
		ID: env.container.ID,
 | 
			
		||||
	})
 | 
			
		||||
	env.client.ContainerKill(context.TODO(), env.server.DockerContainer.ID, "KILL")
 | 
			
		||||
	env.Destroy()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -108,15 +110,13 @@ func TestKillDockerEnvironment(t *testing.T) {
 | 
			
		|||
	env.Start()
 | 
			
		||||
	err := env.Kill()
 | 
			
		||||
 | 
			
		||||
	i, ierr := env.client.InspectContainer(env.container.ID)
 | 
			
		||||
	i, ierr := env.client.ContainerInspect(context.TODO(), env.server.DockerContainer.ID)
 | 
			
		||||
 | 
			
		||||
	assert.Nil(t, err)
 | 
			
		||||
	assert.Nil(t, ierr)
 | 
			
		||||
	assert.False(t, i.State.Running)
 | 
			
		||||
 | 
			
		||||
	env.client.KillContainer(docker.KillContainerOptions{
 | 
			
		||||
		ID: env.container.ID,
 | 
			
		||||
	})
 | 
			
		||||
	env.client.ContainerKill(context.TODO(), env.server.DockerContainer.ID, "KILL")
 | 
			
		||||
	env.Destroy()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,17 +1,19 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
	"github.com/pterodactyl/wings/api/websockets"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Status string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	StatusStopped  Status = "stopped"
 | 
			
		||||
	StatusStarting Status = "starting"
 | 
			
		||||
	StatusRunning  Status = "running"
 | 
			
		||||
	StatusStopping Status = "stopping"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrServerExists is returned when a server already exists on creation.
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +37,7 @@ type Server interface {
 | 
			
		|||
	Save() error
 | 
			
		||||
 | 
			
		||||
	Environment() (Environment, error)
 | 
			
		||||
	Websockets() *websockets.Collection
 | 
			
		||||
 | 
			
		||||
	HasPermission(string, string) bool
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -42,32 +45,36 @@ type Server interface {
 | 
			
		|||
// ServerStruct is a single instance of a Service managed by the panel
 | 
			
		||||
type ServerStruct struct {
 | 
			
		||||
	// ID is the unique identifier of the server
 | 
			
		||||
	ID string `json:"uuid"`
 | 
			
		||||
	ID string `json:"uuid" jsonapi:"primary,server"`
 | 
			
		||||
 | 
			
		||||
	// ServiceName is the name of the service. It is mainly used to allow storing the service
 | 
			
		||||
	// in the config
 | 
			
		||||
	ServiceName string `json:"serviceName"`
 | 
			
		||||
	service     *Service
 | 
			
		||||
	ServiceName string   `json:"serviceName"`
 | 
			
		||||
	Service     *Service `json:"-" jsonapi:"relation,service"`
 | 
			
		||||
	environment Environment
 | 
			
		||||
 | 
			
		||||
	// StartupCommand is the command executed in the environment to start the server
 | 
			
		||||
	StartupCommand string `json:"startupCommand"`
 | 
			
		||||
	StartupCommand string `json:"startupCommand" jsonapi:"attr,startup_command"`
 | 
			
		||||
 | 
			
		||||
	// DockerContainer holds information regarding the docker container when the server
 | 
			
		||||
	// is running in a docker environment
 | 
			
		||||
	DockerContainer dockerContainer `json:"dockerContainer"`
 | 
			
		||||
	DockerContainer dockerContainer `json:"dockerContainer" jsonapi:"attr,docker_container"`
 | 
			
		||||
 | 
			
		||||
	// EnvironmentVariables are set in the Environment the server is running in
 | 
			
		||||
	EnvironmentVariables map[string]string `json:"env"`
 | 
			
		||||
	EnvironmentVariables map[string]string `json:"environmentVariables" jsonapi:"attr,environment_variables"`
 | 
			
		||||
 | 
			
		||||
	// Allocations contains the ports and ip addresses assigned to the server
 | 
			
		||||
	Allocations allocations `json:"allocation"`
 | 
			
		||||
	Allocations allocations `json:"allocation" jsonapi:"attr,allocations"`
 | 
			
		||||
 | 
			
		||||
	// Settings are the environment settings and limitations for the server
 | 
			
		||||
	Settings settings `json:"settings"`
 | 
			
		||||
	Settings settings `json:"settings" jsonapi:"attr,settings"`
 | 
			
		||||
 | 
			
		||||
	// Keys are some auth keys we will hopefully replace by something better.
 | 
			
		||||
	// TODO remove
 | 
			
		||||
	Keys map[string][]string `json:"keys"`
 | 
			
		||||
 | 
			
		||||
	websockets *websockets.Collection
 | 
			
		||||
	status     Status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type allocations struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -99,73 +106,6 @@ type serversMap map[string]*ServerStruct
 | 
			
		|||
 | 
			
		||||
var servers = make(serversMap)
 | 
			
		||||
 | 
			
		||||
// LoadServerConfigurations loads the configured servers from a specified path
 | 
			
		||||
func LoadServerConfigurations(path string) error {
 | 
			
		||||
	serverFiles, err := ioutil.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	servers = make(serversMap)
 | 
			
		||||
 | 
			
		||||
	for _, file := range serverFiles {
 | 
			
		||||
		if file.IsDir() {
 | 
			
		||||
			server, err := loadServerConfiguration(filepath.Join(path, file.Name(), constants.ServerConfigFile))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			servers[server.ID] = server
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadServerConfiguration(path string) (*ServerStruct, error) {
 | 
			
		||||
	file, err := ioutil.ReadFile(path)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server := &ServerStruct{}
 | 
			
		||||
	if err := json.Unmarshal(file, server); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return server, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func storeServerConfiguration(server *ServerStruct) error {
 | 
			
		||||
	serverJSON, err := json.MarshalIndent(server, "", constants.JSONIndent)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.MkdirAll(server.path(), constants.DefaultFolderPerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := ioutil.WriteFile(server.configFilePath(), serverJSON, constants.DefaultFilePerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func storeServerConfigurations() error {
 | 
			
		||||
	for _, s := range servers {
 | 
			
		||||
		if err := storeServerConfiguration(s); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteServerFolder(id string) error {
 | 
			
		||||
	path := filepath.Join(viper.GetString(config.DataPath), constants.ServersPath, id)
 | 
			
		||||
	folder, err := os.Stat(path)
 | 
			
		||||
	if os.IsNotExist(err) || !folder.IsDir() {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.RemoveAll(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetServers returns an array of all servers the daemon manages
 | 
			
		||||
func GetServers() []Server {
 | 
			
		||||
	serverArray := make([]Server, len(servers))
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +133,11 @@ func CreateServer(server *ServerStruct) (Server, error) {
 | 
			
		|||
	}
 | 
			
		||||
	servers[server.ID] = server
 | 
			
		||||
	if err := server.Save(); err != nil {
 | 
			
		||||
		delete(servers, server.ID)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := server.init(); err != nil {
 | 
			
		||||
		DeleteServer(server.ID)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return server, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -208,13 +153,40 @@ func DeleteServer(id string) error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) init() error {
 | 
			
		||||
	// TODO: Properly use the correct service, mock for now.
 | 
			
		||||
	s.Service = &Service{
 | 
			
		||||
		DockerImage:     "quay.io/pterodactyl/core:java",
 | 
			
		||||
		EnvironmentName: "docker",
 | 
			
		||||
	}
 | 
			
		||||
	s.status = StatusStopped
 | 
			
		||||
 | 
			
		||||
	s.websockets = websockets.NewCollection()
 | 
			
		||||
	go s.websockets.Run()
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	if s.environment == nil {
 | 
			
		||||
		switch s.GetService().EnvironmentName {
 | 
			
		||||
		case "docker":
 | 
			
		||||
			s.environment, err = NewDockerEnvironment(s)
 | 
			
		||||
		default:
 | 
			
		||||
			log.WithField("service", s.ServiceName).Error("Invalid environment name")
 | 
			
		||||
			return errors.New("Invalid environment name")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) Start() error {
 | 
			
		||||
	s.SetStatus(StatusStarting)
 | 
			
		||||
	env, err := s.Environment()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.SetStatus(StatusStopped)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !env.Exists() {
 | 
			
		||||
		if err := env.Create(); err != nil {
 | 
			
		||||
			s.SetStatus(StatusStopped)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -222,8 +194,10 @@ func (s *ServerStruct) Start() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) Stop() error {
 | 
			
		||||
	s.SetStatus(StatusStopping)
 | 
			
		||||
	env, err := s.Environment()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.SetStatus(StatusRunning)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return env.Stop()
 | 
			
		||||
| 
						 | 
				
			
			@ -259,70 +233,3 @@ func (s *ServerStruct) Rebuild() error {
 | 
			
		|||
	}
 | 
			
		||||
	return env.ReCreate()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Service returns the server's service configuration
 | 
			
		||||
func (s *ServerStruct) Service() *Service {
 | 
			
		||||
	if s.service == nil {
 | 
			
		||||
		// TODO: Properly use the correct service, mock for now.
 | 
			
		||||
		s.service = &Service{
 | 
			
		||||
			DockerImage:     "quay.io/pterodactyl/core:java",
 | 
			
		||||
			EnvironmentName: "docker",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return s.service
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UUIDShort returns the first block of the UUID
 | 
			
		||||
func (s *ServerStruct) UUIDShort() string {
 | 
			
		||||
	return s.ID[0:strings.Index(s.ID, "-")]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Environment returns the servers environment
 | 
			
		||||
func (s *ServerStruct) Environment() (Environment, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	if s.environment == nil {
 | 
			
		||||
		switch s.Service().EnvironmentName {
 | 
			
		||||
		case "docker":
 | 
			
		||||
			s.environment, err = NewDockerEnvironment(s)
 | 
			
		||||
		default:
 | 
			
		||||
			log.WithField("service", s.ServiceName).Error("Invalid environment name")
 | 
			
		||||
			return nil, errors.New("Invalid environment name")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return s.environment, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasPermission checks wether a provided token has a specific permission
 | 
			
		||||
func (s *ServerStruct) HasPermission(token string, permission string) bool {
 | 
			
		||||
	for key, perms := range s.Keys {
 | 
			
		||||
		if key == token {
 | 
			
		||||
			for _, perm := range perms {
 | 
			
		||||
				if perm == permission || perm == "s:*" {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) Save() error {
 | 
			
		||||
	if err := storeServerConfiguration(s); err != nil {
 | 
			
		||||
		log.WithField("server", s.ID).WithError(err).Error("Failed to store server configuration.")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) path() string {
 | 
			
		||||
	return filepath.Join(viper.GetString(config.DataPath), constants.ServersPath, s.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) dataPath() string {
 | 
			
		||||
	return filepath.Join(s.path(), constants.ServerDataPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) configFilePath() string {
 | 
			
		||||
	return filepath.Join(s.path(), constants.ServerConfigFile)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										108
									
								
								control/server_persistence.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								control/server_persistence.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LoadServerConfigurations loads the configured servers from a specified path
 | 
			
		||||
func LoadServerConfigurations(path string) error {
 | 
			
		||||
	serverFiles, err := ioutil.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	servers = make(serversMap)
 | 
			
		||||
 | 
			
		||||
	for _, file := range serverFiles {
 | 
			
		||||
		if file.IsDir() {
 | 
			
		||||
			server, err := loadServerConfiguration(filepath.Join(path, file.Name(), constants.ServerConfigFile))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			servers[server.ID] = server
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadServerConfiguration(path string) (*ServerStruct, error) {
 | 
			
		||||
	file, err := ioutil.ReadFile(path)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server := &ServerStruct{}
 | 
			
		||||
	if err := json.Unmarshal(file, server); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := server.init(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return server, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func storeServerConfiguration(server *ServerStruct) error {
 | 
			
		||||
	serverJSON, err := json.MarshalIndent(server, "", constants.JSONIndent)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := os.MkdirAll(server.path(), constants.DefaultFolderPerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := ioutil.WriteFile(server.configFilePath(), serverJSON, constants.DefaultFilePerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func storeServerConfigurations() error {
 | 
			
		||||
	for _, s := range servers {
 | 
			
		||||
		if err := storeServerConfiguration(s); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteServerFolder(id string) error {
 | 
			
		||||
	path := filepath.Join(viper.GetString(config.DataPath), constants.ServersPath, id)
 | 
			
		||||
	folder, err := os.Stat(path)
 | 
			
		||||
	if os.IsNotExist(err) || !folder.IsDir() {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.RemoveAll(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) Save() error {
 | 
			
		||||
	if err := storeServerConfiguration(s); err != nil {
 | 
			
		||||
		log.WithField("server", s.ID).WithError(err).Error("Failed to store server configuration.")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) path() string {
 | 
			
		||||
	p, err := filepath.Abs(viper.GetString(config.DataPath))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.WithError(err).WithField("server", s.ID).Error("Failed to get absolute data path for server.")
 | 
			
		||||
		p = viper.GetString(config.DataPath)
 | 
			
		||||
	}
 | 
			
		||||
	return filepath.Join(p, constants.ServersPath, s.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) dataPath() string {
 | 
			
		||||
	return filepath.Join(s.path(), constants.ServerDataPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) configFilePath() string {
 | 
			
		||||
	return filepath.Join(s.path(), constants.ServerConfigFile)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								control/server_util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								control/server_util.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
package control
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/api/websockets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) SetStatus(st Status) {
 | 
			
		||||
	s.status = st
 | 
			
		||||
	s.websockets.Broadcast <- websockets.Message{
 | 
			
		||||
		Type:    websockets.MessageTypeStatus,
 | 
			
		||||
		Payload: s.status,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Service returns the server's service configuration
 | 
			
		||||
func (s *ServerStruct) GetService() *Service {
 | 
			
		||||
	return s.Service
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UUIDShort returns the first block of the UUID
 | 
			
		||||
func (s *ServerStruct) UUIDShort() string {
 | 
			
		||||
	return s.ID[0:strings.Index(s.ID, "-")]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Environment returns the servers environment
 | 
			
		||||
func (s *ServerStruct) Environment() (Environment, error) {
 | 
			
		||||
	return s.environment, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ServerStruct) Websockets() *websockets.Collection {
 | 
			
		||||
	return s.websockets
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasPermission checks wether a provided token has a specific permission
 | 
			
		||||
func (s *ServerStruct) HasPermission(token string, permission string) bool {
 | 
			
		||||
	for key, perms := range s.Keys {
 | 
			
		||||
		if key == token {
 | 
			
		||||
			for _, perm := range perms {
 | 
			
		||||
				if perm == permission || perm == "s:*" {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ type Service struct {
 | 
			
		|||
	server *Server
 | 
			
		||||
 | 
			
		||||
	// EnvironmentName is the name of the environment used by the service
 | 
			
		||||
	EnvironmentName string `json:"environmentName"`
 | 
			
		||||
	EnvironmentName string `json:"environmentName" jsonapi:"primary,service"`
 | 
			
		||||
 | 
			
		||||
	DockerImage string `json:"dockerImage"`
 | 
			
		||||
	DockerImage string `json:"dockerImage" jsonapi:"attr,docker_image"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										177
									
								
								glide.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								glide.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,177 @@
 | 
			
		|||
hash: 59375e40229965f33d35e16d1ce13db87188dc5a70f89cf54c2921eb9475ba4f
 | 
			
		||||
updated: 2017-11-03T18:09:41.337408376+01:00
 | 
			
		||||
imports:
 | 
			
		||||
- name: bitbucket.org/tebeka/strftime
 | 
			
		||||
  version: 2194253a23c090a4d5953b152a3c63fb5da4f5a4
 | 
			
		||||
- name: github.com/Azure/go-ansiterm
 | 
			
		||||
  version: 19f72df4d05d31cbe1c56bfc8045c96babff6c7e
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - winterm
 | 
			
		||||
- name: github.com/docker/docker
 | 
			
		||||
  version: 90d35abf7b3535c1c319c872900fbd76374e521c
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - api/types
 | 
			
		||||
  - api/types/blkiodev
 | 
			
		||||
  - api/types/container
 | 
			
		||||
  - api/types/filters
 | 
			
		||||
  - api/types/mount
 | 
			
		||||
  - api/types/network
 | 
			
		||||
  - api/types/registry
 | 
			
		||||
  - api/types/strslice
 | 
			
		||||
  - api/types/swarm
 | 
			
		||||
  - api/types/versions
 | 
			
		||||
  - opts
 | 
			
		||||
  - pkg/archive
 | 
			
		||||
  - pkg/fileutils
 | 
			
		||||
  - pkg/homedir
 | 
			
		||||
  - pkg/idtools
 | 
			
		||||
  - pkg/ioutils
 | 
			
		||||
  - pkg/jsonlog
 | 
			
		||||
  - pkg/jsonmessage
 | 
			
		||||
  - pkg/longpath
 | 
			
		||||
  - pkg/pools
 | 
			
		||||
  - pkg/promise
 | 
			
		||||
  - pkg/stdcopy
 | 
			
		||||
  - pkg/system
 | 
			
		||||
  - pkg/term
 | 
			
		||||
  - pkg/term/windows
 | 
			
		||||
- name: github.com/docker/go-connections
 | 
			
		||||
  version: 3ede32e2033de7505e6500d6c868c2b9ed9f169d
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - nat
 | 
			
		||||
- name: github.com/docker/go-units
 | 
			
		||||
  version: 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
 | 
			
		||||
- name: github.com/fsnotify/fsnotify
 | 
			
		||||
  version: 4da3e2cfbabc9f751898f250b49f2439785783a1
 | 
			
		||||
- name: github.com/fsouza/go-dockerclient
 | 
			
		||||
  version: 4df4873b288c855e4186534280c3a3a1af403e67
 | 
			
		||||
- name: github.com/gin-gonic/gin
 | 
			
		||||
  version: e2212d40c62a98b388a5eb48ecbdcf88534688ba
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - binding
 | 
			
		||||
  - render
 | 
			
		||||
- name: github.com/go-ole/go-ole
 | 
			
		||||
  version: 085abb85892dc1949567b726dff00fa226c60c45
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - oleutil
 | 
			
		||||
- name: github.com/golang/protobuf
 | 
			
		||||
  version: 2402d76f3d41f928c7902a765dfc872356dd3aad
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - proto
 | 
			
		||||
- name: github.com/google/jsonapi
 | 
			
		||||
  version: 46d3ced0434461be12e555852e2f1a9ed382e139
 | 
			
		||||
- name: github.com/gorilla/websocket
 | 
			
		||||
  version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b
 | 
			
		||||
- name: github.com/hashicorp/go-cleanhttp
 | 
			
		||||
  version: 3573b8b52aa7b37b9358d966a898feb387f62437
 | 
			
		||||
- name: github.com/hashicorp/hcl
 | 
			
		||||
  version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - hcl/ast
 | 
			
		||||
  - hcl/parser
 | 
			
		||||
  - hcl/scanner
 | 
			
		||||
  - hcl/strconv
 | 
			
		||||
  - hcl/token
 | 
			
		||||
  - json/parser
 | 
			
		||||
  - json/scanner
 | 
			
		||||
  - json/token
 | 
			
		||||
- name: github.com/inconshreveable/mousetrap
 | 
			
		||||
  version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
 | 
			
		||||
- name: github.com/lestrrat/go-file-rotatelogs
 | 
			
		||||
  version: ab335c655133cea61d8164853c1ed0e97d6c77cb
 | 
			
		||||
- name: github.com/magiconair/properties
 | 
			
		||||
  version: 51463bfca2576e06c62a8504b5c0f06d61312647
 | 
			
		||||
- name: github.com/manucorporat/sse
 | 
			
		||||
  version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d
 | 
			
		||||
- name: github.com/mattn/go-isatty
 | 
			
		||||
  version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
 | 
			
		||||
- name: github.com/Microsoft/go-winio
 | 
			
		||||
  version: c4dc1301f1dc0307acd38e611aa375a64dfe0642
 | 
			
		||||
- name: github.com/mitchellh/mapstructure
 | 
			
		||||
  version: d0303fe809921458f417bcf828397a65db30a7e4
 | 
			
		||||
- name: github.com/moby/moby
 | 
			
		||||
  version: 90d35abf7b3535c1c319c872900fbd76374e521c
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - client
 | 
			
		||||
- name: github.com/Nvveen/Gotty
 | 
			
		||||
  version: cd527374f1e5bff4938207604a14f2e38a9cf512
 | 
			
		||||
- name: github.com/opencontainers/runc
 | 
			
		||||
  version: 6ca8b741bb67839b7170d96257dde5c246f8b784
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - libcontainer/system
 | 
			
		||||
  - libcontainer/user
 | 
			
		||||
- name: github.com/pelletier/go-buffruneio
 | 
			
		||||
  version: c37440a7cf42ac63b919c752ca73a85067e05992
 | 
			
		||||
- name: github.com/pelletier/go-toml
 | 
			
		||||
  version: 4a000a21a414d139727f616a8bb97f847b1b310b
 | 
			
		||||
- name: github.com/rifflock/lfshook
 | 
			
		||||
  version: 6844c808343cb8fa357d7f141b1b990e05d24e41
 | 
			
		||||
- name: github.com/shirou/gopsutil
 | 
			
		||||
  version: 6e221c482653ef05c9f6a7bf71ddceea0e40bac5
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - cpu
 | 
			
		||||
  - host
 | 
			
		||||
  - internal/common
 | 
			
		||||
  - mem
 | 
			
		||||
  - net
 | 
			
		||||
  - process
 | 
			
		||||
- name: github.com/shirou/w32
 | 
			
		||||
  version: bb4de0191aa41b5507caa14b0650cdbddcd9280b
 | 
			
		||||
- name: github.com/Sirupsen/logrus
 | 
			
		||||
  version: f006c2ac4710855cf0f916dd6b77acf6b048dc6e
 | 
			
		||||
  repo: https://github.com/sirupsen/logrus.git
 | 
			
		||||
  vcs: git
 | 
			
		||||
- name: github.com/sirupsen/logrus
 | 
			
		||||
  version: f006c2ac4710855cf0f916dd6b77acf6b048dc6e
 | 
			
		||||
- name: github.com/spf13/afero
 | 
			
		||||
  version: 9be650865eab0c12963d8753212f4f9c66cdcf12
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - mem
 | 
			
		||||
- name: github.com/spf13/cast
 | 
			
		||||
  version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
 | 
			
		||||
- name: github.com/spf13/cobra
 | 
			
		||||
  version: 90fc11bbc0a789c29272c21b5ff9e93db183f8dc
 | 
			
		||||
- name: github.com/spf13/jwalterweatherman
 | 
			
		||||
  version: 0efa5202c04663c757d84f90f5219c1250baf94f
 | 
			
		||||
- name: github.com/spf13/pflag
 | 
			
		||||
  version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
 | 
			
		||||
- name: github.com/spf13/viper
 | 
			
		||||
  version: c1de95864d73a5465492829d7cb2dd422b19ac96
 | 
			
		||||
- name: github.com/StackExchange/wmi
 | 
			
		||||
  version: ea383cf3ba6ec950874b8486cd72356d007c768f
 | 
			
		||||
- name: golang.org/x/crypto
 | 
			
		||||
  version: bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - ssh/terminal
 | 
			
		||||
- name: golang.org/x/net
 | 
			
		||||
  version: f315505cf3349909cdf013ea56690da34e96a451
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - context
 | 
			
		||||
  - context/ctxhttp
 | 
			
		||||
- name: golang.org/x/sys
 | 
			
		||||
  version: f7928cfef4d09d1b080aa2b6fd3ca9ba1567c733
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - unix
 | 
			
		||||
  - windows
 | 
			
		||||
- name: golang.org/x/text
 | 
			
		||||
  version: 5a2c30c33799f1e813f7f3259000d594a5ed493a
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - transform
 | 
			
		||||
  - unicode/norm
 | 
			
		||||
- name: gopkg.in/go-playground/validator.v8
 | 
			
		||||
  version: c193cecd124b5cc722d7ee5538e945bdb3348435
 | 
			
		||||
- name: gopkg.in/yaml.v2
 | 
			
		||||
  version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
 | 
			
		||||
testImports:
 | 
			
		||||
- name: github.com/davecgh/go-spew
 | 
			
		||||
  version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - spew
 | 
			
		||||
- name: github.com/pmezard/go-difflib
 | 
			
		||||
  version: 792786c7400a136282c1664665ae0a8db921c6c2
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - difflib
 | 
			
		||||
- name: github.com/stretchr/testify
 | 
			
		||||
  version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - assert
 | 
			
		||||
							
								
								
									
										33
									
								
								glide.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								glide.yaml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
package: github.com/pterodactyl/wings
 | 
			
		||||
import:
 | 
			
		||||
- package: github.com/gin-gonic/gin
 | 
			
		||||
  version: ~1.1.4
 | 
			
		||||
- package: github.com/lestrrat/go-file-rotatelogs
 | 
			
		||||
  version: ~2.0.0
 | 
			
		||||
- package: github.com/rifflock/lfshook
 | 
			
		||||
  version: ~1.7.0
 | 
			
		||||
- package: github.com/sirupsen/logrus
 | 
			
		||||
  version: ~1.0.0
 | 
			
		||||
- package: github.com/Sirupsen/logrus
 | 
			
		||||
  version: ~1.0.0
 | 
			
		||||
  repo: https://github.com/sirupsen/logrus.git
 | 
			
		||||
  vcs: git
 | 
			
		||||
- package: github.com/spf13/viper
 | 
			
		||||
- package: github.com/spf13/cobra
 | 
			
		||||
- package: github.com/shirou/gopsutil
 | 
			
		||||
  version: ^2.17.6
 | 
			
		||||
- package: github.com/moby/moby
 | 
			
		||||
  version: ~17.5.0-ce-rc3
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - client
 | 
			
		||||
- package: github.com/fsouza/go-dockerclient
 | 
			
		||||
- package: github.com/hashicorp/go-cleanhttp
 | 
			
		||||
- package: github.com/gorilla/websocket
 | 
			
		||||
  version: ~1.2.0
 | 
			
		||||
- package: github.com/google/jsonapi
 | 
			
		||||
  version: ~1.0.0
 | 
			
		||||
testImport:
 | 
			
		||||
- package: github.com/stretchr/testify
 | 
			
		||||
  version: ~1.1.4
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - assert
 | 
			
		||||
							
								
								
									
										71
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								main.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,71 +0,0 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/api"
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
	"github.com/pterodactyl/wings/control"
 | 
			
		||||
	"github.com/pterodactyl/wings/utils"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var configPath string
 | 
			
		||||
var RootCommand = &cobra.Command{
 | 
			
		||||
	Use:   "wings",
 | 
			
		||||
	Short: "Wings is the next generation server control daemon for Pterodactyl",
 | 
			
		||||
	Long:  "Wings is the next generation server control daemon for Pterodactyl",
 | 
			
		||||
	Run:   run,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Entrypoint of the application. Currently just boots up the cobra command
 | 
			
		||||
// and lets that handle everything else.
 | 
			
		||||
func main() {
 | 
			
		||||
	RootCommand.Flags().StringVarP(&configPath, "config", "c", "./config.yml", "Allows to set the path of the configuration file.")
 | 
			
		||||
 | 
			
		||||
	if err := RootCommand.Execute(); err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bootstraps the application and beging the process of running the API and
 | 
			
		||||
// server instances.
 | 
			
		||||
func run(cmd *cobra.Command, args []string) {
 | 
			
		||||
	utils.InitLogging()
 | 
			
		||||
 | 
			
		||||
	logrus.Info("Booting configuration file...")
 | 
			
		||||
	if err := config.LoadConfiguration(configPath); err != nil {
 | 
			
		||||
		logrus.WithError(err).Fatal("Could not locate a suitable config.yml file for this Daemon.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Info("Configuration successfully loaded, booting application.")
 | 
			
		||||
	utils.ConfigureLogging()
 | 
			
		||||
 | 
			
		||||
	logrus.Info(`                     ____`)
 | 
			
		||||
	logrus.Info(`__ Pterodactyl _____/___/_______ _______ ______`)
 | 
			
		||||
	logrus.Info(`\_____\    \/\/    /   /       /  __   /   ___/`)
 | 
			
		||||
	logrus.Info(`   \___\          /   /   /   /  /_/  /___   /`)
 | 
			
		||||
	logrus.Info(`        \___/\___/___/___/___/___    /______/`)
 | 
			
		||||
	logrus.Info(`                            /_______/ v` + constants.Version)
 | 
			
		||||
	logrus.Info()
 | 
			
		||||
 | 
			
		||||
	logrus.Info("Loading configured servers.")
 | 
			
		||||
	if err := control.LoadServerConfigurations(filepath.Join(viper.GetString(config.DataPath), constants.ServersPath)); err != nil {
 | 
			
		||||
		logrus.WithError(err).Error("Failed to load configured servers.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if amount := len(control.GetServers()); amount == 1 {
 | 
			
		||||
		logrus.Info("Found and loaded " + strconv.Itoa(amount) + " server(s).")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logrus.Info("Registering API server and booting.")
 | 
			
		||||
	a := api.InternalAPI{}
 | 
			
		||||
	a.Listen()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,12 +3,13 @@ package utils
 | 
			
		|||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
	//"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/pterodactyl/wings/constants"
 | 
			
		||||
 | 
			
		||||
	//"github.com/lestrrat/go-file-rotatelogs"
 | 
			
		||||
	//"github.com/rifflock/lfshook"
 | 
			
		||||
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
 | 
			
		||||
	"github.com/rifflock/lfshook"
 | 
			
		||||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,20 +31,23 @@ func ConfigureLogging() error {
 | 
			
		|||
	if err := os.MkdirAll(path, constants.DefaultFolderPerms); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	//writer := rotatelogs.New(
 | 
			
		||||
	//	path+"/wings.%Y%m%d-%H%M.log",
 | 
			
		||||
	//	rotatelogs.WithLinkName(path),
 | 
			
		||||
	//	rotatelogs.WithMaxAge(time.Duration(viper.GetInt(config.LogDeleteAfterDays))*time.Hour*24),
 | 
			
		||||
	//	rotatelogs.WithRotationTime(time.Duration(604800)*time.Second),
 | 
			
		||||
	//)
 | 
			
		||||
	//
 | 
			
		||||
	//log.AddHook(lfshook.NewHook(lfshook.WriterMap{
 | 
			
		||||
	//	log.DebugLevel: writer,
 | 
			
		||||
	//	log.InfoLevel:  writer,
 | 
			
		||||
	//	log.WarnLevel:  writer,
 | 
			
		||||
	//	log.ErrorLevel: writer,
 | 
			
		||||
	//	log.FatalLevel: writer,
 | 
			
		||||
	//}))
 | 
			
		||||
	writer, err := rotatelogs.New(
 | 
			
		||||
		path+"/wings.%Y%m%d-%H%M.log",
 | 
			
		||||
		rotatelogs.WithLinkName(path),
 | 
			
		||||
		rotatelogs.WithMaxAge(time.Duration(viper.GetInt(config.LogDeleteAfterDays))*time.Hour*24),
 | 
			
		||||
		rotatelogs.WithRotationTime(time.Hour*24),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.AddHook(lfshook.NewHook(lfshook.WriterMap{
 | 
			
		||||
		log.DebugLevel: writer,
 | 
			
		||||
		log.InfoLevel:  writer,
 | 
			
		||||
		log.WarnLevel:  writer,
 | 
			
		||||
		log.ErrorLevel: writer,
 | 
			
		||||
		log.FatalLevel: writer,
 | 
			
		||||
	}, &log.JSONFormatter{}))
 | 
			
		||||
 | 
			
		||||
	level := viper.GetString(config.LogLevel)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								wings-api.paw
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								wings-api.paw
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user