Compare commits
37 Commits
v1.0.0-rc.
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f3394b82d | ||
|
|
bae63c4321 | ||
|
|
f99640a42d | ||
|
|
c73d632e0d | ||
|
|
903902e123 | ||
|
|
1c787b5f26 | ||
|
|
3f9ac8b89a | ||
|
|
560c832cc6 | ||
|
|
13058ad64b | ||
|
|
305cd512a7 | ||
|
|
3cd17a2856 | ||
|
|
56789196d4 | ||
|
|
70804dd20f | ||
|
|
19d821aab5 | ||
|
|
4ce35d3d17 | ||
|
|
a62b588ace | ||
|
|
9b54be06bb | ||
|
|
c031d37b91 | ||
|
|
19051c99ef | ||
|
|
99fd416cee | ||
|
|
acf09180f0 | ||
|
|
b19fc88a95 | ||
|
|
e185f597ba | ||
|
|
3973747c9c | ||
|
|
947279a07c | ||
|
|
ad1ed0f24a | ||
|
|
80387bc294 | ||
|
|
e8dbba5cc0 | ||
|
|
f50f580dcc | ||
|
|
7e8033d96c | ||
|
|
1b94eadc68 | ||
|
|
3a496af9f1 | ||
|
|
7f4771068f | ||
|
|
3463c223e8 | ||
|
|
6c291d9b0e | ||
|
|
e29789114c | ||
|
|
4c9aaeb769 |
35
.github/workflows/build-test.yml
vendored
35
.github/workflows/build-test.yml
vendored
@@ -9,25 +9,48 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
# Default is true, cancels jobs for other platforms in the matrix if one fails
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
os: [ ubuntu-20.04 ]
|
||||
go: [ 1.15 ]
|
||||
goos: [ linux ]
|
||||
goarch: [ amd64, arm, arm64 ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.15.2'
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Build
|
||||
run: GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=dev-${GIT_COMMIT:0:7}" -o build/wings_linux_amd64 -v wings.go
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
go build -v -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=dev-${GIT_COMMIT:0:7}" -o build/wings_${{ matrix.goos }}_${{ matrix.goarch }} wings.go
|
||||
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
|
||||
- name: Compress binary and make it executable
|
||||
if: ${{ github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' }}
|
||||
run: upx build/wings_linux_amd64 && chmod +x build/wings_linux_amd64
|
||||
run: |
|
||||
upx build/wings_${{ matrix.goos }}_${{ matrix.goarch }} && chmod +x build/wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
name: wings_linux_amd64
|
||||
path: build/wings_linux_amd64
|
||||
name: wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
path: build/wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
name: wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
path: build/wings_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
|
||||
48
.github/workflows/release.yml
vendored
48
.github/workflows/release.yml
vendored
@@ -8,8 +8,10 @@ on:
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.15.2'
|
||||
@@ -17,13 +19,19 @@ jobs:
|
||||
- name: Build
|
||||
env:
|
||||
REF: ${{ github.ref }}
|
||||
run: GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_amd64 -v wings.go
|
||||
run: |
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_amd64 -v wings.go
|
||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_arm64 -v wings.go
|
||||
GOOS=linux GOARCH=arm go build -ldflags="-s -w -X github.com/pterodactyl/wings/system.Version=${REF:11}" -o build/wings_linux_arm -v wings.go
|
||||
|
||||
- name: Test
|
||||
run: go test ./...
|
||||
|
||||
- name: Compress binary and make it executable
|
||||
run: upx --brute build/wings_linux_amd64 && chmod +x build/wings_linux_amd64
|
||||
run: |
|
||||
upx --brute build/wings_linux_amd64 && chmod +x build/wings_linux_amd64
|
||||
upx build/wings_linux_arm64 && chmod +x build/wings_linux_arm64
|
||||
upx build/wings_linux_arm && chmod +x build/wings_linux_arm
|
||||
|
||||
- name: Extract changelog
|
||||
env:
|
||||
@@ -35,8 +43,10 @@ jobs:
|
||||
- name: Create checksum and add to changelog
|
||||
run: |
|
||||
SUM=`cd build && sha256sum wings_linux_amd64`
|
||||
echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG
|
||||
echo $SUM > checksum.txt
|
||||
SUM2=`cd build && sha256sum wings_linux_arm64`
|
||||
SUM3=`cd build && sha256sum wings_linux_arm`
|
||||
echo -e "\n#### SHA256 Checksum\n\`\`\`\n$SUM\n$SUM2\n$SUM3\n\`\`\`\n" >> ./RELEASE_CHANGELOG
|
||||
echo -e "$SUM\n$SUM2\n$SUM3" > checksums.txt
|
||||
|
||||
- name: Create release branch
|
||||
env:
|
||||
@@ -64,8 +74,7 @@ jobs:
|
||||
draft: true
|
||||
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
|
||||
|
||||
- name: Upload binary
|
||||
id: upload-release-binary
|
||||
- name: Upload amd64 Binary
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -75,13 +84,32 @@ jobs:
|
||||
asset_name: wings_linux_amd64
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload checksum
|
||||
id: upload-release-checksum
|
||||
- name: Upload arm64 Binary
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./checksum.txt
|
||||
asset_name: checksum.txt
|
||||
asset_path: build/wings_linux_arm64
|
||||
asset_name: wings_linux_arm64
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload arm Binary
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: build/wings_linux_arm
|
||||
asset_name: wings_linux_arm
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload checksum
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./checksums.txt
|
||||
asset_name: checksums.txt
|
||||
asset_content_type: text/plain
|
||||
|
||||
282
CHANGELOG.md
282
CHANGELOG.md
@@ -1,5 +1,254 @@
|
||||
# Changelog
|
||||
|
||||
## next
|
||||
### Added
|
||||
* Adds support for ARM to build outputs for wings.
|
||||
|
||||
### Fixed
|
||||
* Fixed a few docker clients not having version negotiation enabled.
|
||||
* Fixes local images prefixed with `~` getting pulled from remote sources rather than just using the local copy.
|
||||
* Fixes console output breaking with certain games when excessive line length was output.
|
||||
* Fixes an error when console lines were too long that would cause the console to stop updating until the server was restarted,
|
||||
|
||||
### Changed
|
||||
* Simplified timezone logic for containers by properly grabbing the system timezone and then passing that through to containers with the `TZ=` environment variable.
|
||||
|
||||
## v1.0.0
|
||||
This is the first official stable release of Wings! Please be aware that while this specific version changelog is very short,
|
||||
it technically includes all of the previous beta and alpha releases within it. For the sake of version history and following
|
||||
along though, I've only included the differences between this version and the previous RC build.
|
||||
|
||||
### Fixed
|
||||
* Fixes file parser not properly appending newline character to modified lines.
|
||||
* Fixes server disk usage not being properly reported back to the API and websocket.
|
||||
|
||||
### Changed
|
||||
* Changes diagnostics endpoint URL to be `ptero.co` rather than `hastebin.com`.
|
||||
* Diagnostics report now includes system time for easier debugging of logs and container issues.
|
||||
|
||||
## v1.0.0-rc.7
|
||||
### Fixed
|
||||
* **[Security]** Prevents the `allowed_mounts` configuration value from being set by a remote API call.
|
||||
* Fixes an unexpected error when attempting to make a copy of an archive file.
|
||||
* Fixes certain expected filesystems errors being returned to the API as a 500 error rather than a 4XX series error.
|
||||
* Fixes a panic crash when there is no text on a line and the parser is attempting to determine if the line is a comment.
|
||||
* Fixes multiple filesystem operations to correctly check, increment, and decrement disk usage for a server.
|
||||
* Fixes a negative disk space usage issue when deleting a file due to a bad int swap.
|
||||
* Errors from a websocket connection being closed will no longer spam the console.
|
||||
* Fixes an extraneous `.` in the install script for servers causing errors in some scenarios.
|
||||
* Fixes unexpected error spam due to a change in how `os.ErrNotExist` is returned from some functions.
|
||||
|
||||
### Changed
|
||||
* Stacktrace is no longer emitted when warning that an image exists locally.
|
||||
* Configuration parser now attempts to create the directory structure leading to a configuration file if it is missing.
|
||||
* If a file name is too long for the system a nice error is returned to the caller.
|
||||
* Enables client version negotiation for Docker to support more versions.
|
||||
* Disk space errors are no longer logged into the wings logs.
|
||||
* Servers can no longer be reinstalled while another power action is currently running. This avoids data collisions and wings running into issues with the container state.
|
||||
* Wings now uses `1024` rather than `1000` bytes when calculating the disk space usage for a server to match how the Panel reports usage.
|
||||
* JWT errors in the websocket are now sent back to the connection as a specific event type allowing them to be handled even if the timer fails to execute or executes but is not being listened for.
|
||||
* A server struct is no longer embedded in the virtual filesystem allowing for easier testing and modularization of the codebase.
|
||||
* Server websockets are now closed when a server is deleted, disconnecting any currently connected clients.
|
||||
|
||||
### Removed
|
||||
* **[Security]** Removes the `SafeJoin` function which could wrongly assume the state of a file and allow a user to escape the root if the developer implementing the call calls `Stat` rather than `Lstat`.
|
||||
|
||||
## v1.0.0-rc.6
|
||||
### Fixed
|
||||
* Fixes race condition when checking if the running state of a server has changed.
|
||||
* Fixes files mistakenly unpacking themselves to the root directory rather than the directory they are told to unpack to.
|
||||
* Fixes console output not being sent to the websocket in the same order that it was received.
|
||||
* Fixes a file busy error causing a 500 error response when unpacking an archive rather than a 400 error with a message indicating what is wrong to the user.
|
||||
* Fixes docker image not properly updating when a server is started.
|
||||
|
||||
### Changed
|
||||
* Replaces fragile event bus logic with a more robust and easier to understand system. This fixes all remaining console and stats output issues that have been reported.
|
||||
* Cleans up API response error messaging to avoid empty errors being logged which cannot be acted upon.
|
||||
* Adds support for retrying a file write a few times with a back-off if the file is busy when the write occurs.
|
||||
|
||||
### Added
|
||||
* Docker image pull data is shown in the console when an admin is connected to the websocket.
|
||||
* Adds console throttling to stop a server if too much data is being piped out of the console. This logic mimics the logic present in the old Nodejs daemon, but with a 2x line count limit (1000 -> 2000) per period.
|
||||
|
||||
## v1.0.0-rc.5
|
||||
### Fixed
|
||||
* Fixes long standing bug with console output not properly sending back to the client with server stats in certain edge case scenarios. This was "fixed" in `rc.4` but ended up breaking many servers in significantly more painful manners, but did allow us to better track down the issue.
|
||||
* Fixes build flags during release process to correctly strip unnessary code allowing the final binary size to be reduced back down to `~5MB`
|
||||
* Fixes Wings returning the last `16384` lines of the log file when connecting to the websocket.
|
||||
* Fixes pre-boot actions always running for a server start event even if the server is already running.
|
||||
|
||||
### Added
|
||||
* Adds support for configuring the amount of time that can elapse between server disk size checking before the value is considered stale.
|
||||
|
||||
## v1.0.0-rc.4
|
||||
### Fixed
|
||||
* Fixes server files being inaccessible if the root data path is a symlink to another location on the machine.
|
||||
* Fixes some console output being written to the logs accidentally truncating other lines due to special ANSI sequences being output.
|
||||
* Fixes `server.properties` files getting mangled by the automatic configuration editor when booting a server.
|
||||
* Fixes a missed stream close when stopping resource polling that would lead to memory leaks.
|
||||
* Fixes port bindings being incorrectly re-assigned when using `127.0.0.1` with Docker. These are now properly re-mapped to the `pterodactyl0` interface so that networking operates as expected for the server.
|
||||
* Fixes handling of values within arrays in `yaml` and `json` configuration files.
|
||||
* Fixes a 304 error being returned rather than a 400 error when a file upload is invalid.
|
||||
* Fixes deadlocks and response delays when processing server console and stat events. This was causing server consoles to become completely unresponsive after random periods of time and was only resolvable via a Wings restart.
|
||||
|
||||
### Changed
|
||||
* Allows a stale value to be present when starting a server if the server is allowed an unlimited amount of disk space.
|
||||
* Removed all remaining traces of the `zap` logging library from the codebase.
|
||||
* Servers no longer auto-restart as if they crashed when a stop command is manually sent via the server console to the instance.
|
||||
* Changes the CORS checks to allow `*` as a remote origin.
|
||||
|
||||
### Added
|
||||
* Added an auto-generated logrotate file which is written to the normal logrotate directory when wings is first started.
|
||||
* Added additional debug logging within the internals of the resource polling for servers to better trace unexpected behavior.
|
||||
* Adds additional logic check to avoid even trying to stop a suspended server if it is already stopped.
|
||||
|
||||
## v1.0.0-rc.3
|
||||
### Fixed
|
||||
* Errors during the backup process are now correctly reported to the Panel and logged into the output correctly.
|
||||
* Empty directories are no longer pushed into the backup file list (which was causing errors to occur previously).
|
||||
* Covered an edge case to prevent errors if a file gets deleted while a backup is in progress.
|
||||
* Fixed a bug causing Wings to panic and crash if an invalid environment variable value was passed through. These invalid values are now logged to the output to better detect and an empty string is returned in their place.
|
||||
* Fixed startup variables and other server information not properly being updated when a server is restarted.
|
||||
* Suspension state of a server is now properly returned by the API.
|
||||
* Fixes an error being thrown if a backup does not exist on the local machine. A 404 is now properly returned and handled by the Panel when this occurs.
|
||||
* Fixes an error when attempting to move or rename a folder due to the target being created accidentally before the rename occurred.
|
||||
* Fixes install scripts running even when the checkbox to not run them on installation was selected in the Panel.
|
||||
|
||||
### Changed
|
||||
* Disk space checking modified to not block in as many parts of the codebase and allow returning a stale cache value where that is appropriate. Thanks @cyberkitsune!
|
||||
* SFTP package code is now merged into the codebase to make keeping up with all of the code simpler, and reduce the complexity of the SFTP server.
|
||||
|
||||
### Added
|
||||
* Added the ability for a node to be configured to skip file permissions checking when starting a server.
|
||||
* Added console output message to indicate that server disk space checking is occurring rather than silently sitting there.
|
||||
|
||||
## v1.0.0-rc.2
|
||||
### Fixed
|
||||
* Fixes significant performance degradation due to excessive `syscall` actions when determining directory sizes on large servers. This was previously causing CPU & I/O lockups on servers and should be significantly more performant and less impactful on the system now.
|
||||
* Fixes panic crash when booting wings with no log directory created.
|
||||
|
||||
### Changed
|
||||
* Changed default interval for disk space calculation from every 60 seconds to every 2.5 minutes.
|
||||
|
||||
## v1.0.0-rc.1
|
||||
### Fixed
|
||||
* Servers are no longer incorrectly marked as stopping when they are, in fact, offline.
|
||||
* Release build version is now correctly output when starting wings.
|
||||
* Termination signals can now always be sent to a server instance even if the instance is currently starting/stopping.
|
||||
* Removed the file chown on wings boot to avoid slowing down the boot process unnecessarily when working with hundreds of servers on a node.
|
||||
* Fixed a multitude of race conditions throughout the code that cropped up during testing and made power handling even more robust for server instances.
|
||||
* In general multiple fragile areas of the codebase have been improved and are more likely to return useful errors should they break.
|
||||
* Addressed serious CPU usage issues when generating backups, and additionally decreased the amount of time they take to generate.
|
||||
|
||||
### Changed
|
||||
* Internal server now uses more secure and recommended TLS settings.
|
||||
* Environment handling is now completely separate from the server package itself, allowing the environments to no longer be tightly coupled to the server.
|
||||
* `/tmp` directory mounted into containers can now be programmatically managed and uses better defaults to avoid people even needing to edit it.
|
||||
|
||||
### Added
|
||||
* Wings logs are now properly persisted to the disk.
|
||||
* Adds the ability for an egg to use ANSI-stripped matching when determining if a server is done booting.
|
||||
|
||||
## v1.0.0-beta.9
|
||||
### Fixed
|
||||
* Fixes server resource usage stats not being returned correctly by the API endpoint.
|
||||
* Fixes an exception thrown when attempting to write server installation logs.
|
||||
* Fixes error handling to provide a more accurate stack-trace in more scenarios where one is missing initially.
|
||||
* Fixes a memory leak and zombie event listeners when disconnecting from a server's websocket.
|
||||
* Fixes a race condition when wings is attempting to register/de-register event subscribers.
|
||||
* Server data directories now correctly have their permissions set recursively when booting Wings.
|
||||
* Fixes a race condition when a server's console stream was not fully closed before the next power action was started.
|
||||
|
||||
### Changed
|
||||
* Server power handling is now handled in a synchronous manner. This avoids endless bugs and race conditions that would crop up if someone triggered two restart processes back to back. The new logic prevents performing any additional power actions until the currently executing action is completed.
|
||||
* Server disk usage is now correctly calculated when restarting the daemon as long as the server data directory exists.
|
||||
* Multiple code pathways within the boot process were cleaned up and modified to be less IO intensive and overall easier to reason about as a developer.
|
||||
* Additional timezone data is now mounted into containers to better improve the ability for instances running to be using the correct timezone.
|
||||
|
||||
### Added
|
||||
* Adds basic internal file upload endpoints (these are currently not exposed anywhere in the Panel).
|
||||
* Added additional process events for installation start and completion.
|
||||
* Additional CORS allowed origins for the websocket can now be defined in the configuration file.
|
||||
* Adds the ability to authenticate with a docker registry when pulling images.
|
||||
|
||||
## v1.0.0-beta.8
|
||||
### Fixed
|
||||
* Server state is synced with the Panel before performing a reinstall to ensure that the latest information is used.
|
||||
* Wings no longer crashes when a non-string environment variable value is passed through.
|
||||
* SFTP server authentication no longer attempts to contact the Panel to validate credentials if the format is known to already be incorrect.
|
||||
* Some previously missing error stacks are now properly returned when encountered.
|
||||
* Renaming a file no longer triggers an error if the base path does not exist.
|
||||
* Disk space is now properly calculated for a server even if it is assigned unlimited space.
|
||||
* **[Security]** Prevent symlink files from unintentionally chmodding their target file when a server is booted when the target file resides outside the server data directory.
|
||||
* **[Security]** Cleans up multiple code pathways that may unintentionally allow a malicious user to impact files outside their home directory.
|
||||
* Fixes numerous race conditions during a server's boot process that may lead to unintended data states.
|
||||
* Fixes an error thrown when attempting to delete a file that points to a symlink outside the server data directory.
|
||||
* Deleting a symlink will no longer attempt to remove the source file, only the symlink itself.
|
||||
* Websocket no longer blocks when handling a long running process for a user.
|
||||
|
||||
### Changed
|
||||
* Install log for servers is now more detailed and more useful for debugging what might be going wrong.
|
||||
* Certain file management API endpoints now support passing through multiple paths at once to make mass actions easier to perform.
|
||||
* Re-worked file walking implementation to not cause a server crash when working with very large directories, and avoid race conditions when recursively walking directories.
|
||||
* Server configuration structure re-worked to be more manageable in the code base and avoid additional race conditions and complexity while making on-the-fly changes to it.
|
||||
|
||||
### Added
|
||||
* Support for configuring additional file mount points in a container via the Panel.
|
||||
* Support for automated SSL certificate generation when booting the Daemon.
|
||||
* Added wings diagnostics command.
|
||||
* New API endpoints to compress and decompress files on a server.
|
||||
|
||||
## v1.0.0-beta.7
|
||||
### Fixed
|
||||
* Stacktraces are now displayed once in the error output, rather than twice when certain errors are encountered.
|
||||
* Additional errors that did not previously output a stack trace have been fixed to output.
|
||||
* Fixes a bug where server space available would be triggered when creating a new server from a remote configuration before that file location existed on the disk, leading to an error.
|
||||
* Fixes context timeouts while pulling server docker images. Time was upped from 10 seconds to 15 minutes.
|
||||
* Configuration file replacement values are now properly de-escaped when writing to the disk. `\/no\/more\/slashes`
|
||||
* `.properties` files are now saved correctly to the disk with newlines, rather than shoved on a single line.
|
||||
* `./wings configure` command can now properly save the configuration to the disk.
|
||||
* Custom SSL locations are no longer obliterated when making changes to the Node's configuration via the Panel.
|
||||
|
||||
### Changed
|
||||
* Exclusive lock is now acquired when performing a server installation to avoid two install processes being triggered at the same time. This also allows an install to be properly canceled if the server is deleted before it is completed.
|
||||
|
||||
## v1.0.0-beta.6
|
||||
### Fixed
|
||||
* Server status is no longer sent to all connected websocket clients when a new client connects to the socket.
|
||||
* Server disk usage is sent back over the socket when connecting.
|
||||
* Default configuration for the SFTP server is now properly returned to `on` rather than `off`
|
||||
* Server boot is no longer blocked if there is an error fetching the Docker image as long as that image exists on the host.
|
||||
* The websocket no longer gets locked up when Wings attempts to send an error to the client.
|
||||
* Fixed a crash loop when an error is thrown during pre-boot phase of server startup.
|
||||
* Errors with `BindJSON` in API endpoints are now properly handled and returned.
|
||||
* Fixed warning about Gin running in non-release mode, even when the binary is running in release mode.
|
||||
|
||||
### Changed
|
||||
* Switched logging libraries to output data in a clearer format more suited to the CLI where this application runs.
|
||||
* Cleaner debug messaging in debug mode from the router.
|
||||
|
||||
## v1.0.0-beta.5
|
||||
### Fixed
|
||||
* Default config location settled on `/etc/pterodactyl/config.yml`; wings will now check all of the previous locations for the configuration and move it automatically to the new location.
|
||||
* Deleting a server no longer fails the process if the container cannot be found.
|
||||
* Fixes permissions checking for subusers connecting to the SFTP instance.
|
||||
* S3 backups now properly send back hash data to the panel.
|
||||
* Server installation containers are now always deleted, even if the installer process fails for the instance.
|
||||
* Files and folders with special characters and spaces in them no longer return a 404 error.
|
||||
* Servers using eggs with bad configurations will no longer cause the daemon to fail booting; these bad configurations are simply skipped over and a warning is emitted into the logs.
|
||||
* Environment variables passed to containers no longer contain improper quotes around them.
|
||||
* Matching on array indexes in configurations now works correctly; `foo[0]` is quietly transformed into the proper `foo.0` syntax.
|
||||
|
||||
### Added
|
||||
* New banner error message when the daemon is unable to locate the configuration file. This should better clarify what the user needs to do in order to resolve the issue.
|
||||
* Adds ability to configure the default networking driver used by docker.
|
||||
|
||||
## v1.0.0-beta.4
|
||||
### Fixed
|
||||
* Fixes unexpected nil-pointer panic when attempting to start some newly created servers, or any server that was missing a container on the system.
|
||||
* Fixes memory usage of process being reported differently than the `docker stats` output leading to some confusion. These numbers should be more correct now.
|
||||
* Fixes possible nil-pointer panic when detecting a deleted container as being crashed.
|
||||
|
||||
## v1.0.0-beta.3
|
||||
### Fixed
|
||||
* Daemon will no longer crash if someone requests a websocket for a deleted server.
|
||||
@@ -11,6 +260,39 @@
|
||||
### Changed
|
||||
* Memory overhead for containers is now 5/10/15% higher than the passed limit to account for JVM heap and prevent crashing.
|
||||
|
||||
## v1.0.0-beta.2
|
||||
### Changed
|
||||
* Backup functionality made significantly more modular to ease adding additional methods in the future.
|
||||
* Websocket permissions changed to use same name as in panel.
|
||||
* Container memory hard-limits are now adjusted up by 15% (< 2G memory), 10% (< 4G memory), or 5% to avoid unexpected OOM crashes for memory heavy games.
|
||||
* Wings executable is now 80% smaller thanks to better compilation arguments.
|
||||
|
||||
### Added
|
||||
* Adds support for ignoring files and directories when generating a new backup.
|
||||
* Added internal directory walker with callback-continue support.
|
||||
|
||||
### Fixed
|
||||
* Fixed linux distro matching when booting the daemon.
|
||||
* Fixes DNS to be configurable for docker containers that are made for servers.
|
||||
* Fixes incorrect file truncation when making modifications to a server's configuration files.
|
||||
|
||||
## v1.0.0-beta.1
|
||||
### Added
|
||||
* Added support for passing specific threads to the docker environment when running a server.
|
||||
* Added support for reinstalling an existing server.
|
||||
* Added support for restarting a server.
|
||||
* Adds support for transferring servers between daemon instances.
|
||||
* Added auto-deploy command to fetch configuration from Panel automatically.
|
||||
|
||||
### Changed
|
||||
* Server file and backup downloads now hit a direct endpoint with a one-time JWT to avoid proxying large downloads through the panel.
|
||||
|
||||
### Fixed
|
||||
* Fixes a go routine causing a memory and CPU leak.
|
||||
* Fixed improper chown of server directories on boot.
|
||||
* Miscellaneous fixes to CPU usage, abandoned go-routines, and race conditions.
|
||||
* Fixes support for Alpine based systems.
|
||||
|
||||
## v1.0.0-alpha.2
|
||||
### Added
|
||||
* Ability to run an installation process for a server and notify the panel when completed.
|
||||
|
||||
3
Makefile
3
Makefile
@@ -1,5 +1,6 @@
|
||||
build:
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(PWD)" -o build/wings_linux_amd64 -v wings.go
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(pwd)" -o build/wings_linux_amd64 -v wings.go
|
||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -gcflags "all=-trimpath=$(pwd)" -o build/wings_linux_arm64 -v wings.go
|
||||
|
||||
compress:
|
||||
upx --brute build/wings_*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[](https://pterodactyl.io)
|
||||
|
||||
[](https://pterodactyl.io/discord)
|
||||

|
||||

|
||||
[](https://goreportcard.com/report/github.com/pterodactyl/wings)
|
||||
|
||||
# Pterodactyl Wings
|
||||
@@ -17,13 +18,15 @@ I would like to extend my sincere thanks to the following sponsors for helping f
|
||||
|
||||
| Company | About |
|
||||
| ------- | ----- |
|
||||
| [**WISP**](https://wisp.gg) | Extra features. |
|
||||
| [**Bloom.host**](https://bloom.host) | Bloom.host offers dedicated core VPS and Minecraft hosting with Ryzen 9 processors. With owned-hardware, we offer truly unbeatable prices on high-performance hosting. |
|
||||
| [**VersatileNode**](https://versatilenode.com/) | Looking to host a minecraft server, vps, or a website? VersatileNode is one of the most affordable hosting providers to provide quality yet cheap services with incredible support. |
|
||||
| [**MineStrator**](https://minestrator.com/) | Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord, trust us. |
|
||||
| [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. |
|
||||
| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! |
|
||||
| [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. |
|
||||
| [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. |
|
||||
| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. |
|
||||
| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. |
|
||||
|
||||
## Documentation
|
||||
* [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html)
|
||||
|
||||
@@ -34,7 +34,7 @@ type InstallationScript struct {
|
||||
}
|
||||
|
||||
// GetAllServerConfigurations fetches configurations for all servers assigned to this node.
|
||||
func (r *PanelRequest) GetAllServerConfigurations() (map[string]*ServerConfigurationResponse, *RequestError, error) {
|
||||
func (r *PanelRequest) GetAllServerConfigurations() (map[string]json.RawMessage, *RequestError, error) {
|
||||
resp, err := r.Get("/servers")
|
||||
if err != nil {
|
||||
return nil, nil, errors.WithStack(err)
|
||||
@@ -48,7 +48,7 @@ func (r *PanelRequest) GetAllServerConfigurations() (map[string]*ServerConfigura
|
||||
}
|
||||
|
||||
b, _ := r.ReadBody()
|
||||
res := map[string]*ServerConfigurationResponse{}
|
||||
res := map[string]json.RawMessage{}
|
||||
if len(b) == 2 {
|
||||
return res, nil, nil
|
||||
}
|
||||
@@ -61,24 +61,23 @@ func (r *PanelRequest) GetAllServerConfigurations() (map[string]*ServerConfigura
|
||||
}
|
||||
|
||||
// Fetches the server configuration and returns the struct for it.
|
||||
func (r *PanelRequest) GetServerConfiguration(uuid string) (*ServerConfigurationResponse, *RequestError, error) {
|
||||
func (r *PanelRequest) GetServerConfiguration(uuid string) (ServerConfigurationResponse, *RequestError, error) {
|
||||
res := ServerConfigurationResponse{}
|
||||
|
||||
resp, err := r.Get(fmt.Sprintf("/servers/%s", uuid))
|
||||
if err != nil {
|
||||
return nil, nil, errors.WithStack(err)
|
||||
return res, nil, errors.WithStack(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
r.Response = resp
|
||||
|
||||
if r.HasError() {
|
||||
return nil, r.Error(), nil
|
||||
return res, r.Error(), nil
|
||||
}
|
||||
|
||||
res := &ServerConfigurationResponse{}
|
||||
b, _ := r.ReadBody()
|
||||
|
||||
if err := json.Unmarshal(b, res); err != nil {
|
||||
return nil, nil, errors.WithStack(err)
|
||||
if err := json.Unmarshal(b, &res); err != nil {
|
||||
return res, nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return res, nil, nil
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/pterodactyl/wings/environment"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -13,20 +14,20 @@ import (
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/AlecAivazis/survey/v2/terminal"
|
||||
"github.com/docker/cli/components/engine/pkg/parsers/operatingsystem"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"github.com/pterodactyl/wings/system"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const DefaultHastebinUrl = "https://hastebin.com"
|
||||
const DefaultLogLines = 50
|
||||
const DefaultHastebinUrl = "https://ptero.co"
|
||||
const DefaultLogLines = 200
|
||||
|
||||
var (
|
||||
diagnosticsArgs struct {
|
||||
@@ -88,37 +89,39 @@ func diagnosticsCmdRun(cmd *cobra.Command, args []string) {
|
||||
output := &strings.Builder{}
|
||||
fmt.Fprintln(output, "Pterodactyl Wings - Diagnostics Report")
|
||||
printHeader(output, "Versions")
|
||||
fmt.Fprintln(output, "wings:", system.Version)
|
||||
fmt.Fprintln(output, " wings:", system.Version)
|
||||
if dockerErr == nil {
|
||||
fmt.Fprintln(output, "Docker", dockerVersion.Version)
|
||||
fmt.Fprintln(output, "Docker:", dockerVersion.Version)
|
||||
}
|
||||
if v, err := kernel.GetKernelVersion(); err == nil {
|
||||
fmt.Fprintln(output, "Kernel:", v)
|
||||
}
|
||||
if os, err := operatingsystem.GetOperatingSystem(); err == nil {
|
||||
fmt.Fprintln(output, "OS:", os)
|
||||
fmt.Fprintln(output, " OS:", os)
|
||||
}
|
||||
|
||||
printHeader(output, "Wings Configuration")
|
||||
cfg, err := config.ReadConfiguration(config.DefaultLocation)
|
||||
if cfg != nil {
|
||||
fmt.Fprintln(output, "Panel Location:", redact(cfg.PanelLocation))
|
||||
fmt.Fprintln(output, "Api Host:", redact(cfg.Api.Host))
|
||||
fmt.Fprintln(output, "Api Port:", cfg.Api.Port)
|
||||
fmt.Fprintln(output, "Api Ssl Enabled:", cfg.Api.Ssl.Enabled)
|
||||
fmt.Fprintln(output, "Api Ssl Certificate:", redact(cfg.Api.Ssl.CertificateFile))
|
||||
fmt.Fprintln(output, "Api Ssl Key:", redact(cfg.Api.Ssl.KeyFile))
|
||||
fmt.Fprintln(output, "Sftp Address:", redact(cfg.System.Sftp.Address))
|
||||
fmt.Fprintln(output, "Sftp Port:", cfg.System.Sftp.Port)
|
||||
fmt.Fprintln(output, "Sftp Read Only:", cfg.System.Sftp.ReadOnly)
|
||||
fmt.Fprintln(output, "Sftp Diskchecking Disabled:", cfg.System.Sftp.DisableDiskChecking)
|
||||
fmt.Fprintln(output, "System Root Directory:", cfg.System.RootDirectory)
|
||||
fmt.Fprintln(output, "System Logs Directory:", cfg.System.LogDirectory)
|
||||
fmt.Fprintln(output, "System Data Directory:", cfg.System.Data)
|
||||
fmt.Fprintln(output, "System Archive Directory:", cfg.System.ArchiveDirectory)
|
||||
fmt.Fprintln(output, "System Backup Directory:", cfg.System.BackupDirectory)
|
||||
fmt.Fprintln(output, "System Username:", cfg.System.Username)
|
||||
fmt.Fprintln(output, "Debug Enabled:", cfg.Debug)
|
||||
fmt.Fprintln(output, " Panel Location:", redact(cfg.PanelLocation))
|
||||
fmt.Fprintln(output, "")
|
||||
fmt.Fprintln(output, " Internal Webserver:", redact(cfg.Api.Host), ":", cfg.Api.Port)
|
||||
fmt.Fprintln(output, " SSL Enabled:", cfg.Api.Ssl.Enabled)
|
||||
fmt.Fprintln(output, " SSL Certificate:", redact(cfg.Api.Ssl.CertificateFile))
|
||||
fmt.Fprintln(output, " SSL Key:", redact(cfg.Api.Ssl.KeyFile))
|
||||
fmt.Fprintln(output, "")
|
||||
fmt.Fprintln(output, " SFTP Server:", redact(cfg.System.Sftp.Address), ":", cfg.System.Sftp.Port)
|
||||
fmt.Fprintln(output, " SFTP Read-Only:", cfg.System.Sftp.ReadOnly)
|
||||
fmt.Fprintln(output, "")
|
||||
fmt.Fprintln(output, " Root Directory:", cfg.System.RootDirectory)
|
||||
fmt.Fprintln(output, " Logs Directory:", cfg.System.LogDirectory)
|
||||
fmt.Fprintln(output, " Data Directory:", cfg.System.Data)
|
||||
fmt.Fprintln(output, " Archive Directory:", cfg.System.ArchiveDirectory)
|
||||
fmt.Fprintln(output, " Backup Directory:", cfg.System.BackupDirectory)
|
||||
fmt.Fprintln(output, "")
|
||||
fmt.Fprintln(output, " Username:", cfg.System.Username)
|
||||
fmt.Fprintln(output, " Server Time:", time.Now().Format(time.RFC1123Z))
|
||||
fmt.Fprintln(output, " Debug Mode:", cfg.Debug)
|
||||
} else {
|
||||
fmt.Println("Failed to load configuration.", err)
|
||||
}
|
||||
@@ -137,7 +140,7 @@ func diagnosticsCmdRun(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(output, "LoggingDriver:", dockerInfo.LoggingDriver)
|
||||
fmt.Fprintln(output, "CgroupDriver:", dockerInfo.CgroupDriver)
|
||||
fmt.Fprintln(output, " CgroupDriver:", dockerInfo.CgroupDriver)
|
||||
if len(dockerInfo.Warnings) > 0 {
|
||||
for _, w := range dockerInfo.Warnings {
|
||||
fmt.Fprintln(output, w)
|
||||
@@ -184,7 +187,7 @@ func diagnosticsCmdRun(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
func getDockerInfo() (types.Version, types.Info, error) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
cli, err := environment.DockerClient()
|
||||
if err != nil {
|
||||
return types.Version{}, types.Info{}, err
|
||||
}
|
||||
|
||||
@@ -132,6 +132,13 @@ func rootCmdRun(*cobra.Command, []string) {
|
||||
config.Set(c)
|
||||
config.SetDebugViaFlag(debug)
|
||||
|
||||
if err := c.System.ConfigureTimezone(); err != nil {
|
||||
log.WithField("error", err).Fatal("failed to detect system timezone or use supplied configuration value")
|
||||
return
|
||||
}
|
||||
|
||||
log.WithField("timezone", c.System.Timezone).Info("configured wings with system timezone")
|
||||
|
||||
if err := c.System.ConfigureDirectories(); err != nil {
|
||||
log.WithField("error", err).Fatal("failed to configure system directories for pterodactyl")
|
||||
return
|
||||
|
||||
@@ -73,9 +73,6 @@ type Configuration struct {
|
||||
|
||||
// Defines the configuration of the internal SFTP server.
|
||||
type SftpConfiguration struct {
|
||||
// If set to true disk checking will not be performed. This will prevent the SFTP
|
||||
// server from checking the total size of a directory when uploading files.
|
||||
DisableDiskChecking bool `default:"false" yaml:"disable_disk_checking"`
|
||||
// The bind address of the SFTP server.
|
||||
Address string `default:"0.0.0.0" json:"bind_address" yaml:"bind_address"`
|
||||
// The bind port of the SFTP server.
|
||||
|
||||
@@ -49,18 +49,6 @@ type DockerConfiguration struct {
|
||||
// Domainname is the Docker domainname for all containers.
|
||||
Domainname string `default:"" json:"domainname" yaml:"domainname"`
|
||||
|
||||
// If true, container images will be updated when a server starts if there
|
||||
// is an update available. If false the daemon will not attempt updates and will
|
||||
// defer to the host system to manage image updates.
|
||||
UpdateImages bool `default:"true" json:"update_images" yaml:"update_images"`
|
||||
|
||||
// The location of the Docker socket.
|
||||
Socket string `default:"/var/run/docker.sock" json:"socket" yaml:"socket"`
|
||||
|
||||
// Defines the location of the timezone file on the host system that should
|
||||
// be mounted into the created containers so that they all use the same time.
|
||||
TimezonePath string `default:"/etc/timezone" json:"timezone_path" yaml:"timezone_path"`
|
||||
|
||||
// Registries .
|
||||
Registries map[string]RegistryConfiguration `json:"registries" yaml:"registries"`
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/pkg/errors"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Defines basic system configuration settings.
|
||||
@@ -29,6 +35,13 @@ type SystemConfiguration struct {
|
||||
// The user that should own all of the server files, and be used for containers.
|
||||
Username string `default:"pterodactyl" yaml:"username"`
|
||||
|
||||
// The timezone for this Wings instance. This is detected by Wings automatically if possible,
|
||||
// and falls back to UTC if not able to be detected. If you need to set this manually, that
|
||||
// can also be done.
|
||||
//
|
||||
// This timezone value is passed into all containers created by Wings.
|
||||
Timezone string `yaml:"timezone"`
|
||||
|
||||
// Definitions for the user that gets created to ensure that we can quickly access
|
||||
// this information without constantly having to do a system lookup.
|
||||
User struct {
|
||||
@@ -166,3 +179,47 @@ func (sc *SystemConfiguration) GetStatesPath() string {
|
||||
func (sc *SystemConfiguration) GetInstallLogPath() string {
|
||||
return path.Join(sc.LogDirectory, "install/")
|
||||
}
|
||||
|
||||
// Configures the timezone data for the configuration if it is currently missing. If
|
||||
// a value has been set, this functionality will only run to validate that the timezone
|
||||
// being used is valid.
|
||||
func (sc *SystemConfiguration) ConfigureTimezone() error {
|
||||
if sc.Timezone == "" {
|
||||
if b, err := ioutil.ReadFile("/etc/timezone"); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "failed to open /etc/timezone for automatic server timezone calibration")
|
||||
}
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second * 5)
|
||||
// Okay, file isn't found on this OS, we will try using timedatectl to handle this. If this
|
||||
// command fails, exit, but if it returns a value use that. If no value is returned we will
|
||||
// fall through to UTC to get Wings booted at least.
|
||||
out, err := exec.CommandContext(ctx, "timedatectl").Output()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Warn("failed to execute \"timedatectl\" to determine system timezone, falling back to UTC")
|
||||
|
||||
sc.Timezone = "UTC"
|
||||
return nil
|
||||
}
|
||||
|
||||
r := regexp.MustCompile(`Time zone: ([\w/]+)`)
|
||||
matches := r.FindSubmatch(out)
|
||||
if len(matches) != 2 || string(matches[1]) == "" {
|
||||
log.Warn("failed to parse timezone from \"timedatectl\" output, falling back to UTC")
|
||||
|
||||
sc.Timezone = "UTC"
|
||||
return nil
|
||||
}
|
||||
|
||||
sc.Timezone = string(matches[1])
|
||||
} else {
|
||||
sc.Timezone = string(b)
|
||||
}
|
||||
}
|
||||
|
||||
sc.Timezone = regexp.MustCompile(`(?i)[^a-z_/]+`).ReplaceAllString(sc.Timezone, "")
|
||||
|
||||
_, err := time.LoadLocation(sc.Timezone)
|
||||
|
||||
return errors.Wrap(err, fmt.Sprintf("the supplied timezone %s is invalid", sc.Timezone))
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package environment
|
||||
import (
|
||||
"context"
|
||||
"github.com/apex/log"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
@@ -10,10 +12,28 @@ import (
|
||||
"github.com/pterodactyl/wings/config"
|
||||
)
|
||||
|
||||
var _cmu sync.Mutex
|
||||
var _client *client.Client
|
||||
|
||||
// Return a Docker client to be used throughout the codebase. Once a client has been created it
|
||||
// will be returned for all subsequent calls to this function.
|
||||
func DockerClient() (*client.Client, error) {
|
||||
_cmu.Lock()
|
||||
defer _cmu.Unlock()
|
||||
|
||||
if _client != nil {
|
||||
return _client, nil
|
||||
}
|
||||
|
||||
_client, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
|
||||
return _client, err
|
||||
}
|
||||
|
||||
// Configures the required network for the docker environment.
|
||||
func ConfigureDocker(c *config.DockerConfiguration) error {
|
||||
// Ensure the required docker network exists on the system.
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
cli, err := DockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -64,7 +84,7 @@ func createDockerNetwork(cli *client.Client, c *config.DockerConfiguration) erro
|
||||
Options: map[string]string{
|
||||
"encryption": "false",
|
||||
"com.docker.network.bridge.default_bridge": "false",
|
||||
"com.docker.network.bridge.enable_icc": "true",
|
||||
"com.docker.network.bridge.enable_icc": strconv.FormatBool(c.Network.EnableICC),
|
||||
"com.docker.network.bridge.enable_ip_masquerade": "true",
|
||||
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
|
||||
"com.docker.network.bridge.name": "pterodactyl0",
|
||||
|
||||
@@ -2,6 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -286,15 +287,53 @@ func (e *Environment) followOutput() error {
|
||||
|
||||
reader, err := e.client.ContainerLogs(context.Background(), e.Id, opts)
|
||||
|
||||
go func(r io.ReadCloser) {
|
||||
defer r.Close()
|
||||
go func(reader io.ReadCloser) {
|
||||
defer reader.Close()
|
||||
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
e.Events().Publish(environment.ConsoleOutputEvent, s.Text())
|
||||
r := bufio.NewReader(reader)
|
||||
ParentLoop:
|
||||
for {
|
||||
var b bytes.Buffer
|
||||
var line []byte
|
||||
var isPrefix bool
|
||||
|
||||
for {
|
||||
// Read the line and write it to the buffer.
|
||||
line, isPrefix, err = r.ReadLine()
|
||||
|
||||
// Certain games like Minecraft output absolutely random carriage returns in the output seemingly
|
||||
// in line with that it thinks is the terminal size. Those returns break a lot of output handling,
|
||||
// so we'll just replace them with proper new-lines and then split it later and send each line as
|
||||
// its own event in the response.
|
||||
b.Write(bytes.ReplaceAll(line, []byte(" \r"), []byte("\r\n")))
|
||||
|
||||
// Finish this loop and begin outputting the line if there is no prefix (the line fit into
|
||||
// the default buffer), or if we hit the end of the line.
|
||||
if !isPrefix || err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err := s.Err(); err != nil {
|
||||
// If we encountered an error with something in ReadLine that was not an EOF just abort
|
||||
// the entire process here.
|
||||
if err != nil {
|
||||
break ParentLoop
|
||||
}
|
||||
}
|
||||
|
||||
// Publish the line for this loop. Break on new-line characters so every line is sent as a single
|
||||
// output event, otherwise you get funky handling in the browser console.
|
||||
for _, line := range strings.Split(b.String(), "\r\n") {
|
||||
e.Events().Publish(environment.ConsoleOutputEvent, line)
|
||||
}
|
||||
|
||||
// If the error we got previously that lead to the line being output is an io.EOF we want to
|
||||
// exit the entire looping process.
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
log.WithField("error", err).WithField("container_id", e.Id).Warn("error processing scanner line in console output")
|
||||
}
|
||||
}(reader)
|
||||
@@ -310,12 +349,15 @@ func (e *Environment) followOutput() error {
|
||||
// need to block all of the servers from booting just because of that. I'd imagine in a lot of
|
||||
// cases an outage shouldn't affect users too badly. It'll at least keep existing servers working
|
||||
// correctly if anything.
|
||||
//
|
||||
// TODO: local images
|
||||
func (e *Environment) ensureImageExists(image string) error {
|
||||
e.Events().Publish(environment.DockerImagePullStarted, "")
|
||||
defer e.Events().Publish(environment.DockerImagePullCompleted, "")
|
||||
|
||||
// Images prefixed with a ~ are local images that we do not need to try and pull.
|
||||
if strings.HasPrefix(image, "~") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Give it up to 15 minutes to pull the image. I think this should cover 99.8% of cases where an
|
||||
// image pull might fail. I can't imagine it will ever take more than 15 minutes to fully pull
|
||||
// an image. Let me know when I am inevitably wrong here...
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
type Metadata struct {
|
||||
Image string
|
||||
Stop *api.ProcessStopConfiguration
|
||||
Stop api.ProcessStopConfiguration
|
||||
}
|
||||
|
||||
// Ensure that the Docker environment is always implementing all of the methods
|
||||
@@ -55,7 +55,7 @@ type Environment struct {
|
||||
// reference the container from here on out. This should be unique per-server (we use the UUID
|
||||
// by default). The container does not need to exist at this point.
|
||||
func New(id string, m *Metadata, c *environment.Configuration) (*Environment, error) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
cli, err := environment.DockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func (e *Environment) Config() *environment.Configuration {
|
||||
}
|
||||
|
||||
// Sets the stop configuration for the environment.
|
||||
func (e *Environment) SetStopConfiguration(c *api.ProcessStopConfiguration) {
|
||||
func (e *Environment) SetStopConfiguration(c api.ProcessStopConfiguration) {
|
||||
e.mu.Lock()
|
||||
e.meta.Stop = c
|
||||
e.mu.Unlock()
|
||||
|
||||
@@ -126,8 +126,8 @@ func (e *Environment) Stop() error {
|
||||
s := e.meta.Stop
|
||||
e.mu.RUnlock()
|
||||
|
||||
if s == nil || s.Type == api.ProcessStopSignal {
|
||||
if s == nil {
|
||||
if s.Type == "" || s.Type == api.ProcessStopSignal {
|
||||
if s.Type == "" {
|
||||
log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using termination procedure")
|
||||
}
|
||||
|
||||
|
||||
@@ -33,14 +33,12 @@ func (e *Environment) SendCommand(c string) error {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
|
||||
if e.meta.Stop != nil {
|
||||
// If the command being processed is the same as the process stop command then we want to mark
|
||||
// the server as entering the stopping state otherwise the process will stop and Wings will think
|
||||
// it has crashed and attempt to restart it.
|
||||
if e.meta.Stop.Type == "command" && c == e.meta.Stop.Value {
|
||||
e.Events().Publish(environment.StateChangeEvent, environment.ProcessStoppingState)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := e.stream.Conn.Write([]byte(c + "\n"))
|
||||
|
||||
|
||||
@@ -390,39 +390,26 @@ func (f *ConfigurationFile) parseYamlFile(path string) error {
|
||||
// scanning a file and performing a replacement. You should attempt to use anything other
|
||||
// than this function where possible.
|
||||
func (f *ConfigurationFile) parseTextFile(path string) error {
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0644)
|
||||
input, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
hasReplaced := false
|
||||
t := scanner.Text()
|
||||
|
||||
// Iterate over the potential replacements for the line and check if there are
|
||||
// any matches.
|
||||
lines := strings.Split(string(input), "\n")
|
||||
for i, line := range lines {
|
||||
for _, replace := range f.Replace {
|
||||
if !strings.HasPrefix(t, replace.Match) {
|
||||
// If this line doesn't match what we expect for the replacement, move on to the next
|
||||
// line. Otherwise, update the line to have the replacement value.
|
||||
if !strings.HasPrefix(line, replace.Match) {
|
||||
continue
|
||||
}
|
||||
|
||||
hasReplaced = true
|
||||
t = strings.Replace(t, replace.Match, replace.ReplaceWith.String(), 1)
|
||||
}
|
||||
|
||||
// If there was a replacement that occurred on this specific line, do a write to the file
|
||||
// immediately to write that modified content to the disk.
|
||||
if hasReplaced {
|
||||
if _, err := file.WriteAt([]byte(t+"\n"), int64(len(scanner.Bytes()))); err != nil {
|
||||
return err
|
||||
}
|
||||
lines[i] = replace.ReplaceWith.String()
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
if err := ioutil.WriteFile(path, []byte(strings.Join(lines, "\n")), 0644); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -75,6 +75,16 @@ func (fs *Filesystem) HasSpaceAvailable(allowStaleValue bool) bool {
|
||||
return size <= fs.MaxDisk()
|
||||
}
|
||||
|
||||
// Returns the cached value for the amount of disk space used by the filesystem. Do not rely on this
|
||||
// function for critical logical checks. It should only be used in areas where the actual disk usage
|
||||
// does not need to be perfect, e.g. API responses for server resource usage.
|
||||
func (fs *Filesystem) CachedUsage() int64 {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
|
||||
return fs.diskUsed
|
||||
}
|
||||
|
||||
// Internal helper function to allow other parts of the codebase to check the total used disk space
|
||||
// as needed without overly taxing the system. This will prioritize the value from the cache to avoid
|
||||
// excessive IO usage. We will only walk the filesystem and determine the size of the directory if there
|
||||
@@ -199,7 +209,8 @@ func (fs *Filesystem) hasSpaceFor(size int64) error {
|
||||
|
||||
// Updates the disk usage for the Filesystem instance.
|
||||
func (fs *Filesystem) addDisk(i int64) int64 {
|
||||
var size = atomic.LoadInt64(&fs.diskUsed)
|
||||
size := atomic.LoadInt64(&fs.diskUsed)
|
||||
|
||||
// Sorry go gods. This is ugly but the best approach I can come up with for right
|
||||
// now without completely re-evaluating the logic we use for determining disk space.
|
||||
//
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
@@ -329,7 +330,7 @@ func TestFilesystem_Readfile(t *testing.T) {
|
||||
|
||||
g.AfterEach(func() {
|
||||
buf.Truncate(0)
|
||||
fs.diskUsed = 0
|
||||
atomic.StoreInt64(&fs.diskUsed, 0)
|
||||
rfs.reset()
|
||||
})
|
||||
})
|
||||
@@ -347,7 +348,7 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
g.It("can create a new file", func() {
|
||||
r := bytes.NewReader([]byte("test file content"))
|
||||
|
||||
g.Assert(fs.diskUsed).Equal(int64(0))
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(0))
|
||||
|
||||
err := fs.Writefile("test.txt", r)
|
||||
g.Assert(err).IsNil()
|
||||
@@ -355,7 +356,7 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
err = fs.Readfile("test.txt", buf)
|
||||
g.Assert(err).IsNil()
|
||||
g.Assert(buf.String()).Equal("test file content")
|
||||
g.Assert(fs.diskUsed).Equal(r.Size())
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(r.Size())
|
||||
})
|
||||
|
||||
g.It("can create a new file inside a nested directory with leading slash", func() {
|
||||
@@ -388,8 +389,8 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
g.Assert(errors.Is(err, ErrBadPathResolution)).IsTrue()
|
||||
})
|
||||
|
||||
g.It("cannot write a file that exceedes the disk limits", func() {
|
||||
fs.diskLimit = 1024
|
||||
g.It("cannot write a file that exceeds the disk limits", func() {
|
||||
atomic.StoreInt64(&fs.diskLimit, 1024)
|
||||
|
||||
b := make([]byte, 1025)
|
||||
_, err := rand.Read(b)
|
||||
@@ -402,8 +403,8 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
g.Assert(errors.Is(err, ErrNotEnoughDiskSpace)).IsTrue()
|
||||
})
|
||||
|
||||
g.It("updates the total space used when a file is appended to", func() {
|
||||
fs.diskUsed = 100
|
||||
/*g.It("updates the total space used when a file is appended to", func() {
|
||||
atomic.StoreInt64(&fs.diskUsed, 100)
|
||||
|
||||
b := make([]byte, 100)
|
||||
_, _ = rand.Read(b)
|
||||
@@ -411,7 +412,7 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
r := bytes.NewReader(b)
|
||||
err := fs.Writefile("test.txt", r)
|
||||
g.Assert(err).IsNil()
|
||||
g.Assert(fs.diskUsed).Equal(int64(200))
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(200))
|
||||
|
||||
// If we write less data than already exists, we should expect the total
|
||||
// disk used to be decremented.
|
||||
@@ -421,8 +422,8 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
r = bytes.NewReader(b)
|
||||
err = fs.Writefile("test.txt", r)
|
||||
g.Assert(err).IsNil()
|
||||
g.Assert(fs.diskUsed).Equal(int64(150))
|
||||
})
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(150))
|
||||
})*/
|
||||
|
||||
g.It("truncates the file when writing new contents", func() {
|
||||
r := bytes.NewReader([]byte("original data"))
|
||||
@@ -441,8 +442,9 @@ func TestFilesystem_Writefile(t *testing.T) {
|
||||
g.AfterEach(func() {
|
||||
buf.Truncate(0)
|
||||
rfs.reset()
|
||||
fs.diskUsed = 0
|
||||
fs.diskLimit = 0
|
||||
|
||||
atomic.StoreInt64(&fs.diskUsed, 0)
|
||||
atomic.StoreInt64(&fs.diskLimit, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -481,7 +483,7 @@ func TestFilesystem_CreateDirectory(t *testing.T) {
|
||||
g.It("should not increment the disk usage", func() {
|
||||
err := fs.CreateDirectory("test", "/")
|
||||
g.Assert(err).IsNil()
|
||||
g.Assert(fs.diskUsed).Equal(int64(0))
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(0))
|
||||
})
|
||||
|
||||
g.AfterEach(func() {
|
||||
@@ -597,7 +599,7 @@ func TestFilesystem_Copy(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fs.diskUsed = int64(utf8.RuneCountInString("test content"))
|
||||
atomic.StoreInt64(&fs.diskUsed, int64(utf8.RuneCountInString("test content")))
|
||||
})
|
||||
|
||||
g.It("should return an error if the source does not exist", func() {
|
||||
@@ -640,7 +642,7 @@ func TestFilesystem_Copy(t *testing.T) {
|
||||
})
|
||||
|
||||
g.It("should return an error if there is not space to copy the file", func() {
|
||||
fs.diskLimit = 2
|
||||
atomic.StoreInt64(&fs.diskLimit, 2)
|
||||
|
||||
err := fs.Copy("source.txt")
|
||||
g.Assert(err).IsNotNil()
|
||||
@@ -672,7 +674,7 @@ func TestFilesystem_Copy(t *testing.T) {
|
||||
g.Assert(err).IsNil()
|
||||
}
|
||||
|
||||
g.Assert(fs.diskUsed).Equal(int64(utf8.RuneCountInString("test content")) * 3)
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(utf8.RuneCountInString("test content")) * 3)
|
||||
})
|
||||
|
||||
g.It("should create a copy inside of a directory", func() {
|
||||
@@ -694,8 +696,9 @@ func TestFilesystem_Copy(t *testing.T) {
|
||||
|
||||
g.AfterEach(func() {
|
||||
rfs.reset()
|
||||
fs.diskUsed = 0
|
||||
fs.diskLimit = 0
|
||||
|
||||
atomic.StoreInt64(&fs.diskUsed, 0)
|
||||
atomic.StoreInt64(&fs.diskLimit, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -710,7 +713,7 @@ func TestFilesystem_Delete(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fs.diskUsed = int64(utf8.RuneCountInString("test content"))
|
||||
atomic.StoreInt64(&fs.diskUsed, int64(utf8.RuneCountInString("test content")))
|
||||
})
|
||||
|
||||
g.It("does not delete files outside the root directory", func() {
|
||||
@@ -744,7 +747,7 @@ func TestFilesystem_Delete(t *testing.T) {
|
||||
g.Assert(err).IsNotNil()
|
||||
g.Assert(errors.Is(err, os.ErrNotExist)).IsTrue()
|
||||
|
||||
g.Assert(fs.diskUsed).Equal(int64(0))
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(0))
|
||||
})
|
||||
|
||||
g.It("deletes all items inside a directory if the directory is deleted", func() {
|
||||
@@ -762,11 +765,11 @@ func TestFilesystem_Delete(t *testing.T) {
|
||||
g.Assert(err).IsNil()
|
||||
}
|
||||
|
||||
fs.diskUsed = int64(utf8.RuneCountInString("test content") * 3)
|
||||
atomic.StoreInt64(&fs.diskUsed, int64(utf8.RuneCountInString("test content")*3))
|
||||
|
||||
err = fs.Delete("foo")
|
||||
g.Assert(err).IsNil()
|
||||
g.Assert(fs.diskUsed).Equal(int64(0))
|
||||
g.Assert(atomic.LoadInt64(&fs.diskUsed)).Equal(int64(0))
|
||||
|
||||
for _, s := range sources {
|
||||
_, err = rfs.StatServerFile(s)
|
||||
@@ -777,8 +780,9 @@ func TestFilesystem_Delete(t *testing.T) {
|
||||
|
||||
g.AfterEach(func() {
|
||||
rfs.reset()
|
||||
fs.diskUsed = 0
|
||||
fs.diskLimit = 0
|
||||
|
||||
atomic.StoreInt64(&fs.diskUsed, 0)
|
||||
atomic.StoreInt64(&fs.diskLimit, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ import (
|
||||
func (s *Stat) CTime() time.Time {
|
||||
st := s.Info.Sys().(*syscall.Stat_t)
|
||||
|
||||
return time.Unix(st.Ctim.Sec, st.Ctim.Nsec)
|
||||
// Do not remove these "redundant" type-casts, they are required for 32-bit builds to work.
|
||||
return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec))
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func NewInstallationProcess(s *Server, script *api.InstallationScript) (*Install
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.installer.cancel = &cancel
|
||||
|
||||
if c, err := client.NewClientWithOpts(client.FromEnv); err != nil {
|
||||
if c, err := environment.DockerClient(); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
} else {
|
||||
proc.client = c
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/creasty/defaults"
|
||||
@@ -49,8 +50,18 @@ func LoadDirectory() error {
|
||||
data := data
|
||||
|
||||
pool.Submit(func() {
|
||||
// Parse the json.RawMessage into an expected struct value. We do this here so that a single broken
|
||||
// server does not cause the entire boot process to hang, and allows us to show more useful error
|
||||
// messaging in the output.
|
||||
d := api.ServerConfigurationResponse{}
|
||||
|
||||
log.WithField("server", uuid).Info("creating new server object from API response")
|
||||
s, err := FromConfiguration(data)
|
||||
if err := json.Unmarshal(data, &d); err != nil {
|
||||
log.WithField("server", uuid).WithField("error", err).Error("failed to parse server configuration from API response, skipping...")
|
||||
return
|
||||
}
|
||||
|
||||
s, err := FromConfiguration(d)
|
||||
if err != nil {
|
||||
log.WithField("server", uuid).WithField("error", err).Error("failed to load server, skipping...")
|
||||
return
|
||||
@@ -73,7 +84,7 @@ func LoadDirectory() error {
|
||||
// Initializes a server using a data byte array. This will be marshaled into the
|
||||
// given struct using a YAML marshaler. This will also configure the given environment
|
||||
// for a server.
|
||||
func FromConfiguration(data *api.ServerConfigurationResponse) (*Server, error) {
|
||||
func FromConfiguration(data api.ServerConfigurationResponse) (*Server, error) {
|
||||
cfg := Configuration{}
|
||||
if err := defaults.Set(&cfg); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set struct defaults for server configuration")
|
||||
|
||||
@@ -2,10 +2,8 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/apex/log"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"github.com/pterodactyl/wings/environment"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@@ -16,41 +14,17 @@ import (
|
||||
type Mount environment.Mount
|
||||
|
||||
// Returns the default container mounts for the server instance. This includes the data directory
|
||||
// for the server as well as any timezone related files if they exist on the host system so that
|
||||
// servers running within the container will use the correct time.
|
||||
// for the server. Previously this would also mount in host timezone files, however we've moved from
|
||||
// that approach to just setting `TZ=Timezone` environment values in containers which should work
|
||||
// in most scenarios.
|
||||
func (s *Server) Mounts() []environment.Mount {
|
||||
var m []environment.Mount
|
||||
|
||||
m = append(m, environment.Mount{
|
||||
m := []environment.Mount{
|
||||
{
|
||||
Default: true,
|
||||
Target: "/home/container",
|
||||
Source: s.Filesystem().Path(),
|
||||
ReadOnly: false,
|
||||
})
|
||||
|
||||
// Try to mount in /etc/localtime and /etc/timezone if they exist on the host system.
|
||||
if _, err := os.Stat("/etc/localtime"); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.WithField("error", errors.WithStack(err)).Warn("failed to stat /etc/localtime due to an error")
|
||||
}
|
||||
} else {
|
||||
m = append(m, environment.Mount{
|
||||
Target: "/etc/localtime",
|
||||
Source: "/etc/localtime",
|
||||
ReadOnly: true,
|
||||
})
|
||||
}
|
||||
|
||||
if _, err := os.Stat("/etc/timezone"); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
log.WithField("error", errors.WithStack(err)).Warn("failed to stat /etc/timezone due to an error")
|
||||
}
|
||||
} else {
|
||||
m = append(m, environment.Mount{
|
||||
Target: "/etc/timezone",
|
||||
Source: "/etc/timezone",
|
||||
ReadOnly: true,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// Also include any of this server's custom mounts when returning them.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/pterodactyl/wings/environment"
|
||||
"sync"
|
||||
)
|
||||
@@ -17,17 +18,34 @@ type ResourceUsage struct {
|
||||
// The current server status.
|
||||
State string `json:"state" default:"offline"`
|
||||
|
||||
// The current disk space being used by the server. This is cached to prevent slow lookup
|
||||
// issues on frequent refreshes.
|
||||
// The current disk space being used by the server. This value is not guaranteed to be accurate
|
||||
// at all times. It is "manually" set whenever server.Proc() is called. This is kind of just a
|
||||
// hacky solution for now to avoid passing events all over the place.
|
||||
Disk int64 `json:"disk_bytes"`
|
||||
}
|
||||
|
||||
// Alias the resource usage so that we don't infinitely recurse when marshaling the struct.
|
||||
type IResourceUsage ResourceUsage
|
||||
|
||||
// Custom marshaler to ensure that the object is locked when we're converting it to JSON in
|
||||
// order to avoid race conditions.
|
||||
func (ru *ResourceUsage) MarshalJSON() ([]byte, error) {
|
||||
ru.mu.Lock()
|
||||
defer ru.mu.Unlock()
|
||||
|
||||
return json.Marshal(IResourceUsage(*ru))
|
||||
}
|
||||
|
||||
// Returns the resource usage stats for the server instance. If the server is not running, only the
|
||||
// disk space currently used will be returned. When the server is running all of the other stats will
|
||||
// be returned.
|
||||
//
|
||||
// When a process is stopped all of the stats are zeroed out except for the disk.
|
||||
func (s *Server) Proc() *ResourceUsage {
|
||||
s.resources.SetDisk(s.Filesystem().CachedUsage())
|
||||
|
||||
// Get a read lock on the resources at this point. Don't do this before setting
|
||||
// the disk, otherwise you'll cause a deadlock.
|
||||
s.resources.mu.RLock()
|
||||
defer s.resources.mu.RUnlock()
|
||||
|
||||
@@ -35,11 +53,9 @@ func (s *Server) Proc() *ResourceUsage {
|
||||
}
|
||||
|
||||
func (s *Server) emitProcUsage() {
|
||||
s.resources.mu.RLock()
|
||||
if err := s.Events().PublishJson(StatsEvent, s.resources); err != nil {
|
||||
if err := s.Events().PublishJson(StatsEvent, s.Proc()); err != nil {
|
||||
s.Log().WithField("error", err).Warn("error while emitting server resource usage to listeners")
|
||||
}
|
||||
s.resources.mu.RUnlock()
|
||||
}
|
||||
|
||||
// Returns the servers current state.
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/apex/log"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterodactyl/wings/api"
|
||||
"github.com/pterodactyl/wings/config"
|
||||
"github.com/pterodactyl/wings/environment"
|
||||
"github.com/pterodactyl/wings/environment/docker"
|
||||
"github.com/pterodactyl/wings/events"
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"golang.org/x/sync/semaphore"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// High level definition for a server instance being controlled by Wings.
|
||||
@@ -78,10 +78,8 @@ func (s *Server) Id() string {
|
||||
// Returns all of the environment variables that should be assigned to a running
|
||||
// server instance.
|
||||
func (s *Server) GetEnvironmentVariables() []string {
|
||||
zone, _ := time.Now().In(time.Local).Zone()
|
||||
|
||||
var out = []string{
|
||||
fmt.Sprintf("TZ=%s", zone),
|
||||
fmt.Sprintf("TZ=%s", config.Get().System.Timezone),
|
||||
fmt.Sprintf("STARTUP=%s", s.Config().Invocation),
|
||||
fmt.Sprintf("SERVER_MEMORY=%d", s.MemoryLimit()),
|
||||
fmt.Sprintf("SERVER_IP=%s", s.Config().Allocations.DefaultMapping.Ip),
|
||||
@@ -90,6 +88,7 @@ func (s *Server) GetEnvironmentVariables() []string {
|
||||
|
||||
eloop:
|
||||
for k := range s.Config().EnvVars {
|
||||
// Don't allow any environment variables that we have already set above.
|
||||
for _, e := range out {
|
||||
if strings.HasPrefix(e, strings.ToUpper(k)) {
|
||||
continue eloop
|
||||
@@ -129,7 +128,7 @@ func (s *Server) Sync() error {
|
||||
return s.SyncWithConfiguration(cfg)
|
||||
}
|
||||
|
||||
func (s *Server) SyncWithConfiguration(cfg *api.ServerConfigurationResponse) error {
|
||||
func (s *Server) SyncWithConfiguration(cfg api.ServerConfigurationResponse) error {
|
||||
// Update the data structure and persist it to the disk.
|
||||
if err := s.UpdateDataStructure(cfg.Settings); err != nil {
|
||||
return errors.WithStack(err)
|
||||
@@ -148,7 +147,7 @@ func (s *Server) SyncWithConfiguration(cfg *api.ServerConfigurationResponse) err
|
||||
if e, ok := s.Environment.(*docker.Environment); ok {
|
||||
s.Log().Debug("syncing stop configuration with configured docker environment")
|
||||
e.SetImage(s.Config().Container.Image)
|
||||
e.SetStopConfiguration(&cfg.ProcessConfiguration.Stop)
|
||||
e.SetStopConfiguration(cfg.ProcessConfiguration.Stop)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -179,7 +178,7 @@ func (s *Server) CreateEnvironment() error {
|
||||
}
|
||||
|
||||
// Gets the process configuration data for the server.
|
||||
func (s *Server) GetProcessConfiguration() (*api.ServerConfigurationResponse, *api.RequestError, error) {
|
||||
func (s *Server) GetProcessConfiguration() (api.ServerConfigurationResponse, *api.RequestError, error) {
|
||||
return api.NewRequester().GetServerConfiguration(s.Id())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user