diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e67c46b..f6dc26b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -22,6 +22,20 @@ jobs: uses: hadolint/hadolint-action@v3.1.0 with: dockerfile: "Dockerfile" + failure-threshold: error + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + severity: error + scandir: './bin' + + - name: Lint Python Files Using Black + uses: psf/black@stable + with: + options: "--check --verbose" + src: "./config_from_env_vars" + version: "~= 23.0" - name: Build Docker image (Push To Cache) uses: docker/build-push-action@v3 @@ -57,10 +71,43 @@ jobs: - name: Set Execute Permission For All Test Scripts run: chmod -R +x ./tests/ - - name: Peform Dependencies Tests + - name: Dependencies Tests + run: | + ./tests/dependencies.sh + + - name: Config File Generation Tests (Core) + run: | + ./tests/config_file_generation_core.sh + + - name: Config File Generation Tests (Advanced) + run: | + ./tests/config_file_generation_adv.sh + + - name: Bin Script - system-bootstrap Tests run: | - ./tests/docker_dependencies.sh + ./tests/bin_scripts/system-bootstrap.sh + + - name: Bin Script - ark-sa-boostrap Tests + run: | + ./tests/bin_scripts/ark-sa-bootstrap.sh + + - name: Bin Script - ark-sa-updater Tests + run: | + ./tests/bin_scripts/ark-sa-updater.sh + + - name: Bin Script - ark-sa-server Tests + run: | + ./tests/bin_scripts/ark-sa-server.sh + + - name: Bin Script - ark-sa-backup Tests + run: | + ./tests/bin_scripts/ark-sa-backup.sh + + - name: Ensure Python 3.10 Is Installed For Unit Testing of config_from_env_vars + uses: actions/setup-python@v4 + with: + python-version: '3.10' - - name: Peform Environment Variable Tests + - name: Unit Tests - config_from_env_vars run: | - ./tests/docker_environment_variables.sh + python3 -m unittest tests/config_from_env_vars/test_main.py -v diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0bfce7..af49409 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -122,8 +122,8 @@ jobs: echo "The Following Images Would Have Been Pushed:" echo "${{ steps.meta-dry-run.outputs.tags }}" - - name: Update DockerHub description - if: ${{ !env.ACT }} + - name: Update DockerHub Description + if: ${{ !env.ACT && github.ref == 'refs/heads/main' }} uses: peter-evans/dockerhub-description@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} @@ -131,3 +131,8 @@ jobs: repository: johnnyknighten/ark-sa-server short-description: ${{ github.event.repository.description }} enable-url-completion: true + + - name: Update DockerHub Description (DRY RUN) + if: ${{ env.ACT && github.ref == 'refs/heads/main' }} + run: | + echo "DRY RUN - Pushing Dockerhub Description Update" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d253907 --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +# Python +# From: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 34e65d2..ab98247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,85 @@ +## [2.0.0-next.4](https://github.com/Johnny-Knighten/ark-sa-server/compare/2.0.0-next.3...2.0.0-next.4) (2023-11-30) + + +### Bug Fixes + +* added info log to list configs updated ([31f1ca5](https://github.com/Johnny-Knighten/ark-sa-server/commit/31f1ca5f70bbfb1a633b70ca3f13aaa6d10e8d10)) +* added python3 to dockerfile install and updated dependency tests ([b6db7e2](https://github.com/Johnny-Knighten/ark-sa-server/commit/b6db7e2e39f9bfa355eb36fab41f006880de2fdc)) +* caught edge case of no backups existing if it is first time creating config ([6e92fdf](https://github.com/Johnny-Knighten/ark-sa-server/commit/6e92fdf9bc6f94dbe3c0149ed64a6e62b1db6a15)) +* corrected syntax issue in ark-sa-bootstrap ([b85efc0](https://github.com/Johnny-Knighten/ark-sa-server/commit/b85efc0a5119cc5de30f04a1105891ad7850668f)) +* fixed bug that stopped new configs being updated when CONFIG_ values changed ([be8e4e3](https://github.com/Johnny-Knighten/ark-sa-server/commit/be8e4e3614b7dfdaf518de16efd4c67c9dd2b6f6)) +* integrated new python config generation into ark-sa-bootstrap ([8060e1a](https://github.com/Johnny-Knighten/ark-sa-server/commit/8060e1ae3f79017ced1c98f338bfb8193fb41c9a)) +* updated config_from_env_vars to main capitalization and remove spaces around = ([ae9e790](https://github.com/Johnny-Knighten/ark-sa-server/commit/ae9e7901437d4738caddeea2513079a394e5b5fd)) + + +### Features + +* configs are now backedup instead of overwritten, and removing CONFIG_ vars now also remove it from config ([d204ee6](https://github.com/Johnny-Knighten/ark-sa-server/commit/d204ee633d9c7cdbcbbf3cd5a12ec4c839de992d)) +* created python script to extract env vars and convert to ini files ([26745c4](https://github.com/Johnny-Knighten/ark-sa-server/commit/26745c4e4a61d5fb75c729673f47c8fe0eef888e)) +* introduced MANUAL_CONFIG to control if config file generation should be used ([3b72b1f](https://github.com/Johnny-Knighten/ark-sa-server/commit/3b72b1f0bd935a370ab8d466f5705311c94d907f)) + +## [2.0.0-next.3](https://github.com/Johnny-Knighten/ark-sa-server/compare/2.0.0-next.2...2.0.0-next.3) (2023-11-27) + + +### ⚠ BREAKING CHANGES + +* every existing container deployment will need their env var names updated + +### Code Refactoring + +* renamed all env vars to drop ARK prefix ([d77c80c](https://github.com/Johnny-Knighten/ark-sa-server/commit/d77c80ce2620ec2dbdb0224e97ab46ebf0e4be8b)) + +## [2.0.0-next.2](https://github.com/Johnny-Knighten/ark-sa-server/compare/2.0.0-next.1...2.0.0-next.2) (2023-11-27) + + +### Bug Fixes + +* added unzip to container and related test ([470e128](https://github.com/Johnny-Knighten/ark-sa-server/commit/470e128cbccde16c5dfde081ee4c7396010616cb)) + +## [2.0.0-next.1](https://github.com/Johnny-Knighten/ark-sa-server/compare/1.1.0-next.2...2.0.0-next.1) (2023-11-27) + + +### ⚠ BREAKING CHANGES + +* breaking out into multiple volumes will require data transfer + +### Features + +* added backup on container stop ([e4c704c](https://github.com/Johnny-Knighten/ark-sa-server/commit/e4c704c72f447678ef6891b8512d1c04d72f7abd)) +* added option to limit # of backups stored ([dbe7dfd](https://github.com/Johnny-Knighten/ark-sa-server/commit/dbe7dfd1bbd80d7fa234b6f4feca446f141ed72a)) +* added scheduled update cron ([51bf6a5](https://github.com/Johnny-Knighten/ark-sa-server/commit/51bf6a527f37d563ef15278e1b8ba70972be7135)) +* introduce the option to store backups as zip instead of tar.gz ([1a26e08](https://github.com/Johnny-Knighten/ark-sa-server/commit/1a26e08f24e1c39987f780a40f4feaad76b3987b)) +* introduced backups on restarts and before updates ([183629f](https://github.com/Johnny-Knighten/ark-sa-server/commit/183629f19a8ac466c3536b6050eae2bf0cc97bfe)) + +## [1.1.0-next.2](https://github.com/Johnny-Knighten/ark-sa-server/compare/1.1.0-next.1...1.1.0-next.2) (2023-11-24) + + +### Bug Fixes + +* prevent early shootergame log tail by deleteing old logs first ([0524b82](https://github.com/Johnny-Knighten/ark-sa-server/commit/0524b8279f7434b9d7924c10337395321837c343)) +* removes user/pw from supervisord conf due to launch issues ([91cf811](https://github.com/Johnny-Knighten/ark-sa-server/commit/91cf81191882de245ecaf4bd2f6f3535c38ce2fe)) + +## [1.1.0-next.1](https://github.com/Johnny-Knighten/ark-sa-server/compare/1.0.2...1.1.0-next.1) (2023-11-24) + + +### Bug Fixes + +* added -l to useradd and made PGID and PUID build args ([e121ad1](https://github.com/Johnny-Knighten/ark-sa-server/commit/e121ad1cc76a9357a154a5c8a78d5fa1aef49392)) +* added dummy user/password to prevent warning msg at boot ([86a555e](https://github.com/Johnny-Knighten/ark-sa-server/commit/86a555eded96fbd0d81e3fe4ea760c7e545a3edf)) +* converted proton env vars to build args ([84f3eaf](https://github.com/Johnny-Knighten/ark-sa-server/commit/84f3eaf6049d385d0621a7b8b27cc426e8281ca4)) +* made ark-sa-server responsible for starting ark-sa-updater ([a27c950](https://github.com/Johnny-Knighten/ark-sa-server/commit/a27c95090df20ebd23a768ea505550c1a22f79fc)) + + +### Features + +* added scheduled restarts ([16ca5f1](https://github.com/Johnny-Knighten/ark-sa-server/commit/16ca5f114fd238599cf0f1d47e78df3b236dfca2)) +* added scheduled updates ([765af0c](https://github.com/Johnny-Knighten/ark-sa-server/commit/765af0c1c4adebce3cda5214a7e50fb1df258a32)) +* added timezone config to container ([90c053e](https://github.com/Johnny-Knighten/ark-sa-server/commit/90c053e19325f510ef8c47fc8c99a9f29ecda85a)) +* created supervisord controlled process to install/update server ([b07adef](https://github.com/Johnny-Knighten/ark-sa-server/commit/b07adef7898cc5097174d9142f589ac01038056a)) +* created supervisord controlled process to start ark sa server ([acbe0e8](https://github.com/Johnny-Knighten/ark-sa-server/commit/acbe0e8825ad0472773ac787835915535455aa69)) +* installed cron inside container ([28d6d6e](https://github.com/Johnny-Knighten/ark-sa-server/commit/28d6d6e37a5c3d2bd3ce1cb4b49197f2278ac5e7)) +* now using supervisor as our process manager ([02dbc14](https://github.com/Johnny-Knighten/ark-sa-server/commit/02dbc1484bc76cbbfb9a222abf265b54b7443f61)) + ## [1.0.2](https://github.com/Johnny-Knighten/ark-sa-server/compare/1.0.1...1.0.2) (2023-11-20) diff --git a/Dockerfile b/Dockerfile index 32f4572..4991827 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,39 +2,60 @@ FROM steamcmd/steamcmd:ubuntu-22 LABEL maintainer="https://github.com/Johnny-Knighten" -ENV DEBUG=false \ - CONTAINER_BIN_DIR="/opt/ark-sa-container/bin" \ - STEAMCMD_ARK_SA_APP_ID=2430930 \ - STEAMCMD_SKIP_VALIDATION=False \ +ARG PGID=0 \ + PUID=0 \ PROTON_GE_VAR=wine-lutris \ PROTON_GE_VER=GE-Proton8-21 \ - PROTON_GE_ARCH=x86_64 \ - ARK_SERVER_DIR="/ark-server" \ - ARK_MAP="TheIsland_WP" \ - ARK_GAME_PORT=7777 \ - ARK_QUERY_PORT=27015 \ - ARK_EXTRA_LAUNCH_OPTIONS= \ - ARK_SERVER_PASSWORD= \ - ARK_SERVER_ADMIN_PASSWORD=adminpassword \ - ARK_ENABLE_RCON=True \ - ARK_RCON_PORT=27020 \ - ARK_MAX_PLAYERS=70 \ - ARK_SERVER_NAME="Ark Server" \ - ARK_PREVENT_AUTO_UPDATE=False \ - ARK_MOD_LIST= \ - ARK_NO_BATTLEYE=True \ - ARK_EPIC_PUBLIC_IP= \ - ARK_MULTI_HOME= \ - ARK_ENABLE_PVE=False\ - TEST_DRY_RUN=False \ - ARK_REBUILD_CONFIG=False + PROTON_GE_ARCH=x86_64 + +ENV DEBUG=False \ + DRY_RUN=False \ + TZ=America/New_York \ + SKIP_FILE_VALIDATION=False \ + SERVER_DIR="/ark-server/server" \ + BACKUPS_DIR="/ark-server/backups" \ + LOGS_DIR="/ark-server/logs" \ + MAP="TheIsland_WP" \ + GAME_PORT=7777 \ + QUERY_PORT=27015 \ + EXTRA_LAUNCH_OPTIONS= \ + SERVER_PASSWORD= \ + ADMIN_PASSWORD=adminpassword \ + ENABLE_RCON=True \ + RCON_PORT=27020 \ + MAX_PLAYERS=70 \ + SERVER_NAME="Ark Server" \ + UPDATE_ON_BOOT=True \ + MOD_LIST= \ + NO_BATTLEYE=True \ + EPIC_PUBLIC_IP= \ + MULTI_HOME= \ + ENABLE_PVE=False \ + SCHEDULED_RESTART=False \ + BACKUP_ON_SCHEDULED_RESTART=False \ + RESTART_CRON="0 4 * * *" \ + SCHEDULED_UPDATE=False \ + BACKUP_BEFORE_UPDATE=False \ + UPDATE_CRON="0 5 * * 0" \ + BACKUP_ON_STOP=True \ + SCHEDULED_BACKUP=False \ + BACKUP_CRON="0 6 * * *" \ + ZIP_BACKUPS=False \ + RETAIN_BACKUPS= \ + MANUAL_CONFIG=False RUN set -x && \ apt-get update && \ apt-get install --no-install-recommends -y \ wget=1.21.2-2ubuntu1 \ xz-utils=5.2.5-2ubuntu1 \ - xvfb=2:21.1.4-2ubuntu1.7~22.04.2 && \ + xvfb=2:21.1.4-2ubuntu1.7~22.04.2 \ + supervisor=4.2.1-1ubuntu1 \ + cron=3.0pl1-137ubuntu3 \ + tzdata=2023c-0ubuntu0.22.04.2 \ + zip=3.0-12build2 \ + unzip=6.0-26ubuntu3.1 \ + python3=3.10.6-1~22.04 && \ rm -rf /var/lib/apt/lists/* WORKDIR /opt/glorious_eggroll @@ -45,16 +66,26 @@ RUN PROTON_GE_FILE="$PROTON_GE_VAR-$PROTON_GE_VER-$PROTON_GE_ARCH" && \ mv "lutris-$PROTON_GE_VER-$PROTON_GE_ARCH" ./proton && \ rm -r "$PROTON_GE_FILE.tar.xz" -COPY bin/ ${CONTAINER_BIN_DIR} -RUN ["chmod", "-R", "+x", "/opt/ark-sa-container/bin"] +RUN groupadd -g "$PGID" -o ark-sa && \ + useradd -l -g "$PGID" -u "$PUID" -o --create-home ark-sa && \ + usermod -a -G crontab ark-sa + +COPY bin/ /usr/local/bin +COPY config_from_env_vars/ /usr/local/bin/config_from_env_vars +COPY supervisord.conf /usr/local/etc/supervisord.conf +RUN ["chmod", "-R", "+x", "/usr/local/bin"] + + +VOLUME [ "${SERVER_DIR}" ] +VOLUME [ "${BACKUPS_DIR}" ] +VOLUME [ "${LOGS_DIR}" ] -VOLUME [ "${ARK_SERVER_DIR}" ] -WORKDIR ${ARK_SERVER_DIR} +WORKDIR ${SERVER_DIR} EXPOSE 7777/udp EXPOSE 7778/udp EXPOSE 27015/udp EXPOSE 27020/tcp -ENTRYPOINT ["/opt/ark-sa-container/bin/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/local/bin/system-bootstrap.sh"] CMD [] diff --git a/README.md b/README.md index 4aa59b5..5d35f53 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,11 @@ [![Docker Stars](https://img.shields.io/docker/stars/johnnyknighten/ark-sa-server?logo=docker)](https://hub.docker.com/r/johnnyknighten/ark-sa-server) [![Docker Pulls](https://img.shields.io/docker/pulls/johnnyknighten/ark-sa-server?logo=docker)](https://hub.docker.com/r/johnnyknighten/ark-sa-server) -Docker container images for running an ARK Survival Ascended dedicated server. +Docker Linux container image for running an ARK Survival Ascended dedicated server. + +**Note - This container has not been tested with the Epic Game Store version of the game only the Steam version. If there are any problem please open an issue.** + +**See the [wiki](https://github.com/Johnny-Knighten/ark-sa-server/wiki) for more detailed documentation.** # Table of Contents @@ -22,29 +26,17 @@ Docker container images for running an ARK Survival Ascended dedicated server. - [Linux Host](#linux-host) - [Windows Host](#windows-host) * [System Requirements](#system-requirements) -* [Game/Server Configs](#gameserver-configs) +* [Game/Server Configs](#game/server-configs) - [Environment Variables](#environment-variables) + - [Exposed Ports](#exposed-ports) + - [Volumes](#volumes) + - [Backups](#backups) - [Config Files](#config-files) - - [Using Bind Mounts](#using-bind-mounts) - - [Using Docker Volumes](#using-docker-volumes) - - [Finding Config Settings](#finding-config-settings) - [Mods](#mods) +* [Backups](#backups) + - [Tip For Using `BACKUP_ON_STOP=True` ](#tip-for-using-backup_on_stoptrue) * [Deployment](#deployment) - - [Container Specific Deployment Details](#container-specific-deployment-details) - - [Exposed Ports](#exposed-ports) - - [Port Forwarding](#port-forwarding) - - [Volumes](#volumes) - - [Docker Run](#docker-run) - - [Docker Run + Systemd](#docker-run--systemd) - - [Docker Compose](#docker-compose) -* [Performance Testing](#performance-testing) - - [RAM](#ram) - - [CPU](#cpu) - - [Storage](#storage) -* [Automated Builds](#automated-builds) * [Tags](#tags) -* [Known Issues](#known-issues) -* [Roadmap/Future Features](#roadmapfuture-features) * [Shout Outs](#shout-outs) * [Contributing](#contributing) @@ -52,22 +44,24 @@ Docker container images for running an ARK Survival Ascended dedicated server. * Simple automated installation of ARK Survival Ascended (SA) dedicated server * Configuration via environment variables and config files -* Automatic updating of server, but can be frozen to a specific version that is already downloaded +* Scheduled server restarts and updates via Cron + * Can be frozen to a specific version that is already downloaded +* Automated backups * Automatic mod deployment, management, and updating -* Is a Linux Container that runs the Windows version of the game server via wine/proton +* Linux Container that runs the Windows version of the game server via wine/proton * Will switch to Linux game server if it is ever released ## Quick Start -It is assumed you already have Docker installed on your host machine. See [here](https://docs.docker.com/engine/install/) for instructions on how to install Docker on your host machine. +It is assumed you already have Docker installed on your host machine. See [here](https://docs.docker.com/engine/install/) for instructions on how to install Docker. The commands below will run the latest version of the ARK SA Server in a Linux container using `wine`. It will expose the default ports needed for the game server and RCON. It will also set the server name and admin password. -**Note - I do not recommend using this approach unless you just want something quick and dirty. See the [Deployment](#deployment) section for more details on how to deploy the server in a more production ready manner. I highly recommend docker compose for its simplicity.** +**Note - Using `docker run` by itself isn't recommended to host a server in the long term. See the [Deployment Examples](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Deployment-Examples) section of the wiki for more deployment options.** ### Linux Host -The ark server data in this example will be stored in the current users home directory under `/home/USER/ark-data`. +The ark server data in this example will be stored in your home directory (`/home/USERNAME/ark-data`). ```bash # written for bash, but should work in other shells @@ -78,9 +72,11 @@ $ docker run -d \ -p 7778:7778/udp \ -p 27015:27015/udp \ -p 27020:27020/tcp \ - -e ARK_SERVER_NAME="\"Simple ARK SA Server\"" \ - -e ARK_SERVER_ADMIN_PASSWORD=secretpassword \ - -v $HOME/ark-data:/ark-server \ + -e SERVER_NAME="\"Simple ARK SA Server\"" \ + -e ADMIN_PASSWORD=secretpassword \ + -v $HOME/ark-data/server:/ark-server/server \ + -v $HOME/ark-data/logs:/ark-server/logs \ + -v $HOME/ark-data/backups:/ark-server/backups \ johnnyknighten/ark-sa-server:latest ``` @@ -98,44 +94,9 @@ To stop the container: $ docker stop ark-sa-server ``` -Note - When setting `ARK_SERVER_NAME` in the docker run command, you must escape the quotes with a backslash. This is because the value is passed to the server as a command line argument and the quotes are needed to keep the name together. When using docker compose, the escaped quotes are not needed and standard quotes are good enough. - ### Windows Host -This code is written for PowerShell, but assumes you are running Docker via WSL. If you installed Docker via Docker Desktop recently then you should be good to go. The volume mount path is written in context of a Linux shell in WSL, so you will need to adjust it to match your system. - -For instance if you want to use `C:\Users\johnny\ark-data` then the WSL path should be `/mnt/c/Users/johnny/ark-data`. In general `/mnt/c` is the root of your C drive in WSL, and `/mnt/d` is the root of your D drive in WSL and so on. - -```powershell -# written for powershell, but should work in other shells -docker run -d ` - --name ark-sa-server ` - -p 8888:8888/udp ` - -p 8889:8889/udp ` - -p 27016:27016/udp ` - -p 27021:27021/tcp ` - -e ARK_SERVER_NAME="Simple ARK SA Server" ` - -e ARK_SERVER_ADMIN_PASSWORD=secretpassword ` - -e ARK_GAME_PORT=8888 ` - -e ARK_QUERY_PORT=27016 ` - -e ARK_RCON_PORT=27021 ` - -v /mnt/c/Users/USER/ark-data:/ark-server ` - johnnyknighten/ark-sa-server:latest -``` - -To view the container logs: - -```bash -docker logs ark-sa-server -f -``` - -Press `CTRL+C` to exit the logs output. - -To stop the container: - -```bash -docker stop ark-sa-server -``` +See the [Windows Host](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Quick-Start#windows-host) section in the Quick Starts page of the wiki for details. ## System Requirements @@ -146,131 +107,54 @@ General Minimum Spec Recommendation (For Server Only): * 4 Cores Modern CPU * 20GB of Storage (SSD Recommended) -Note - Treat these as suggestions rather than hard rules. These recommendations are based on informal performance testing with some metrics collected over time. See the [Performance Testing](#performance-testing) section for more details. +Note - Treat these as suggestions rather than hard rules. These recommendations are based on informal performance testing with some metrics collected over time. See the [Performance Testing](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Performance-Testing) section in the wiki for more details. -## Game/Server Configs +## Server/Game Configs ### Environment Variables -Environment variables are the primary way to configure the server itself. For configurations such as XP/Gathering/Taming rates and player stats, you will need to use config files. See the [Config Files](#config-files) section for more details. +Environment variables are the primary way to configure the server itself. For game configurations such as XP/Gathering/Taming rates and player stats, you will need to use config files. See the [Config Files](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Config-Files) section in the wiki for more details. The table below shows all the available environment variables and their default values. | Variable | Description | Default | | --- | --- | :---: | -| `STEAMCMD_SKIP_VALIDATION` | Skips SteamCMD validation of the server files. Can speed up server start time, but could risk not detecting corrupted files. | `False` | -| `ARK_PREVENT_AUTO_UPDATE` | Prevents the server from automatically updating. If you do need to update after having this set to `True`, then flip the Value to `False` then restart the container. Afterwards, set it back to `True` and restart again to prevent any further updates. | `False` | -| `ARK_SERVER_NAME` | Name of the server that appears in the server list. If the name contains a space wrap the name in quotes, depending on your system you may need to add escaped quotes `\"`. | `"ARK SA Server"` | -| `ARK_SERVER_PASSWORD` | Password to login to the server. Defaults to no password aka a public server. **Do not put spaces in your password.** | EMPTY | -| `ARK_SERVER_ADMIN_PASSWORD `| Password for the server admin. Also used for RCON access. **Do not put spaces in your password.** | `adminpassword` | -| `ARK_GAME_PORT` | Primary game port. This port +1 will also be used. | `7777` | -| `ARK_QUERY_PORT` | Steam query port. | `27015` | -| `ARK_ENABLE_RCON` | Enable RCON on the server. | `True` | -| `ARK_RCON_PORT`| RCON port for the server. | `27020` | -| `ARK_MAP` | Map launched on the server. | `TheIsland_WP` | -| `ARK_MAX_PLAYERS`| Maximum number of players allowed on the server. | `70` | -| `ARK_ENABLE_PVE`| Enable PvE mode, otherwise it is a PvPvE. |`False`| -| `ARK_NO_BATTLEYE` | Disables BattlEye on the server. | `True` | -| `ARK_EXTRA_LAUNCH_OPTIONS` | Extra launch options for the server. Allows additional flags that do not have an environment variable provided yet. | EMPTY | -| `ARK_MOD_LIST` | Comma separated list of mod ids to install. Needs to be wrapped in quotes and whitespace can appear before or after commas. | EMPTY | -| `ARK_EPIC_PUBLIC_IP` | Public IP address of the server, used by Epic game clients. | EMPTY | -| `ARK_MULTI_HOME_SERVER` | Provide your public IP address when hosting multiple servers on the same machine. | EMPTY | -| `ARK_REBUILD_CONFIG` | Force rebuild of GameUserSettings.ini on container start. Mainly used to force updated variables to appear in GameUserSettings.ini. *Note - this overwrites your current GameUserSettings.ini, so make a copy if needed.*| `False` | - -### Config Files - -Configuration files are primarily used to adjust settings such as such as XP/Gathering/Taming rates and player stats. The config files are located in the `/ark-server/ShooterGame/Saved/Config/WindowsServer` directory inside the container and the primary file you will modify is `GameUserSettings.ini`. - -#### New GameUserSettings.ini Template Process (11/19/2023) - -As of 11/19/2023 the way `GameUserSettings.ini` is generated in this container has changed. Somewhere around server version 26.38 of the dedicated server, a number of server launch command args stopped working. To work around this, the only option was to ensure those cmd args appeared in `GameUserSettings.ini`. A long term goal of this project is to make `GameUserSettings.ini` 100% configurable via Environment Variables, but since this issue impacted the current use of the image, a quick fix was needed. The fix is in the form of a simple `GameUserSettings.ini` template script that injects the cmd args environment variables into a `GameUserSettings.ini` template. See [bin/ark-sa/configs](bin/ark-sa/configs) for more implementation details. - -The only environment variables impacted by this change are: `ARK_SERVER_NAME`, `ARK_ENABLE_RCON`, `ARK_RCON_PORT`, `ARK_MAX_PLAYERS`, `ARK_SERVER_PASSWORD`, `ARK_SERVER_ADMIN_PASSWORD`, and `ARK_ENABLE_PVE`. - -If you make any changes to the above environment variables, you will need to restart the container with `ARK_REBUILD_CONFIG=True` to force the template script to run again. This will overwrite your GameUserSettings.ini and make a new one with the values in [`GameUserSettings.template.ini`](ark-sa/config-templating/GameUserSettings.template.ini) and inject it with your new environment variable values. Make a copy of your existing config if needed and update the newly generated `GameUserSettings.ini` with your old values. Another alternative is to manually update the `GameUserSettings.ini` file to match your new environment variable values and then restart the container. - -Remember to set `ARK_REBUILD_CONFIG=False` (its default value) unless you want `GameUserSettings.ini` to be rewritten with the template every container launch (not recommended). This will make `GameUserSettings.ini` always match the values in [`GameUserSettings.template.ini`](ark-sa/config-templating/GameUserSettings.template.ini). - -##### New Users - -For new users of this image, there is nothing you need to do. The new template script will execute in your first launch of your server. - -##### Existing users - -Make a copy of your existing `GameUserSettings.ini` then set `ARK_REBUILD_CONFIG=True` and restart your container. This will overwrite your current `GameUserSettings.ini` with the values in [`GameUserSettings.template.ini`](ark-sa/config-templating/GameUserSettings.template.ini). Then copy any of your old settings into the new `GameUserSettings.ini` and restart the container with `ARK_REBUILD_CONFIG=False`. - -#### Using Bind Mounts - -If you use a bind mount to attach to /ark-server, you can modify the config files directly on the host. - -**I recommend this approach for those not experienced with Docker. It will allow you to modify files directly with applications such as notepad.** - -Bind mounts are when you mount a directory from your host and map it to a directory inside of the container. From a Windows perspective, this is like picking a folder on your computer to store you server data and mapping that folder into the container. - -Below is an example of using a bind mount with docker run. This is mapping the `C:\Users\johnny\ark-data ` folder to the containers /ark-server directory: - -```bash -$ docker run -d \ - ... - -v /mnt/c/Users/johnny/ark-data:/ark-server \ - ... -``` - -In docker compose it would look like: -```yaml ---- -version: '3' -services: - ark-sa: - container_name: ark - image: johnnyknighten/ark-sa-server:latest - ... - volumes: - - '/mnt/c/Users/johnny/ark-data:/ark-server' - ... -``` -#### Using Docker Volumes - -When using Docker Volumes you have a few options: -* If using Docker Desktop - * You can use the Docker Desktop UI to manage the volume - * Find the container then go to the files section and modify files from there (right click Edit File) -* If using Bash/Powershell - * You will need to copy the config files out of the volume, make your changes, and then copy them back in (will not show an example of this) - * Another alternative for docker volume usage is to open a bash shell in the container while it is running, install a text editor (current builds don't have them preinstalled), modify the files from there, and then restart the container. - -Here is an example of installing nano in the container and modifying the config file as it runs: - -```bash -# Open a bash shell in the container (below assumes a single docker container for ARK SA is running) otherwise use `docker ps` to get the container id -$ docker exec -it $(docker ps -q --filter "ancestor=ark-sa-server") bash - -# Now your inside the container -root@CONTAINER_ID:/ark-server$ apt-get update && apt-get install nano -root@CONTAINER_ID:/ark-server$ nano ./server/ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini -root@CONTAINER_ID:/ark-server$ exit - -# Now restart the container -$ docker restart $(docker ps -q --filter "ancestor=ark-sa-server") -``` - -#### Finding Config Settings You Want To Change - -An easy way to find what config files you want to modify/set is to start a single player ARK SA game and configure it as you would want your dedicated server to be setup. Then go to `PATH_TO_STEAM\Steam\steamapps\common\ARK Survival Ascended\ShooterGame\Saved\Config\Windows` and take a look through the .ini files in that directory. The main file of interest will be `GameUserSettings.ini`. You can also look at the other .ini files to see what other settings are available to you. Then either copy entire ini files into your server config folder or just copy the specific settings you want to change. - -Note - Some of the environment variable settings will also be copied into the config files, such as the server name and passwords. If you manually change these fields in the config file, make sure to also update the environment variables to match otherwise you may experience issues. - -### Mods - -Mods are handled via the `ARK_MOD_LIST` environment variable. The variable is a comma separated list of mod ids to install. The mod ids list need to be wrapped in quotes, and white space is allowed before/after commas since all whitespace in the quotes will be removed. Right now, if you are lucky, the mod author will put the id in there mod description. An alternative way to get mod ids is by installing them on your local machine then going to `PATH_TO_STEAM\Steam\steamapps\common\ARK Survival Ascended\ShooterGame\Binaries\Win64\ShooterGame\Mods\RANDOM#` and look at the subdirectories' name. The first number before the `_` in the subdirectories name is the mod id. - -## Deployment - -### Container Specific Deployment Details - -#### Exposed Ports - -The table below shows the default ports that are exposed by the container. These can be changed by setting the environment variables `ARK_GAME_PORT`, `ARK_QUERY_PORT`, and `ARK_RCON_PORT`. +| `SKIP_FILE_VALIDATION` | Skips SteamCMD validation of the server files. Can speed up server start time, but could risk not detecting corrupted files. | `False` | +| `TZ` | Sets the timezone of the container. See the table [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) and look in the TZ identifier column. Highly recommend to set this if you will be using any of the CRON variables. | `America/New_York` | +| `MANUAL_CONFIG` | If set to `True` then the container will not generate any config files. This is useful if you want to manage the config files yourself. | `False` | +| `SCHEDULED_RESTART` | Enable scheduled restarts of the server. | `False` | +| `BACKUP_ON_SCHEDULED_RESTART` | Determines if the server should backup itself before restarting. | `False` | +| `RESTART_CRON` | Cron expression for scheduled restarts. Default is everyday at 4am. | `0 4 * * *` | +| `SCHEDULED_UPDATE` | Enable scheduled updates of the server. | `False` | +| `UPDATE_CRON` | Cron expression for scheduled updates. Default is every Sunday at 5am. | `0 5 * * 0` | +| `BACKUP_BEFORE_UPDATE` | Determines if the server should backup itself before updating. | `False` | +| `UPDATE_ON_BOOT` | Determines if the server should update itself when it starts. If this is set to `False` then the server will only update if `SCHEDULED_UPDATE=True`, then it will update on the schedule specified by `UPDATE_CRON`. | `True` | +| `SCHEDULED_BACKUP` | Enable scheduled backups of the server. | `False` | +| `BACKUP_CRON` | Cron expression for scheduled backups. Default is every day at 6am. | `0 6 * * *` | +| `BACKUP_ON_STOP` | Determines if the server should backup itself when the container stops. | `True` | +| `ZIP_BACKUPS` | If this is set to `True` then it will zip your backups instead of the default tar and gzip. | `False` | +| `RETAIN_BACKUPS` | Number of backups to keep. If not set, then an unlimited number of backs will be kept. | EMPTY | +| `SERVER_NAME` | Name of the server that appears in the server list. If the name contains a space wrap the name in quotes, depending on your system you may need to add escaped quotes `\"`. | `"ARK SA Server"` | +| `SERVER_PASSWORD` | Password to login to the server. Defaults to no password aka a public server. **Do not put spaces in your password.** | EMPTY | +| `ADMIN_PASSWORD `| Password for the server admin. Also used for RCON access. **Do not put spaces in your password.** | `adminpassword` | +| `GAME_PORT` | Primary game port. This port +1 will also be used. | `7777` | +| `QUERY_PORT` | Steam query port. | `27015` | +| `ENABLE_RCON` | Enable RCON on the server. | `True` | +| `RCON_PORT`| RCON port for the server. | `27020` | +| `MAP` | Map launched on the server. | `TheIsland_WP` | +| `MAX_PLAYERS`| Maximum number of players allowed on the server. | `70` | +| `ENABLE_PVE`| Enable PvE mode, otherwise it is a PvPvE. |`False`| +| `NO_BATTLEYE` | Disables BattlEye on the server. | `True` | +| `EXTRA_LAUNCH_OPTIONS` | Extra launch options for the server. Allows additional flags that do not have an environment variable provided yet. | EMPTY | +| `MOD_LIST` | Comma separated list of mod ids to install. Needs to be wrapped in quotes and whitespace can appear before or after commas. | EMPTY | +| `EPIC_PUBLIC_IP` | Public IP address of the server, used by Epic game clients. | EMPTY | +| `MULTI_HOME_SERVER` | Provide your public IP address when hosting multiple servers on the same machine. | EMPTY | + +**Note - If you are new to CRON, check here to get help understanding the syntax: [crontab guru](https://crontab.guru/).** + +### Exposed Ports + +The table below shows the default ports that are exposed by the container. These can be changed by setting the environment variables `GAME_PORT`, `QUERY_PORT`, and `RCON_PORT`. | Port | Protocol | Description | | :---: | :---: | --- | @@ -279,121 +163,74 @@ The table below shows the default ports that are exposed by the container. These | 27015 | UDP | Steam query port | | 27020 | TCP | RCON Port | +Make sure you have Port Forwarding configured otherwise the server will not be accessible from the internet. See the [Port Forwarding](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Deployment-Examples#port-forwarding) section int he wiki for more details. + Note - Always ensure that your `-p` port mappings if using docker run and the `ports` section of your docker compose match up to the ports specified via the environment variables. If they do not match up, the server will not be accessible. -##### Port Forwarding +### Volumes -Regardless if you use the default ports or specify custom ones, you must setup port forwarding on your router to allow incoming connections to the server. You may also need to setup firewall rules on your host machine to allow incoming connections to the server. +There are thee volumes used by the container -This [guide](https://www.lifewire.com/how-to-port-forward-4163829) is a very high level overview of port forwarding and may be a good starting point if you are new to port forwarding. +| Volume | Description | +| --- | --- | +| /ark-server/server | Contains server and mods files | +| /ark-server/logs | Contains all log files generated by the container | +| /ark-server/backups | Contains all automated backups | -There are too many different routers and firewall setups to provide a single guide for port forwarding. If you need help with port forwarding, I would recommend searching for a guide for your specific router and setup. +### Backups -**Make sure you understand the security implications of opening ports on your router and firewall. If you are unsure, I would recommend buying hosting instead of taking the risk. When opening ports there is always a chance you could end up exposing your computer to bad actors("hackers").** +Backups can be performed automatically if configured. Backups are performed by making a copy the `/ark-server/server/ShooterGame/Saved` directory to the `/ark-server/backups` volume. The backups are named using the following format: `server-backup-{datetime}`. They are compressed as `tar.gz` files by default(can be set to zip via `ZIP_BACKUPS=True`) and are stored in the `/ark-server/backups` volume. You can configure the number of backups to keep using `RETAIN_BACKUPS`, otherwise you will need to manually delete old backups. -#### Volumes +Backup Automation Options +* `BACKUP_ON_SCHEDULED_RESTART` - Backup the server before a scheduled restart +* `BACKUP_BEFORE_UPDATE` - Backup the server before an update +* `BACKUP_ON_STOP` - Backup the server when the container stops +* `SCHEDULED_BACKUP` - Backup the server on a schedule -Right now only one volume is used by the image. This volume is used to store the server data and mods. +**If you are using `BACKUP_ON_STOP=True`, it is highly recommended you adjust the timeout settings of your `docker run/stop/compose` command to allow the backup process enough time to complete its backup. Without doing this, it is likely your backup will be unfinished and corrupt. See the [Backup On Container Stop - Docker Timeout Considerations](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Backups#backup-on-container-stop---docker-timeout-considerations) section of the wiki for more details.** -| Volume | Description | -| --- | --- | -| /ark-server | The directory where the server/mod data is stored | +If desired, you can also manually trigger a backup. See the [Manual Backup](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Backups#manual-backup) section of the wiki. -### Docker Run +#### Restoring Backups -Docker run is mainly good for one off deployments or testing. It is not recommended for production deployments by itself, but combined with something like systemd it can be made production ready. +See the [Restoring Backups](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Backups#restoring-backups) section of the wiki for details. -Below is an example of starting an instance of the ARK SA Server using docker run that has more configuration options than the quick start example. +### Config Files -```bash -# written for bash, but should work in other shells -$ docker run -d \ - --name ark-sa-server \ - -p 8888:8888/udp \ - -p 8889:8889/udp \ - -p 27016:27016/udp \ - -p 27021:27021/tcp \ - -v ark-sa-server:/ark-server \ - -e ARK_SERVER_NAME="\"Simple ARK SA Server\"" \ - -e ARK_GAME_PORT=8888 \ - -e ARK_QUERY_PORT=27016 \ - -e ARK_MAX_PLAYERS=20 \ - -e ARK_SERVER_PASSWORD=password2 \ - -e ARK_SERVER_ADMIN_PASSWORD=adminpassword2 \ - -e ARK_ENABLE_PVE=True \ - -e STEAMCMD_SKIP_VALIDATION=True \ - -e ARK_PREVENT_AUTO_UPDATE=True \ - -e ARK_RCON_ENABLED=True \ - -e ARK_RCON_PORT=27021 \ - -e ARK_MOD_LIST="\"927131, 893657\"" \ - johnnyknighten/ark-sa-server:1.0.0 -``` +Configuration files are primarily used to adjust settings such as such as XP/Gathering/Taming rates and player stats. The config files are located in the `/ark-server/server/ShooterGame/Saved/Config/WindowsServer` directory inside the container and the primary file you will modify is `GameUserSettings.ini`. -### Docker Run + Systemd - -See [deployment-examples\docker-run-with-systemd](deployment-examples\docker-run-with-systemd) details about using docker run with Systemd to run the server as service on a Linux server(that uses Systemd). - -### Docker Compose - -Below is a minimal example of starting an instance of the ARK SA Server using docker compose. See the [deployment-examples\docker-compose](deployment-examples\docker-compose) folder for more docker compose examples. - -```yaml ---- -version: '3' -services: - ark-sa: - container_name: ark - image: johnnyknighten/ark-sa-server:latest - restart: unless-stopped - environment: - - ARK_SERVER_NAME="Simple ARK SA Server" - - ARK_SERVER_ADMIN_PASSWORD=secretpassword - volumes: - - 'ark-files:/ark-server' - ports: - - 7777:7777/udp - - 7778:7778/udp - - 27015:27015/udp - - 27020:27020/tcp -volumes: - ark-files: -``` +This container has two primary ways to manage config files. +* [Environment Variables](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Config-Files#managing-config-files-via-environment-variables) - Recommended +* [Manually](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Config-Files#managing-the-config-files-manually) -## Performance Testing +You should not mix and match these methods (it is possible but you need to understand how both are handled inside the container). If you wish to manage the config files manually, you must set `MANUAL_CONFIG=True` to prevent the container from generating/overwriting any config files. -Currently the image has been tested with these two setups: +Despite setting `MANUAL_CONFIG=True`, if the `GameUserSettings.ini` file is missing a minimal config will be generated using the following variables: +* `SERVER_NAME` +* `SERVER_PASSWORD` +* `ADMIN_PASSWORD` +* `MAX_PLAYERS` +* `ENABLE_PVE` +* `ENABLE_RCON` +* `RCON_PORT` -* Setup 1 - * Windows 11 Pro - * CPU - AMD Ryzen 9 3950X - * RAM - 64GB DDR4 @ 3200Mhz - * Storage - 2TB PCIe 3.0 NVMe SSD -* Setup 2 - * ProxMox Host - * Test VM Ubuntu 22.04 - * CPU - 2x Intel Xeon Gold 6146 - * Test VM Assigned 4 Host Cores - * RAM - 768GB DDR4 @ 3200Mhz - * Test VM Assigned 32GB - * VM Storage - 8 x 512GB SATA SSDs in - * 4 Mirrored VDEVs That Are Then Striped (2TB Usable) - * Test VM Assigned 60GB +For more info see the [Config Files](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Config-Files) wiki page. -### RAM +### Mods -In both setups an empty server (no players) consumes about 12GB of RAM. With about 6 players (with only 20hrs played - so minimal number of buildings/bases/tamed dinos) I saw RAM usage float around 14GB. For a small private server I think 16GB of RAM would be a good starting place. For a larger server with more players and more time played, I could easily see RAM usage creep up to 20-30+GB. +Mods are handled via the `MOD_LIST` environment variable. The variable is a comma separated list of mod ids to install. The mod ids list need to be wrapped in quotes, and white space is allowed before/after commas since all whitespace in the quotes will be removed. Right now, if you are lucky, the mod author will put the id in there mod description. An alternative way to get mod ids is by installing them on your local machine then going to `PATH_TO_STEAM\Steam\steamapps\common\ARK Survival Ascended\ShooterGame\Binaries\Win64\ShooterGame\Mods\RANDOM#` and look at the subdirectories' name. The first number before the `_` in the subdirectories name is the mod id. -### CPU +## Backups -In terms of CPU I didn't see any major CPU usage besides the initial server setup/launch. I would assume a modern CPU with 4 cores should be good enough for a small private server. I cant truly project what large servers would need with the testing performed so far. From the research I have done Ark Survival Evolved benefited from a higher CPU frequency rather than more cores, so I assume the same will be true for Ark Survival Ascended. +There are several different ways to perform backups using this container. See the [Backups](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Backups) section of the wiki for exact details. -### Storage +### Tip For Using `BACKUP_ON_STOP=True` -Storage wise the wine based image is roughly 1.64GB and the docker volume created is about 9.1GB without any mods or backups. I have only tested on SATA and PCIe 3.0 NVMEs SSD drives, so I cant speak to the performance of spinning rust. In my testing there were no noticeable performance difference between SATA and NVME SSDs. Be prepared to use about 12GB of storage minimum, and plan for more for future server updates, mods, and backups. +If you are planning on using `BACKUP_ON_STOP=True`, it is highly recommended you adjust the timeout settings of your `docker stop/compose down` command to allow the backup process enough time to complete its backup. Without doing this, it is likely your backup will be unfinished and corrupt. The longer your server has been running the bigger your backup will become which increases the time needed to backup the server. See the [Backup On Container Stop - Docker Timeout Considerations](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Backups#backup-on-container-stop---docker-timeout-considerations) section of the wiki for more details. -## Automated Builds +## Deployment -Automated builds are made upon successful Pull Requests merges on the `main` and `next` branches via GitHub Actions. Container images are published to [Docker Hub](https://hub.docker.com/r/johnnyknighten/ark-sa-server). +See the [Deployment Examples](https://github.com/Johnny-Knighten/ark-sa-server/wiki/Deployment-Examples) section of the wiki for more deployment option examples. ## Tags @@ -412,37 +249,6 @@ Currently the only execution environment is `wine`, which runs the Windows versi There are also pre-release tags that are built from the `next` branch. These are used for testing and are not recommended for production use. -## Known Issues - -* If booting the container with an existing volume/bind mount the original ShooterGame.log will be read initially and displayed before it is converted to a backup log (ShooterGame-backup-DATETIMESTAMP.log). This can be misleading because it will show old server launch configs before the file is truncated and the fresh ShooterGame.log is created. - -## Roadmap/Future Features - -* Core Features - * Automated Server Restarts - * Automated Server Backups - * Windows Container (Paused See [Windows Container](#windows-container-paused) Below) - * Linux Container Running Linux Server (if ever released) -* Additional Features - * CLI Tool For Simple Server Management via RCON - * Event Hooks To Allow For Custom Scripts Execution On Server Events - * Such As: - * Server Pre/Post Start - * Server Pre/Post Restart - * Server Pre/Post Backup - * ... - * Discord Integration - * Matrix Integration -* Documentation - * Other Deployment Methods - * Kubernetes - * AWS ECS - * AWS Lightsail - -## Windows Container (Paused) - -There is a WIP branch of this project that contains a Windows container for running the Windows version of the game server. For now its development is paused to focus on more impactful features; the container builds and runs the server, but there are some issues with the server not being accessible(current belief is that it is a networking issue). If anyone wants to take a stab at fixing it, feel free to submit a PR. You can find it in the [wip/windows-container](https://github.com/Johnny-Knighten/ark-sa-server/tree/wip/windows-container) branch, check the README in the windows directory. - ## Shout Outs * GloryousEggroll - For their [wine-ge-custom](https://github.com/GloriousEggroll/wine-ge-custom) used in this image diff --git a/bin/ark-sa-backup.sh b/bin/ark-sa-backup.sh new file mode 100644 index 0000000..ab6c5a8 --- /dev/null +++ b/bin/ark-sa-backup.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +set -e + +[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x + +echo "Backup Process - Starting" + +SCRIPT_PARAM="$1" + +main() { + clean_up_backups_dir + backup_ark_sa_server + start_ark_sa_server + start_ark_sa_update +} + +start_ark_sa_server() { + if [[ "$SCRIPT_PARAM" = "restart" ]]; then + echo "Backup Process - Starting Ark SA Server After Backup" + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - supervisorctl start ark-sa-server" + else + supervisorctl start ark-sa-server + fi + fi +} + +start_ark_sa_update() { + if [[ "$SCRIPT_PARAM" = "update" ]]; then + echo "Backup Process - Starting Ark SA Update After Backup" + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - supervisorctl start ark-sa-updater" + else + supervisorctl start ark-sa-updater + fi + fi +} + +zip_backup() { +if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - zip -r \"${BACKUPS_DIR}/ark-sa-server-$(date +%Y%m%d%H%M%S).zip\" \"${SERVER_DIR}/ShooterGame/Saved\"" + else + zip -r "${BACKUPS_DIR}/ark-sa-server-$(date +%Y%m%d%H%M%S).zip" "${SERVER_DIR}/ShooterGame/Saved" + fi +} + +tar_gz_backup() { + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - tar -czf \"${BACKUPS_DIR}/server-backup-$(date +%Y%m%d%H%M%S).tar.gz\" \"${SERVER_DIR}/ShooterGame/Saved\"" + else + tar -czf "${BACKUPS_DIR}"/ark-sa-server-$(date +%Y%m%d%H%M%S).tar.gz "${SERVER_DIR}"/ShooterGame/Saved + fi +} + +backup_ark_sa_server() { + echo "Backup Process - Backing Up Ark SA Server" + if [[ "$ZIP_BACKUPS" = "True" ]]; then + zip_backup + else + tar_gz_backup + fi + echo "Backup Process - Backup Finished" +} + +clean_up_backups_dir() { + if [[ -n "$RETAIN_BACKUPS" ]]; then + echo "Backup Process - Cleaning Up Backup Directory" + count_files() { + find "$BACKUPS_DIR" -type f | wc -l + } + + delete_oldest_file() { + find "$BACKUPS_DIR" -type f -print0 | xargs -0 ls -tr | head -n 1 | xargs rm -f + } + + current_file_count=$(count_files) + + while [ "$current_file_count" -gt "$((RETAIN_BACKUPS - 1))" ]; do + echo "Backup Process - File Limit Exceded: ($current_file_count), Deleting Oldest" + delete_oldest_file + current_file_count=$(count_files) + done + + echo "Backup Process - File Count Post Cleanup: $(count_files)" + fi +} + +main diff --git a/bin/ark-sa-bootstrap.sh b/bin/ark-sa-bootstrap.sh new file mode 100644 index 0000000..b128d9f --- /dev/null +++ b/bin/ark-sa-bootstrap.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +set -e + +[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x + +echo "ARK SA Bootstrap - Starting" + +main() { + generate_config_files + check_if_server_files_exist + auto_update_server + launch_ark_sa_server +} + +launch_ark_sa_server() { + echo "ARK SA Bootstrap - Launching Ark SA Server" + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - supervisorctl start ark-sa-server" + else + supervisorctl start ark-sa-server + fi +} + +launch_update_service() { + echo "ARK SA Bootstrap - Launching Updater Service" + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - supervisorctl start ark-sa-updater" + else + supervisorctl start ark-sa-updater + fi + exit 0 +} + +generate_config_files() { + mkdir -p "${SERVER_DIR}/ShooterGame/Saved/Config/WindowsServer" + + if [[ ! -f "${SERVER_DIR}/ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini" || "$MANUAL_CONFIG" != "True" ]]; then + echo "ARK SA Bootstrap - Generating GameUserSettings.ini" + + export CONFIG_GameUserSettings_SessionSettings_SessionName="${SERVER_NAME}" + export CONFIG_GameUserSettings_ServerSettings_RCONEnabled="${ENABLE_RCON}" + export CONFIG_GameUserSettings_ServerSettings_RCONPort="${RCON_PORT}" + export CONFIG_GameUserSettings_SLASH_Script_SLASH_Engine_DOT_GameSession_MaxPlayers="${MAX_PLAYERS}" + export CONFIG_GameUserSettings_ServerSettings_ServerPassword="${SERVER_PASSWORD}" + export CONFIG_GameUserSettings_ServerSettings_ServerAdminPassword="${ADMIN_PASSWORD}" + export CONFIG_GameUserSettings_ServerSettings_ServerPVE="${ENABLE_PVE}" + + python3 /usr/local/bin/config_from_env_vars/main.py --path "${SERVER_DIR}/ShooterGame/Saved/Config/WindowsServer" + else + echo "ARK SA Bootstrap - Skipping GameUserSettings.ini Generation MANAUL_CONFIG is True" + fi +} + +# -eq 1 below because we assume the single config file is generate at this point +check_if_server_files_exist() { + if [ "$(find "$SERVER_DIR" -mindepth 1 -maxdepth 1 | wc -l)" -eq 1 ]; then + echo "ARK SA Bootstrap - No Server Files Found, Downloading Server" + launch_update_service + fi +} + +auto_update_server() { + if [ "$UPDATE_ON_BOOT" = "True" ]; then + echo "ARK SA Bootstrap - Update On Boot Enabled" + launch_update_service + else + echo "ARK SA Bootstrap - Update On Boot Disabled, Skipping" + fi +} + +main diff --git a/bin/ark-sa-server.sh b/bin/ark-sa-server.sh new file mode 100644 index 0000000..38b318c --- /dev/null +++ b/bin/ark-sa-server.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +set -e + +[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x + + +trap cleanup SIGTERM SIGINT + +start_time=$(date +%s) +echo "Ark Server - Starting at $start_time" + +main() { + handle_old_shooter_log + start_server +} + +handle_old_shooter_log() { + local shooter_log="${SERVER_DIR}/ShooterGame/Saved/Logs/ShooterGame.log" + if [[ -f "$shooter_log" ]]; then + echo "Ark Server - Deleting Old Shooter Logs" + rm "$shooter_log" + fi +} + +start_server() { + local cmd_args="$MAP?listen" + cmd_args+="?Port=$GAME_PORT" + cmd_args+="?QueryPort=$QUERY_PORT" + + if [[ -n "$MULTI_HOME" ]]; then + cmd_args+="?MultiHome=$MULTI_HOME" + fi + + local launch_flags="-log" + + if [[ "$NO_BATTLEYE" = "True" ]]; then + launch_flags+=" -NoBattlEye" + fi + + if [[ -n "$EPIC_PUBLIC_IP" ]]; then + launch_flags+=" --PublicIPforEpic $EPIC_PUBLIC_IP" + fi + + launch_flags+=" -WinLiveMaxPlayers=$MAX_PLAYERS" + + MOD_LIST="$(echo -e "$MOD_LIST" | tr -d '[:space:]')" + if [[ -n "$MOD_LIST" ]]; then + launch_flags+=" -automanagedmods -mods=$MOD_LIST" + fi + + launch_flags+=" $EXTRA_LAUNCH_OPTIONS" + + local proton_wine=/opt/glorious_eggroll/proton/bin/wine + local proton_wine_log=/ark-server/logs/proton-wine.log + local server_exe=/ark-server/server/ShooterGame/Binaries/Win64/ArkAscendedServer.exe + + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - $proton_wine $server_exe \"$cmd_args\" $launch_flags" + return + fi + + xvfb-run $proton_wine $server_exe "$cmd_args" $launch_flags &> $proton_wine_log & + + local log_file="${SERVER_DIR}/ShooterGame/Saved/Logs/ShooterGame.log" + local timeout=300 + + while [ ! -f "$log_file" ] && [ $timeout -gt 0 ]; do + echo "Ark Server - Waiting for File $log_file to Exist" + sleep 5 + ((timeout--)) + done + + if [ -f "$log_file" ]; then + tail -f "$log_file" + else + echo "Ark Server - File $log_file Did Not Appear Within the Timeout Period" + exit 1 + fi +} + +cleanup() { + echo "Ark Server - Cleaning up before stopping..." + /opt/glorious_eggroll/proton/bin/wineserver -k + echo "Ark Server - Cleanup complete." +} + +main diff --git a/bin/ark-sa-updater.sh b/bin/ark-sa-updater.sh new file mode 100644 index 0000000..1f502f0 --- /dev/null +++ b/bin/ark-sa-updater.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e + +[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x + +echo "Updater - Starting" + +main() { + download_and_update_ark_sa_server + launch_ark_sa_server +} + +launch_ark_sa_server() { + echo " Updater - Launching Ark SA Server" + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - supervisorctl start ark-sa-server." + else + supervisorctl start ark-sa-server + fi +} + +download_and_update_ark_sa_server() { + if [ "$SKIP_FILE_VALIDATION" = "True" ]; then + echo "Updater - Skipping SteamCMD Validation of Server Files" + local app_update="+app_update 2430930" + else + local app_update="+app_update 2430930 validate" + fi + + local install_dir="+force_install_dir $SERVER_DIR" + + if [[ "$DRY_RUN" = "True" ]]; then + echo "$DRY_RUN_MSG steamcmd +login anonymous \"$install_dir\" \"$app_update\" +quit" + else + steamcmd +login anonymous "$install_dir" "$app_update" +quit + fi +} + +main diff --git a/bin/ark-sa/config-templating/GameUserSettings.template.ini b/bin/ark-sa/config-templating/GameUserSettings.template.ini deleted file mode 100644 index b8c467b..0000000 --- a/bin/ark-sa/config-templating/GameUserSettings.template.ini +++ /dev/null @@ -1,100 +0,0 @@ -[ServerSettings] -DifficultyOffset=0.20 -PlayerDamageMultiplier=1.0 -StructureResistanceMultiplier=1.0 -XPMultiplier=1.0 -TamingSpeedMultiplier=1.0 -HarvestAmountMultiplier=1.0 -PlayerCharacterWaterDrainMultiplier=1.0 -PlayerCharacterFoodDrainMultiplier=1.0 -DinoCharacterFoodDrainMultiplier=1.0 -PlayerCharacterStaminaDrainMultiplier=1.0 -DinoCharacterStaminaDrainMultiplier=1.0 -PlayerCharacterHealthRecoveryMultiplier=1.0 -DinoCharacterHealthRecoveryMultiplier=1.0 -HarvestHealthMultiplier=1.0 -StartTimeOverride=False -StartTimeHour=10.0 -ListenServerTetherDistanceMultiplier=1.0 -RaidDinoCharacterFoodDrainMultiplier=1.0 -StructurePreventResourceRadiusMultiplier=1.0 -PvEDinoDecayPeriodMultiplier=1.0 -AllowRaidDinoFeeding=False -PerPlatformMaxStructuresMultiplier=1 -GlobalVoiceChat=False -ProximityChat=False -NoTributeDownloads=False -AllowThirdPersonPlayer=True -AlwaysNotifyPlayerLeft=False -DontAlwaysNotifyPlayerJoined=False -ServerHardcore=False -ServerPVE=<> -ServerCrosshair=True -ServerForceNoHUD=False -ShowMapPlayerLocation=True -EnablePvPGamma=True -DisableStructureDecayPvE=False -AllowFlyerCarryPvE=False -OnlyAllowSpecifiedEngrams=False -AllowHideDamageSourceFromLogs=True -RandomSupplyCratePoints=False -DisableWeatherFog=False -PreventDownloadSurvivors=False -PreventDownloadItems=False -PreventDownloadDinos=False -DisablePvEGamma=False -DisableDinoDecayPvE=False -AdminLogging=False -AllowCaveBuildingPvE=False -ForceAllowCaveFlyers=False -PreventOfflinePvP=False -PvPDinoDecay=False -OverrideStructurePlatformPrevention=False -AllowAnyoneBabyImprintCuddle=False -DisableImprintDinoBuff=False -ShowFloatingDamageText=False -PreventDiseases=False -NonPermanentDiseases=False -EnableExtraStructurePreventionVolumes=False -PreventTribeAlliances=False -bAllowSpeedLeveling=False -bAllowFlyerSpeedLeveling=False -PreventOfflinePvPInterval=-0 -CraftingSkillBonusMultiplier=1 -SupplyCrateLootQualityMultiplier=1 -ActiveEvent= -OverrideStartTime=False -ActiveMapMod=0 -RCONEnabled=<> -RCONPort=<> -TheMaxStructuresInRange=10500 -OxygenSwimSpeedStatMultiplier=1 -TribeNameChangeCooldown=15 -PlatformSaddleBuildAreaBoundsMultiplier=1 -AlwaysAllowStructurePickup=True -StructurePickupTimeAfterPlacement=30 -StructurePickupHoldDuration=0.5 -KickIdlePlayersPeriod=3600 -AutoSavePeriodMinutes=15 -MaxTamedDinos=5000 -ItemStackSizeMultiplier=1 -RCONServerGameLogBuffer=600 -ImplantSuicideCD=28800 -AllowHitMarkers=True -ServerPassword=<> -ServerAdminPassword=<> -DayCycleSpeedScale=1.0 -DayTimeSpeedScale=1.0 -NightTimeSpeedScale=1.0 -DinoDamageMultiplier=1.0 -StructureDamageMultiplier=1.0 -PlayerResistanceMultiplier=1.0 -DinoResistanceMultiplier=1.0 -PvEStructureDecayPeriodMultiplier=1.0 - -[SessionSettings] -SessionName=<> - -[/Script/Engine.GameSession] -MaxPlayers=<> - diff --git a/bin/ark-sa/config-templating/bootstrap-configs.sh b/bin/ark-sa/config-templating/bootstrap-configs.sh deleted file mode 100644 index 3a4d196..0000000 --- a/bin/ark-sa/config-templating/bootstrap-configs.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -set -e - -[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x - -config_template="${CONTAINER_BIN_DIR}/ark-sa/config-templating/GameUserSettings.template.ini" -config_file="${ARK_SERVER_DIR}/server/ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini" - -if [[ ! -f "$config_file" ]] || [[ "$ARK_REBUILD_CONFIG" = "True" ]]; then - - if [ "$ARK_REBUILD_CONFIG" = "True" ]; then - echo "ARK_REBUILD_CONFIG is set to True, rebuilding existing GameUserSettings.ini..." - else - echo "GameUserSettings.ini not found, creating from template..." - fi - - if [ -f "$config_template" ]; then - mkdir -p "${ARK_SERVER_DIR}/server/ShooterGame/Saved/Config/WindowsServer" - cp "$config_template" "$config_file" - sed -i "s/SessionName=<>/SessionName=$ARK_SERVER_NAME/" "$config_file" - sed -i "s/RCONEnabled=<>/RCONEnabled=$ARK_ENABLE_RCON/" "$config_file" - sed -i "s/RCONPort=<>/RCONPort=$ARK_RCON_PORT/" "$config_file" - sed -i "s/MaxPlayers=<>/MaxPlayers=$ARK_MAX_PLAYERS/" "$config_file" - sed -i "s/ServerPassword=<>/ServerPassword=$ARK_SERVER_PASSWORD/" "$config_file" - sed -i "s/ServerAdminPassword=<>/ServerAdminPassword=$ARK_SERVER_ADMIN_PASSWORD/" "$config_file" - sed -i "s/ServerPVE=<>/ServerPVE=$ARK_ENABLE_PVE/" "$config_file" - else - echo "GameUserSettings template not found at $config_template" - exit 1 - fi -else - echo "GameUserSettings.ini found, skipping creation from template..." -fi diff --git a/bin/docker-entrypoint.sh b/bin/docker-entrypoint.sh deleted file mode 100644 index bea78b3..0000000 --- a/bin/docker-entrypoint.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -set -e - -handle_steam_cmd_install_error() { - echo "An error occurred in steam-cmd-install.sh" - exit 1 -} - -handle_ark_sa_config_bootstrap_error() { - echo "An error occurred in ark-sa/config-templating/bootstrap-configs.sh" - exit 1 -} - -handle_launch_ark_sa_error() { - echo "An error occurred in launch-ark-sa.sh" - exit 1 -} - -[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x - -required_sub_dirs=("server") - -for sub_dir in "${required_sub_dirs[@]}"; do - if [[ ! -d "${ARK_SERVER_DIR}/${sub_dir}" ]]; then - mkdir -p "${ARK_SERVER_DIR}/${sub_dir}" || echo "Failed to create directory: ${ARK_SERVER_DIR}/${sub_dir}" - fi - chown "${CONTAINER_USER}". "${ARK_SERVER_DIR}/${sub_dir}" || echo "Failed setting rights on ${ARK_SERVER_DIR}/${sub_dir}, continuing startup..." -done - -trap 'handle_steam_cmd_install_error' ERR -"$CONTAINER_BIN_DIR/steam-cmd-install.sh" -trap - ERR - -trap 'handle_ark_sa_config_bootstrap_error' ERR -"$CONTAINER_BIN_DIR/ark-sa/config-templating/bootstrap-configs.sh" -trap - ERR - -trap 'handle_launch_ark_sa_error' ERR -"$CONTAINER_BIN_DIR/launch-ark-sa.sh" -trap - ERR diff --git a/bin/launch-ark-sa.sh b/bin/launch-ark-sa.sh deleted file mode 100644 index 1fce55b..0000000 --- a/bin/launch-ark-sa.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash - -set -e - -[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x - -CMD_ARGS="$ARK_MAP?listen" -CMD_ARGS+="?Port=$ARK_GAME_PORT" -CMD_ARGS+="?QueryPort=$ARK_QUERY_PORT" - -if [[ -n "$ARK_MULTI_HOME" ]]; then - CMD_ARGS+="?MultiHome=$ARK_MULTI_HOME" -fi - -if [[ "$ARK_NO_BATTLEYE" = "True" ]]; then - BATTLE_EYE_FLAG="-NoBattlEye" -else - BATTLE_EYE_FLAG="" -fi - -if [[ -n "$ARK_EPIC_PUBLIC_IP" ]]; then - EPIC_IP_FLAG="--PublicIPforEpic $ARK_EPIC_PUBLIC_IP" -else - EPIC_IP_FLAG="" -fi - -PLAYER_COUNT_FLAG="-WinLiveMaxPlayers=$ARK_MAX_PLAYERS" - -# remove all whitespace from ARK_MOD_LIST -ARK_MOD_LIST="$(echo -e "$ARK_MOD_LIST" | tr -d '[:space:]')" - -if [[ -n "$ARK_MOD_LIST" ]]; then - MOD_ARGS="-automanagedmods -mods=$ARK_MOD_LIST" -else - MOD_ARGS="" -fi - -if [[ "$TEST_DRY_RUN" == "True" ]]; then - echo "DRY RUN: Running Ark Server with the following arguments:" - echo "/opt/glorious_eggroll/proton/bin/wine ./server/ShooterGame/Binaries/Win64/ArkAscendedServer.exe $CMD_ARGS -log $BATTLE_EYE_FLAG $EPIC_IP_FLAG $PLAYER_COUNT_FLAG $MOD_ARGS $ARK_EXTRA_LAUNCH_OPTIONS" - exit 0 -fi - -xvfb-run /opt/glorious_eggroll/proton/bin/wine ./server/ShooterGame/Binaries/Win64/ArkAscendedServer.exe "$CMD_ARGS" -log $BATTLE_EYE_FLAG $EPIC_IP_FLAG $PLAYER_COUNT_FLAG $MOD_ARGS $ARK_EXTRA_LAUNCH_OPTIONS &> proton-wine.log & - -log_file="${ARK_SERVER_DIR}/server/ShooterGame/Saved/Logs/ShooterGame.log" -timeout=300 - -while [ ! -f "$log_file" ] && [ $timeout -gt 0 ]; do - echo "Waiting for file $log_file to exist..." - sleep 1 - ((timeout--)) -done - -if [ -f "$log_file" ]; then - tail -f "$log_file" -else - echo "File $log_file did not appear within the timeout period" - exit 1 -fi diff --git a/bin/steam-cmd-install.sh b/bin/steam-cmd-install.sh deleted file mode 100644 index 51e7929..0000000 --- a/bin/steam-cmd-install.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -set -e - -[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x - -if [ "$STEAMCMD_SKIP_VALIDATION" = "True" ]; then - echo "SteamCMD Will Not Validate Ark SA Server Files" - APP_UPDATE_LINE="+app_update $STEAMCMD_ARK_SA_APP_ID" -else - echo "SteamCMD Will Validate Ark SA Server Files" - APP_UPDATE_LINE="+app_update $STEAMCMD_ARK_SA_APP_ID validate" -fi - -if [ "$TEST_DRY_RUN" = "True" ]; then - if [ "$ARK_PREVENT_AUTO_UPDATE" = "True" ]; then - echo "DRY RUN: Skipping Auto-Update of Ark SA Server" - exit 0 - else - echo "DRY RUN: Running steamcmd with the following arguments:" - echo "steamcmd +login anonymous +force_install_dir $ARK_SERVER_DIR/server $APP_UPDATE_LINE +quit" - exit 0 - fi -fi - -# run steamcmd if ARK_PREVENT_AUTO_UPDATE is not "True" OR if install directory is empty -if [[ "$ARK_PREVENT_AUTO_UPDATE" != "True" ]] || [[ -z "$(ls -A "$ARK_SERVER_DIR/server")" ]]; then - steamcmd +login anonymous +force_install_dir "$ARK_SERVER_DIR/server" $APP_UPDATE_LINE +quit -else - echo "Skipping Auto-Update of Ark SA Server" -fi diff --git a/bin/system-bootstrap.sh b/bin/system-bootstrap.sh new file mode 100644 index 0000000..a0d3ac1 --- /dev/null +++ b/bin/system-bootstrap.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +set -e + +[[ -z "${DEBUG}" ]] || [[ "${DEBUG,,}" = "false" ]] || [[ "${DEBUG,,}" = "0" ]] || set -x + +echo "System Bootstrap - Starting" + +cleanup() { + echo "System Bootstrap - Cleanup Starting" + supervisorctl stop all + supervisorctl start ark-sa-backup + wait_for_backup_completion + supervisorctl exit + echo "System Bootstrap - Cleanup Stopping" +} + +main() { + setup_cron_jobs + if [[ "$DRY_RUN" = "True" ]]; then + echo "DRY_RUN - exec /usr/bin/supervisord -c /usr/local/etc/supervisord.conf" + else + trap 'cleanup' SIGTERM + /usr/bin/supervisord -c /usr/local/etc/supervisord.conf & + wait $! + fi +} + +setup_cron_jobs() { + if [[ "$SCHEDULED_RESTART" = "True" ]]; then + if [[ "$BACKUP_ON_SCHEDULED_RESTART" = "True" ]]; then + echo "System Bootstrap - Setting Up Scheduled Restart With Backup" + setup_cron_scheduled_restart_with_backup >> /usr/local/bin/ark-sa-cron-jobs + else + echo "System Bootstrap - Setting Up Scheduled Restart" + setup_cron_scheduled_restart >> /usr/local/bin/ark-sa-cron-jobs + fi + fi + + if [[ "$SCHEDULED_UPDATE" = "True" ]]; then + if [[ "$BACKUP_BEFORE_UPDATE" = "True" ]]; then + echo "System Bootstrap - Setting Up Scheduled Update With Backup" + setup_cron_scheduled_update_with_backup >> /usr/local/bin/ark-sa-cron-jobs + else + echo "System Bootstrap - Setting Up Scheduled Update" + setup_cron_scheduled_update >> /usr/local/bin/ark-sa-cron-jobs + fi + fi + + if [[ "$SCHEDULED_BACKUP" = "True" ]]; then + echo "System Bootstrap - Setting Up Scheduled Backup" + setup_cron_scheduled_backup >> /usr/local/bin/ark-sa-cron-jobs + fi + + if [[ -f /usr/local/bin/ark-sa-cron-jobs ]]; then + echo "System Bootstrap - Updating Crontab" + crontab /usr/local/bin/ark-sa-cron-jobs + rm /usr/local/bin/ark-sa-cron-jobs + fi +} + +setup_cron_scheduled_restart() { + echo "$(date) - Server Restart CRON Scheduled For: $RESTART_CRON" >> /ark-server/logs/cron.log + echo "$RESTART_CRON supervisorctl restart ark-sa-server && \ + echo \"\$(date) - CRON Restart - ark-sa-server\" >> /ark-server/logs/cron.log" +} + +setup_cron_scheduled_restart_with_backup() { + echo "$(date) - Server Restart and Backup CRON Scheduled For: $RESTART_CRON" >> /ark-server/logs/cron.log + echo "$RESTART_CRON supervisorctl stop ark-sa-server && supervisorctl start ark-sa-backup-and-restart &&\ + echo \"\$(date) - CRON Restart + Backup - ark-sa-server\" >> /ark-server/logs/cron.log" +} + +setup_cron_scheduled_update() { + echo "$(date) - Server Update Scheduled For: $UPDATE_CRON" >> /ark-server/logs/cron.log + echo "$UPDATE_CRON supervisorctl stop ark-sa-server && supervisorctl start ark-sa-updater && \ + echo \"\$(date) - CRON Update - ark-sa-updater\" >> /ark-server/logs/cron.log" +} + +setup_cron_scheduled_update_with_backup() { + echo "$(date) - Server Update and Backup Scheduled For: $UPDATE_CRON" >> /ark-server/logs/cron.log + echo "$UPDATE_CRON supervisorctl stop ark-sa-server && supervisorctl start ark-sa-backup-and-update && \ + echo \"\$(date) - CRON Update + Backup - ark-sa-updater\" >> /ark-server/logs/cron.log" +} + +setup_cron_scheduled_backup() { + echo "$(date) - Server Backup Scheduled For: $BACKUP_CRON" >> /ark-server/logs/cron.log + echo "$BACKUP_CRON supervisorctl stop ark-sa-server && supervisorctl start ark-sa-backup-and-restart && \ + echo \"\$(date) - CRON Backup - ark-sa-backup\" >> /ark-server/logs/cron.log" +} + +wait_for_backup_completion() { + local counter=0 + local max_wait_time=600 + + while true; do + local status + status=$(supervisorctl status ark-sa-backup) + if [[ "$status" == *"RUNNING"* ]] || [[ "$status" == *"STARTING"* ]]; then + echo "System Bootstrap - Waiting For Backup Process To Finish." + elif [[ "$status" == *"STOPPED"* ]] || [[ "$status" == *"EXITED"* ]] || [[ "$status" == *"FATAL"* ]]; then + echo "System Bootstrap - Backup Process Has Finished" + fi + + sleep 5 + ((counter += 5)) + + if [[ "$counter" -ge "$max_wait_time" ]]; then + echo "System Bootstrap - Timeout Duration Exceeded, Closing Supervisor Without Complete Backup" + break + fi + done +} + +main diff --git a/config_from_env_vars/README.md b/config_from_env_vars/README.md new file mode 100644 index 0000000..3a90969 --- /dev/null +++ b/config_from_env_vars/README.md @@ -0,0 +1,78 @@ +# config_from_env_vars + +``` +usage: main.py [-h] [--path CONFIG_DIRECTORY] + +Update ini configuration files based on environment variables. + +options: + -h, --help show this help message and exit + --path CONFIG_DIRECTORY + Path to store created ini files. +``` + +## Environment Variable Format + +```bash +CONFIG__
_=. +``` +* `CONFIG_` + * All variables used by the script must have the `CONFIG_` prefix. +* `` + * The name of the config file to create. Do not include the extension, all files will be `.ini` files. + * Must be contained within the first pairs of underscores `_`. + * If the config file already exists, it will be updated. +* `
` + * The section of the config file to create the variables in. + * If the section contains special characters like `/` or `.` replace them with `_SLASH_` and `_DOT_` respectively. +* `` + * The name of the variable to create in the config file. + * Must be after the last underscores `_`. +* `` + * The value to set the variable to. + +## File Backups + +When a `CONFIG_` variable is changed between executions, the script will create a backup copy of the current config file then generate a new one. The backup will be stored in the same directory as the config file with the same name and the `.backup#` extension. The `#` will be incremented for each backup created, and the highest number reflects the newest backup. + +If you introduce `CONFIG_` vars and eventually remove all of them, the current configs will have a backup made, but no new config will be generated. + +## Example Execution + +### Example 1 + +```bash +export CONFIG_Test_Test_Section_Var=Value +python3 main.py --path $(pwd) +``` + +Creates + +```ini +#Test.ini +[Test_Section] +Var = Value +``` + +### Example 2 + +```bash +export CONFIG_Test_Test_DOT_Section_Var=Test +python3 main.py --path $(pwd) +``` + +Creates + +```ini +#Test.ini +[Test.Section] +Var = Test +``` + +## Test Execution + +To execute the unit tests associated with this script, run the following command from the project's root directory: + +```bash +python3 -m unittest tests/config_from_env_vars/test_main.py +``` \ No newline at end of file diff --git a/config_from_env_vars/__init__.py b/config_from_env_vars/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config_from_env_vars/main.py b/config_from_env_vars/main.py new file mode 100644 index 0000000..72cec06 --- /dev/null +++ b/config_from_env_vars/main.py @@ -0,0 +1,155 @@ +import filecmp +import os +import logging +from argparse import ArgumentParser +from configparser import RawConfigParser +from pathlib import Path +import shutil +import sys +from typing import Dict + +# Configure logging +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) + + +class MaintainCaseConfigParser(RawConfigParser): + def optionxform(self, optionstr): + return optionstr + + +def process_env_vars() -> Dict[str, Dict[str, Dict[str, str]]]: + config_files: Dict[str, Dict[str, Dict[str, str]]] = {} + for key, value in os.environ.items(): + if not key.startswith("CONFIG_"): + continue + + tokens = key.split("_") + + if len(tokens) < 4: + logging.warning(f"Invalid config environment variable: {key}") + continue + + file_name = tokens[1] + var_name = tokens[-1] + section_name = "_".join(tokens[2:-1]).replace("SLASH", "/").replace("DOT", ".") + section_name = ( + section_name.replace("_/_", "/").replace("/_", "/").replace("_._", ".") + ) + + if file_name not in config_files: + config_files[file_name] = {} + if section_name not in config_files[file_name]: + config_files[file_name][section_name] = {} + + config_files[file_name][section_name][var_name] = value + + return config_files + + +def update_ini_files( + config_data: Dict[str, Dict[str, Dict[str, str]]], path: str +) -> None: + for file_name, sections in config_data.items(): + config_parser = MaintainCaseConfigParser(strict=False) + file_path = os.path.join(path, f"{file_name}.ini") + + try: + if not Path(file_path).exists(): + logging.warning( + f"File not found: {file_path}. A new file will be created." + ) + config_parser.read(file_path) + + for section, vars in sections.items(): + if not config_parser.has_section(section): + config_parser.add_section(section) + for var, value in vars.items(): + config_parser.set(section, var, value) + logging.info(f"Saving {section} {var}={value} to {file_path}") + + with open(file_path, "w") as config_file: + config_parser.write(config_file, space_around_delimiters=False) + + except Exception as e: + logging.error(f"Error updating file {file_name}.ini: {e}") + continue + + +def backup_existing_ini_files(path: str) -> None: + for file_name in os.listdir(path): + if not file_name.endswith(".ini"): + continue + + file_path = os.path.join(path, file_name) + backup_path = os.path.join(path, f"{file_name}.backup") + + counter = 1 + while Path(backup_path).exists(): + backup_path = os.path.join(path, f"{file_name}.backup{counter}") + counter += 1 + + try: + logging.info(f"Creating backup of {file_path} to {backup_path}") + shutil.move(file_path, backup_path) + except Exception as e: + logging.error(f"Error creating backup of {file_name}: {e}") + continue + + +def get_latest_backup_file(base_file_path: str): + base_file_name = os.path.basename(base_file_path) + directory = os.path.dirname(base_file_path) + backup_files = sorted( + Path(directory).glob(f"{base_file_name}.backup*"), reverse=True + ) + return str(backup_files[0]) if backup_files else "" + + +def compare_and_cleanup_configs(path: str): + for file in Path(path).glob("*.ini"): + latest_backup = get_latest_backup_file(str(file)) + + if not latest_backup: + logging.info(f"No backups exist for: {file}") + continue + + if Path(latest_backup).exists() and filecmp.cmp( + file, latest_backup, shallow=False + ): + os.remove(latest_backup) + logging.info(f"New config matches old, backup removed: {latest_backup}") + else: + logging.info( + f"Configuration changed, latest backup retained: {latest_backup}" + ) + + +def main(): + parser = ArgumentParser( + description="Update ini configuration files based on environment variables." + ) + parser.add_argument( + "--path", + dest="config_directory", + type=str, + help="Path to store created ini files.", + default="/ark-server/server/ShooterGame/Saved/Config/WindowsServer", + ) + args = parser.parse_args() + + if not os.path.isdir(args.config_directory): + logging.error( + f"The specified directory does not exist: {args.config_directory}" + ) + sys.exit(1) + + backup_existing_ini_files(args.config_directory) + config_data = process_env_vars() + update_ini_files(config_data, args.config_directory) + compare_and_cleanup_configs(args.config_directory) + + +if __name__ == "__main__": + main() diff --git a/deployment-examples/docker-compose/.env b/deployment-examples/docker-compose/.env new file mode 100644 index 0000000..03ddd82 --- /dev/null +++ b/deployment-examples/docker-compose/.env @@ -0,0 +1,87 @@ +CONFIG_GameUserSettings_ServerSettings_DifficultyOffset=0.20 +CONFIG_GameUserSettings_ServerSettings_PlayerDamageMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_StructureResistanceMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_XPMultiplier=2.5 +CONFIG_GameUserSettings_ServerSettings_TamingSpeedMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_HarvestAmountMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PlayerCharacterWaterDrainMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PlayerCharacterFoodDrainMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_DinoCharacterFoodDrainMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PlayerCharacterStaminaDrainMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_DinoCharacterStaminaDrainMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PlayerCharacterHealthRecoveryMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_DinoCharacterHealthRecoveryMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_HarvestHealthMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_StartTimeOverride=False +CONFIG_GameUserSettings_ServerSettings_StartTimeHour=10.0 +CONFIG_GameUserSettings_ServerSettings_ListenServerTetherDistanceMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_RaidDinoCharacterFoodDrainMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_StructurePreventResourceRadiusMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PvEDinoDecayPeriodMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_AllowRaidDinoFeeding=False +CONFIG_GameUserSettings_ServerSettings_PerPlatformMaxStructuresMultiplier=1 +CONFIG_GameUserSettings_ServerSettings_GlobalVoiceChat=False +CONFIG_GameUserSettings_ServerSettings_ProximityChat=False +CONFIG_GameUserSettings_ServerSettings_NoTributeDownloads=False +CONFIG_GameUserSettings_ServerSettings_AllowThirdPersonPlayer=True +CONFIG_GameUserSettings_ServerSettings_AlwaysNotifyPlayerLeft=False +CONFIG_GameUserSettings_ServerSettings_DontAlwaysNotifyPlayerJoined=False +CONFIG_GameUserSettings_ServerSettings_ServerHardcore=False +CONFIG_GameUserSettings_ServerSettings_ServerCrosshair=True +CONFIG_GameUserSettings_ServerSettings_ServerForceNoHUD=False +CONFIG_GameUserSettings_ServerSettings_ShowMapPlayerLocation=True +CONFIG_GameUserSettings_ServerSettings_EnablePvPGamma=True +CONFIG_GameUserSettings_ServerSettings_DisableStructureDecayPvE=False +CONFIG_GameUserSettings_ServerSettings_AllowFlyerCarryPvE=False +CONFIG_GameUserSettings_ServerSettings_OnlyAllowSpecifiedEngrams=False +CONFIG_GameUserSettings_ServerSettings_AllowHideDamageSourceFromLogs=True +CONFIG_GameUserSettings_ServerSettings_RandomSupplyCratePoints=False +CONFIG_GameUserSettings_ServerSettings_DisableWeatherFog=False +CONFIG_GameUserSettings_ServerSettings_PreventDownloadSurvivors=False +CONFIG_GameUserSettings_ServerSettings_PreventDownloadItems=False +CONFIG_GameUserSettings_ServerSettings_PreventDownloadDinos=False +CONFIG_GameUserSettings_ServerSettings_DisablePvEGamma=False +CONFIG_GameUserSettings_ServerSettings_DisableDinoDecayPvE=False +CONFIG_GameUserSettings_ServerSettings_AdminLogging=False +CONFIG_GameUserSettings_ServerSettings_AllowCaveBuildingPvE=False +CONFIG_GameUserSettings_ServerSettings_ForceAllowCaveFlyers=False +CONFIG_GameUserSettings_ServerSettings_PreventOfflinePvP=False +CONFIG_GameUserSettings_ServerSettings_PvPDinoDecay=False +CONFIG_GameUserSettings_ServerSettings_OverrideStructurePlatformPrevention=False +CONFIG_GameUserSettings_ServerSettings_AllowAnyoneBabyImprintCuddle=False +CONFIG_GameUserSettings_ServerSettings_DisableImprintDinoBuff=False +CONFIG_GameUserSettings_ServerSettings_ShowFloatingDamageText=False +CONFIG_GameUserSettings_ServerSettings_PreventDiseases=False +CONFIG_GameUserSettings_ServerSettings_NonPermanentDiseases=False +CONFIG_GameUserSettings_ServerSettings_EnableExtraStructurePreventionVolumes=False +CONFIG_GameUserSettings_ServerSettings_PreventTribeAlliances=False +CONFIG_GameUserSettings_ServerSettings_bAllowSpeedLeveling=False +CONFIG_GameUserSettings_ServerSettings_bAllowFlyerSpeedLeveling=False +CONFIG_GameUserSettings_ServerSettings_PreventOfflinePvPInterval=-0 +CONFIG_GameUserSettings_ServerSettings_CraftingSkillBonusMultiplier=1 +CONFIG_GameUserSettings_ServerSettings_SupplyCrateLootQualityMultiplier=1 +CONFIG_GameUserSettings_ServerSettings_ActiveEvent= +CONFIG_GameUserSettings_ServerSettings_OverrideStartTime=False +CONFIG_GameUserSettings_ServerSettings_ActiveMapMod=0 +CONFIG_GameUserSettings_ServerSettings_TheMaxStructuresInRange=10500 +CONFIG_GameUserSettings_ServerSettings_OxygenSwimSpeedStatMultiplier=1 +CONFIG_GameUserSettings_ServerSettings_TribeNameChangeCooldown=15 +CONFIG_GameUserSettings_ServerSettings_PlatformSaddleBuildAreaBoundsMultiplier=1 +CONFIG_GameUserSettings_ServerSettings_AlwaysAllowStructurePickup=True +CONFIG_GameUserSettings_ServerSettings_StructurePickupTimeAfterPlacement=30 +CONFIG_GameUserSettings_ServerSettings_StructurePickupHoldDuration=0.5 +CONFIG_GameUserSettings_ServerSettings_KickIdlePlayersPeriod=3600 +CONFIG_GameUserSettings_ServerSettings_AutoSavePeriodMinutes=15 +CONFIG_GameUserSettings_ServerSettings_MaxTamedDinos=5000 +CONFIG_GameUserSettings_ServerSettings_ItemStackSizeMultiplier=1 +CONFIG_GameUserSettings_ServerSettings_RCONServerGameLogBuffer=600 +CONFIG_GameUserSettings_ServerSettings_ImplantSuicideCD=28800 +CONFIG_GameUserSettings_ServerSettings_AllowHitMarkers=True +CONFIG_GameUserSettings_ServerSettings_DayCycleSpeedScale=1.0 +CONFIG_GameUserSettings_ServerSettings_DayTimeSpeedScale=1.0 +CONFIG_GameUserSettings_ServerSettings_NightTimeSpeedScale=1.0 +CONFIG_GameUserSettings_ServerSettings_DinoDamageMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_StructureDamageMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PlayerResistanceMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_DinoResistanceMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_PvEStructureDecayPeriodMultiplier=1.0 diff --git a/deployment-examples/docker-compose/README.md b/deployment-examples/docker-compose/README.md index f492abb..fdb7d47 100644 --- a/deployment-examples/docker-compose/README.md +++ b/deployment-examples/docker-compose/README.md @@ -58,6 +58,12 @@ Stop the stack. $ docker compose -f basic-docker-compose.yml down ``` +### Stop Compose Stack With Timeout (Needed For Large Backups) +Stop the stack and wait up to 10min before killing the container. +```bash +$ docker compose -f advanced-docker-compose.yml down -t 600 +``` + ## Advanced Example This example has more configuration options set via environment variables than the above example. This reflects a more robust configuration that would be used in a live server. @@ -75,7 +81,32 @@ Press `CTRL+C` to stop the stack. ```bash $ docker compose -f advanced-docker-compose.yml up -d ``` +### Environment File To Store Configs + +It is important to note that the advanced example uses an environment file to store the environment variables. If you are going to use a lot of `CONFIG_` environment variables I recommend moving them to an environment file. This will make it easier to manage the config specific variables and prevent you compose file from becoming too long. + +You specify the environment file in the `env_file` section of your compose file: +```yaml +version: '3' +services: + ark-sa: + container_name: ark + image: johnnyknighten/ark-sa-server:latest + restart: unless-stopped + env_file: + - .env + ... +``` + +The actual environment file is a simple text file with each environment variable on its own line. +```env +CONFIG_GameUserSettings_ServerSettings_DifficultyOffset=0.20 +CONFIG_GameUserSettings_ServerSettings_PlayerDamageMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_StructureResistanceMultiplier=1.0 +CONFIG_GameUserSettings_ServerSettings_XPMultiplier=5.0 +... +``` ### Viewing Logs When Using A Detached Compose Stack #### View all logs generated. ```bash @@ -110,6 +141,12 @@ Stop the stack. $ docker compose -f advanced-docker-compose.yml down ``` +### Stop Compose Stack With Timeout (Needed For Large Backups) +Stop the stack and wait up to 10min before killing the container. +```bash +$ docker compose -f advanced-docker-compose.yml down -t 600 +``` + ## Note About Volumes In the two above examples a docker volume called `ark-files` will be created and store all server files. This volume will persist even if the container is removed. This allows the container to be removed and re-created without losing any server data. @@ -122,7 +159,9 @@ services: ark-sa: ... # all other config before the volumes section volumes: - - '/desired/directory/to/store/ark/files:/ark-server' + - '/desired/directory/to/store/ark/server:/ark-server/server' + - '/desired/directory/to/store/ark/backups:/ark-server/backups' + - '/desired/directory/to/store/ark/logs:/ark-server/logs' ... # removed or commented out #volumes: @@ -131,4 +170,4 @@ services: ## Note About Server Updates -If you have `ARK_PREVENT_AUTO_UPDATE` set to `True`, then the container will not download any updates via [SteamCMD](https://developer.valvesoftware.com/wiki/SteamCMD) and will continue to use the files in the supplied volume/bind mount. Whenever you are ready to perform an update, set ARK_PREVENT_AUTO_UPDATE to `False ` and restart the container. The container will then download the latest version of the server files and update the server. Once the update is complete, set `ARK_PREVENT_AUTO_UPDATE` back to `True` and restart the container. The container will then use the updated files for the server and go back to not updating on restarts. +If you have `UPDATE_ON_BOOT` set to `False`, then the container will not download any updates via [SteamCMD](https://developer.valvesoftware.com/wiki/SteamCMD) and will continue to use the files in the supplied volume/bind mount. Whenever you are ready to perform an update, set `UPDATE_ON_BOOT` to `True ` and restart the container. The container will then download the latest version of the server files and update the server. Once the update is complete, set `UPDATE_ON_BOOT` back to `False` and restart the container. The container will then use the updated files for the server and go back to not updating on restarts. diff --git a/deployment-examples/docker-compose/advanced-docker-compose.yml b/deployment-examples/docker-compose/advanced-docker-compose.yml index 6f31a0d..0304d04 100644 --- a/deployment-examples/docker-compose/advanced-docker-compose.yml +++ b/deployment-examples/docker-compose/advanced-docker-compose.yml @@ -5,28 +5,42 @@ services: container_name: ark image: johnnyknighten/ark-sa-server:latest restart: unless-stopped + env_file: + - .env environment: - - ARK_GAME_PORT=8888 - - ARK_QUERY_PORT=27016 - - ARK_MAX_PLAYERS=55 - - ARK_SERVER_NAME="Containerized Server Test223" - - ARK_SERVER_PASSWORD= - - ARK_SERVER_ADMIN_PASSWORD=adminpassword25 - - ARK_ENABLE_PVE=False - - STEAMCMD_SKIP_VALIDATION=False - - ARK_PREVENT_AUTO_UPDATE=False - - ARK_ENABLE_RCON=True - - ARK_RCON_PORT=27021 - - ARK_MOD_LIST="927131, 893657" - - ARK_MULTI_HOME=0.0.0.0 # Make Your Public IP - - ARK_EPIC_PUBLIC_IP=0.0.0.0 # Make Your Public IP - - ARK_NO_BATTLEYE=True + - TZ=America/Los_Angeles + - GAME_PORT=8888 + - QUERY_PORT=27016 + - MAX_PLAYERS=55 + - SERVER_NAME="Containerized Server Test223" + - SERVER_PASSWORD= + - ADMIN_PASSWORD=adminpassword25 + - ENABLE_PVE=False + - SKIP_FILE_VALIDATION=False + - UPDATE_ON_BOOT=True + - ENABLE_RCON=True + - RCON_PORT=27021 + - MOD_LIST="927131, 893657" + - MULTI_HOME=0.0.0.0 # Make Your Public IP + - EPIC_PUBLIC_IP=0.0.0.0 # Make Your Public IP + - NO_BATTLEYE=True + - SCHEDULED_RESTART=False + - BACKUP_ON_SCHEDULED_RESTART=False + - RESTART_CRON=0 4 * * 0,3 + - SCHEDULED_UPDATE=False + - UPDATE_CRON=0 5 * * 6 + - BACKUP_ON_STOP=True + - RETAIN_BACKUPS=10 volumes: - - 'ark-files:/ark-server' + - 'ark-server:/ark-server/server' + - 'ark-backups:/ark-server/backups' + - 'ark-logs:/ark-server/logs' ports: - 8888:8888/udp - 8889:8889/udp - 27016:27016/udp - 27021:27021/tcp volumes: - ark-files: + ark-server: + ark-backups: + ark-logs: diff --git a/deployment-examples/docker-compose/basic-docker-compose.yml b/deployment-examples/docker-compose/basic-docker-compose.yml index 0bec391..1a35b9c 100644 --- a/deployment-examples/docker-compose/basic-docker-compose.yml +++ b/deployment-examples/docker-compose/basic-docker-compose.yml @@ -6,14 +6,18 @@ services: image: johnnyknighten/ark-sa-server:latest restart: unless-stopped environment: - - ARK_SERVER_NAME="Simple ARK SA Server" - - ARK_SERVER_ADMIN_PASSWORD=secretpassword + - SERVER_NAME="Simple ARK SA Server" + - ADMIN_PASSWORD=secretpassword volumes: - - 'ark-files:/ark-server' + - 'ark-server:/ark-server/server' + - 'ark-backups:/ark-server/backups' + - 'ark-logs:/ark-server/logs' ports: - 7777:7777/udp - 7778:7778/udp - 27015:27015/udp - 27020:27020/tcp volumes: - ark-files: + ark-server: + ark-backups: + ark-logs: diff --git a/deployment-examples/docker-run-with-systemd/README.md b/deployment-examples/docker-run-with-systemd/README.md index b1fb978..754ba40 100644 --- a/deployment-examples/docker-run-with-systemd/README.md +++ b/deployment-examples/docker-run-with-systemd/README.md @@ -9,18 +9,18 @@ Start by creating a file to hold all of your variables. Consider the permissions Create a service config file [`/etc/sysconfig/ark-sa-server.env`](ark-sa-server.env): ``` -ARK_SERVER_NAME="Simple ARK SA Server" -ARK_GAME_PORT=8888 -ARK_QUERY_PORT=27016 -ARK_MAX_PLAYERS=20 -ARK_SERVER_PASSWORD=password2 -ARK_SERVER_ADMIN_PASSWORD=adminpassword2 -ARK_ENABLE_PVE=True -STEAMCMD_SKIP_VALIDATION=True +SERVER_NAME="Simple ARK SA Server" +GAME_PORT=8888 +QUERY_PORT=27016 +MAX_PLAYERS=20 +SERVER_PASSWORD=password2 +ADMIN_PASSWORD=adminpassword2 +ENABLE_PVE=True +SKIP_FILE_VALIDATION=True ARK_PREVENT_AUTO_UPDATE=True ARK_RCON_ENABLED=True -ARK_RCON_PORT=27021 -ARK_MOD_LIST="927131, 893657" +RCON_PORT=27021 +MOD_LIST="927131, 893657" ``` ## Create Systemd Service Unit File @@ -44,7 +44,9 @@ ExecStart=/usr/bin/docker run \ --name %n \ --pull=always \ # may want to remove if using locally built image --rm \ - -v /opt/ark-sa-server:/ark-server\ + -v /opt/ark-sa/server:/ark-server/server\ + -v /opt/ark-sa/backups:/ark-server/backups\ + -v /opt/ark-sa/logs:/ark-server/logs\ -p 8888:8888/udp \ -p 8889:8889/udp \ -p 27016:27016/udp \ diff --git a/deployment-examples/docker-run-with-systemd/ark-sa-server.env b/deployment-examples/docker-run-with-systemd/ark-sa-server.env index d34a04d..f2db5ad 100644 --- a/deployment-examples/docker-run-with-systemd/ark-sa-server.env +++ b/deployment-examples/docker-run-with-systemd/ark-sa-server.env @@ -1,12 +1,12 @@ -ARK_SERVER_NAME="Simple ARK SA Server" -ARK_GAME_PORT=8888 -ARK_QUERY_PORT=27016 -ARK_MAX_PLAYERS=20 -ARK_SERVER_PASSWORD=password2 -ARK_SERVER_ADMIN_PASSWORD=adminpassword2 -ARK_ENABLE_PVE=True -STEAMCMD_SKIP_VALIDATION=True -ARK_PREVENT_AUTO_UPDATE=True +SERVER_NAME="Simple ARK SA Server" +GAME_PORT=8888 +QUERY_PORT=27016 +MAX_PLAYERS=20 +SERVER_PASSWORD=password2 +ADMIN_PASSWORD=adminpassword2 +ENABLE_PVE=True +SKIP_FILE_VALIDATION=True +UPDATE_ON_BOOT=False ARK_RCON_ENABLED=True -ARK_RCON_PORT=27021 -ARK_MOD_LIST="927131, 893657" \ No newline at end of file +RCON_PORT=27021 +MOD_LIST="927131, 893657" \ No newline at end of file diff --git a/deployment-examples/docker-run-with-systemd/ark-sa-server.service b/deployment-examples/docker-run-with-systemd/ark-sa-server.service index 16666f6..79eb0ad 100644 --- a/deployment-examples/docker-run-with-systemd/ark-sa-server.service +++ b/deployment-examples/docker-run-with-systemd/ark-sa-server.service @@ -11,7 +11,9 @@ ExecStartPre=-/usr/bin/docker rm %n ExecStart=/usr/bin/docker run \ --name %n \ --rm \ - -v /opt/ark-sa-server:/ark-server\ + -v /opt/ark-sa/server:/ark-server/server\ + -v /opt/ark-sa/backups:/ark-server/backups\ + -v /opt/ark-sa/logs:/ark-server/logs\ -p 8888:8888/udp \ -p 8889:8889/udp \ -p 27016:27016/udp \ diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..6af2bf2 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,126 @@ +[supervisord] +user=root +nodaemon=true +logfile=/ark-server/logs/supervisord.log +pidfile=/var/run/supervisord.pid +childlogdir=/var/log/supervisor + +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0770 +chown=ark-sa:ark-sa + +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[program:crond] +user=root +command=cron -f +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=true +autorestart=true +priority=10 + +[program:ark-sa-bootstrap] +user=ark-sa +command=/usr/local/bin/ark-sa-bootstrap.sh +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=true +autorestart=false +startsecs=0 +startretries=0 +priority=20 + +[program:ark-sa-updater] +user=ark-sa +command=/usr/local/bin/ark-sa-updater.sh +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=false +autorestart=false +startsecs=0 +startretries=0 +priority=30 + +[program:ark-sa-server] +user=ark-sa +command=/usr/local/bin/ark-sa-server.sh +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=false +autorestart=false +stopasgroup=true +killasgroup=true +startsecs=45 +startretries=2 +stopwaitsecs=45 +priority=50 + +[program:ark-sa-backup] +user=ark-sa +command=/usr/local/bin/ark-sa-backup.sh +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=false +autorestart=false +startsecs=0 +startretries=0 +stopwaitsecs=600 +priority=100 + +[program:ark-sa-backup-and-restart] +user=ark-sa +command=/usr/local/bin/ark-sa-backup.sh restart +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=false +autorestart=false +startsecs=0 +startretries=0 +stopwaitsecs=600 +priority=100 + +[program:ark-sa-backup-and-update] +user=ark-sa +command=/usr/local/bin/ark-sa-backup.sh update +stdout_syslog=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_syslog=true +stderr_logfile_maxbytes=0 +redirect_stderr=true +autostart=false +autorestart=false +startsecs=0 +startretries=0 +stopwaitsecs=600 +priority=100 diff --git a/tests/bin_scripts/ark-sa-backup.sh b/tests/bin_scripts/ark-sa-backup.sh new file mode 100644 index 0000000..92ac813 --- /dev/null +++ b/tests/bin_scripts/ark-sa-backup.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +perform_test "Verify Backup Is Created With Defaults (.tar.gz)" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "mkdir -p /ark-server/server/ShooterGame/Saved && \ + echo "test" > /ark-server/server/ShooterGame/Saved/test.txt && \ + /usr/local/bin/ark-sa-backup.sh > /dev/null 2>&1 && \ + ls /ark-server/backups/*.tar.gz > /dev/null 2>&1"' + +perform_test "Verify Backup Is Created With Defaults (.zip)" \ + 'docker run --rm \ + -e ZIP_BACKUPS=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "mkdir -p /ark-server/server/ShooterGame/Saved && \ + echo "test" > /ark-server/server/ShooterGame/Saved/test.txt && \ + /usr/local/bin/ark-sa-backup.sh > /dev/null 2>&1 && \ + ls /ark-server/backups/*.zip > /dev/null 2>&1"' + +perform_test "Test RETAIN_BACKUPS=2 When There Are Already 5 Backups (Delete Files)" \ + 'docker run --rm \ + -e RETAIN_BACKUPS=2 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "mkdir -p /ark-server/server/ShooterGame/Saved > /dev/null 2>&1 && \ + echo "test" > /ark-server/server/ShooterGame/Saved/test.txt > /dev/null 2>&1 && \ + for i in \$(seq 1 5); do touch /ark-server/backups/testfile\$i.txt; done; \ + /usr/local/bin/ark-sa-backup.sh > /dev/null 2>&1 && \ + test \$(ls /ark-server/backups | wc -l) -eq 2"' + +perform_test "Test RETAIN_BACKUPS=6 When There Are Already 5 Backups (No Deletion)" \ + 'docker run --rm \ + -e RETAIN_BACKUPS=6 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "mkdir -p /ark-server/server/ShooterGame/Saved > /dev/null 2>&1 && \ + echo "test" > /ark-server/server/ShooterGame/Saved/test.txt > /dev/null 2>&1 && \ + for i in \$(seq 1 5); do touch /ark-server/backups/testfile\$i.txt; done; \ + /usr/local/bin/ark-sa-backup.sh > /dev/null 2>&1 && \ + test \$(ls /ark-server/backups | wc -l) -eq 6"' + +perform_test "Test RETAIN_BACKUPS Is Empty When There Are Already 5 Backups (No Deletion)" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "mkdir -p /ark-server/server/ShooterGame/Saved > /dev/null 2>&1 && \ + echo "test" > /ark-server/server/ShooterGame/Saved/test.txt > /dev/null 2>&1 && \ + for i in \$(seq 1 5); do touch /ark-server/backups/testfile\$i.txt; done; \ + /usr/local/bin/ark-sa-backup.sh > /dev/null 2>&1 && \ + test \$(ls /ark-server/backups | wc -l) -eq 6"' + +perform_test "Verify ark-sa-server Is Launched When 'restart' Is Passed To The Script" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-backup.sh restart"); + echo $OUTPUT | grep -q "supervisorctl start ark-sa-server"' + +perform_test "Verify ark-sa-updater Is Launched When 'update' Is Passed To The Script" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-backup.sh update"); + echo $OUTPUT | grep -q "supervisorctl start ark-sa-updater"' + +log_failed_tests diff --git a/tests/bin_scripts/ark-sa-bootstrap.sh b/tests/bin_scripts/ark-sa-bootstrap.sh new file mode 100644 index 0000000..508d318 --- /dev/null +++ b/tests/bin_scripts/ark-sa-bootstrap.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +GAME_SETTINGS_PATH="/ark-server/server/ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini" +CONFIG_BOOTSTRAP_PATH="/opt/ark-sa-container/bin/ark-sa/config-templating/bootstrap-configs.sh" + +perform_test "Ensure Config Templating Script Is Being Launched" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-bootstrap.sh > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH"' + +perform_test "Server Files Not Present - Download ARK Server" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-bootstrap.sh"); + echo $OUTPUT | grep -q "No Server Files Found" && \ + echo $OUTPUT | grep -qv "Update On Boot Enabled" && \ + echo $OUTPUT | grep -q "supervisorctl start ark-sa-updater" && \ + echo $OUTPUT | grep -qv "supervisorctl start ark-sa-server"' + +perform_test "Server Files Present With Default UPDATE_ON_BOOT (Default = True)" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh && \ + touch /ark-server/server/extra_file.txt && \ + /usr/local/bin/ark-sa-bootstrap.sh"); + echo $OUTPUT | grep -qv "No Server Files Found" && \ + echo $OUTPUT | grep -q "Update On Boot Enabled" && \ + echo $OUTPUT | grep -q "supervisorctl start ark-sa-updater" && \ + echo $OUTPUT | grep -qv "supervisorctl start ark-sa-server"' + +perform_test "Server Files Present With UPDATE_ON_BOOT=False" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e UPDATE_ON_BOOT=False \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh && \ + touch /ark-server/server/extra_file.txt && \ + /usr/local/bin/ark-sa-bootstrap.sh"); + echo $OUTPUT | grep -qv "No Server Files Found" && \ + echo $OUTPUT | grep -q "Update On Boot Disabled" && \ + echo $OUTPUT | grep -qv "supervisorctl start ark-sa-updater" && \ + echo $OUTPUT | grep -q "supervisorctl start ark-sa-server"' + +log_failed_tests diff --git a/tests/bin_scripts/ark-sa-server.sh b/tests/bin_scripts/ark-sa-server.sh new file mode 100644 index 0000000..ae667ea --- /dev/null +++ b/tests/bin_scripts/ark-sa-server.sh @@ -0,0 +1,170 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +perform_test "MAP=Not_TheIsland_WP - Map Is Not_TheIsland_WP" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e MAP="Not_TheIsland_WP" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "Not_TheIsland_WP"' + +perform_test "MAP Not Set - Defaults To TheIsland_WP" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "TheIsland_WP"' + +perform_test "GAME_PORT=12345 - Game Port Is Set To 12345" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e GAME_PORT=12345 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "?Port=12345"' + +perform_test "GAME_PORT Not Set - Defaults To 7777" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "?Port=7777"' + +perform_test "QUERY_PORT=12345 - Query Port Is Set To 12345" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e QUERY_PORT=12345 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "QueryPort=12345"' + +perform_test "QUERY_PORT Not Set - Defaults To 27015" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "QueryPort=27015"' + +perform_test "EXTRA_LAUNCH_OPTIONS='-ExtraFlag' - '-ExtraFlag' Appears In Launch Command" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e EXTRA_LAUNCH_OPTIONS="-ExtraFlag" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\-ExtraFlag"' + +perform_test "MULTI_HOME='192.168.1.2' - MultiHome Value Is 192.168.1.2" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e MULTI_HOME=192.168.1.2 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "?MultiHome=192.168.1.2"' + +perform_test "MULTI_HOME Not Set - No MultiHome Param" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -qv "?MultiHome="' + +perform_test "NO_BATTLEYE=False - Battleye Flag Is Not Present" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e NO_BATTLEYE=False \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -qv "\-NoBattlEye"' + +perform_test "NO_BATTLEYE Not Set - Battleye Flag Is Present" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\-NoBattlEye"' + +perform_test "EPIC_PUBLIC_IP=192.168.1.2 - --PublicIPforEpic Set to 192.168.1.2" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e EPIC_PUBLIC_IP=192.168.1.2 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\--PublicIPforEpic 192.168.1.2"' + +perform_test "EPIC_PUBLIC_IP Not Set - --PublicIPforEpic Flag Is Not Present" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -qv "\--PublicIPforEpic"' + +perform_test "MOD_LIST=\"1234,5678\" - -automanagedmods Flag Present With -mods=1234,5678" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e MOD_LIST='1234,5678' \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\-automanagedmods \-mods=1234,5678"' + +perform_test "MOD_LIST=\" 1234 , 5678\" - -automanagedmods Flag Present With -mods=1234,5678 (Spaces Removed)" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e MOD_LIST='1234,5678' \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\-automanagedmods \-mods=1234,5678"' + + perform_test "MOD_LIST Not Set - Not Mod Flags Present" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -qv "\-automanagedmods" &&\ + echo $OUTPUT | grep -qv "\-mods="' + +perform_test "MAX_PLAYERS=25 - Player Count Is 25" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + -e MAX_PLAYERS=25 \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\-WinLiveMaxPlayers=25"' + +perform_test "MAX_PLAYERS Not Set - Default Player Count Is 70" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-server.sh"); + echo $OUTPUT | grep -q "\-WinLiveMaxPlayers=70"' + +perform_test "Existing ShooterGame File Are Deleted To Avoid Premature Log Tail" \ + 'docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "mkdir -p /ark-server/server/ShooterGame/Saved/Logs && \ + touch /ark-server/server/ShooterGame/Saved/Logs/ShooterGame.log && \ + /usr/local/bin/ark-sa-server.sh | grep -q "Deleting Old Shooter Logs" \ + test ! -f /ark-server/server/ShooterGame/Saved/Logs/ShooterGame.log"' + +log_failed_tests diff --git a/tests/bin_scripts/ark-sa-updater.sh b/tests/bin_scripts/ark-sa-updater.sh new file mode 100644 index 0000000..fce4768 --- /dev/null +++ b/tests/bin_scripts/ark-sa-updater.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +perform_test "SKIP_FILE_VALIDATION=True - Skips Steam Validation" \ + "OUTPUT=\$(docker run --rm \ + -e DRY_RUN=True \ + -e SKIP_FILE_VALIDATION=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-updater.sh"); + echo \$OUTPUT | grep -qv 'validate'" + +perform_test "SKIP_FILE_VALIDATION=False - Steam Validation Is Performed" \ + "OUTPUT=\$(docker run --rm \ + -e DRY_RUN=True \ + -e SKIP_FILE_VALIDATION=False \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/ark-sa-updater.sh"); + echo \$OUTPUT | grep -q 'validate'" + +log_failed_tests diff --git a/tests/bin_scripts/system-bootstrap.sh b/tests/bin_scripts/system-bootstrap.sh new file mode 100644 index 0000000..bd98f9d --- /dev/null +++ b/tests/bin_scripts/system-bootstrap.sh @@ -0,0 +1,156 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +perform_test "Verify Default Scheduled Restart CRON Job" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_RESTART=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"0 4 \* \* \*\""' + +perform_test "Verify No Scheduled CRON If SCHEDULED_RESTART=False" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_RESTART=False \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + ! crontab -l"' + +perform_test "Verify Restart CRON Job Scheduled Correctly" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_RESTART=True \ + -e RESTART_CRON="10 * * * *" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"10 \* \* \* \*\""' + +perform_test "Verify Restart With Backup CRON Job Scheduled Correctly (Default Schedule)" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_RESTART=True \ + -e BACKUP_ON_SCHEDULED_RESTART=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"0 4 \* \* \*\" && + crontab -l | grep -q \"ark-sa-backup-and-restart\""' + +perform_test "Verify Restart With Backup CRON Job Scheduled Correctly (Non Default Schedule)" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_RESTART=True \ + -e BACKUP_ON_SCHEDULED_RESTART=True \ + -e RESTART_CRON="10 * * * *" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"10 \* \* \* \*\" && + crontab -l | grep -q \"ark-sa-backup-and-restart\""' + +perform_test "Verify Default Scheduled Update CRON Job" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_UPDATE=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"0 5 \* \* 0\""' + +perform_test "Verify No Scheduled CRON If SCHEDULED_UPDATE=False" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_UPDATE=False \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + ! crontab -l"' + +perform_test "Verify Update CRON Job Scheduled Correctly" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_UPDATE=True \ + -e UPDATE_CRON="10 * * * *" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"10 \* \* \* \*\""' + +perform_test "Verify Update With Backup CRON Job Scheduled Correctly (Default Schedule)" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_UPDATE=True \ + -e BACKUP_BEFORE_UPDATE=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"0 5 \* \* 0\" && + crontab -l | grep -q \"ark-sa-backup-and-update\""' + +perform_test "Verify Update With Backup CRON Job Scheduled Correctly (Non Default Schedule)" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_UPDATE=True \ + -e BACKUP_BEFORE_UPDATE=True \ + -e UPDATE_CRON="10 * * * *" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"10 \* \* \* \*\" && + crontab -l | grep -q \"ark-sa-backup-and-update\""' + +perform_test "Verify Default Scheduled Backup CRON Job" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_BACKUP=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"0 6 \* \* \*\""' + +perform_test "Verify No Scheduled CRON If SCHEDULED_BACKUP=False" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_BACKUP=False \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + ! crontab -l"' + +perform_test "Verify Backup CRON Job Scheduled Correctly" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_BACKUP=True \ + -e BACKUP_CRON="10 * * * *" \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"10 \* \* \* \*\""' + +perform_test "Verify Both Update, Restart, and Backup Can Be Scheduled Together" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SCHEDULED_RESTART=True \ + -e SCHEDULED_UPDATE=True \ + -e SCHEDULED_BACKUP=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh > /dev/null 2>&1 && + crontab -l | grep -q \"0 5 \* \* 0\" && \ + crontab -l | grep -q \"0 6 \* \* \*\" && \ + crontab -l | grep -q \"0 4 \* \* \*\""' + +perform_test "Verify Command To Launch Supervisord Would Have Been Called" \ + 'OUTPUT=$(docker run --rm \ + -e DRY_RUN=True \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "/usr/local/bin/system-bootstrap.sh"); + echo $OUTPUT | grep -q "exec /usr/bin/supervisord -c /usr/local/etc/supervisord.conf"' + +log_failed_tests diff --git a/tests/config_file_generation_adv.sh b/tests/config_file_generation_adv.sh new file mode 100644 index 0000000..5af479f --- /dev/null +++ b/tests/config_file_generation_adv.sh @@ -0,0 +1,331 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +###################################### +# Advance Environment Variable Tests # +###################################### + +GAME_SETTINGS_PATH="/ark-server/server/ShooterGame/Saved/Config/WindowsServer" +ARK_SA_BOOTSTRAP_PATH="/usr/local/bin/ark-sa-bootstrap.sh" + +perform_test "Single File With a Single Section With a Single Var" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +perform_test "Single File With a Single Section With Two Vars" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_TamingSpeedMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"TamingSpeedMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini "' + +perform_test "Single File With Two Sections Each With a Single Var" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e CONFIG_GameUserSettings_SessionSetting_SessionName=Test123 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[SessionSetting]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"SessionName=Test123\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +perform_test "Two Files Each With a Single Section and a Single Var" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e CONFIG_Game_SLASH_Script_SLASH_ShooterGame_DOT_ShooterGameMode_BabyImprintingStatScaleMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + test -f $GAME_SETTINGS_PATH/Game.ini && \ + grep -q \"[/Script/ShooterGame.ShooterGameMode]\" $GAME_SETTINGS_PATH/Game.ini && \ + grep -q \"BabyImprintingStatScaleMultiplier=1.0\" $GAME_SETTINGS_PATH/Game.ini"' + +perform_test "New CONFIG_ Introduced Between Runs Generates New Config Entry" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + export CONFIG_GameUserSettings_ServerSettings_PetXPMultiplier=2.0 && \ + $ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini.backup && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PetXPMultiplier=2.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +perform_test "New Config and Backup Config Generated If CONFIG_ Value Changed Between Runs" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + export CONFIG_GameUserSettings_ServerSettings_XPMultiplier=2.0 && \ + $ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini.backup && \ + grep -q \"XPMultiplier=2.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +perform_test "Backup Config Not Generated If CONFIG_ Value Does Not Change Between Runs" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + export CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 && \ + $ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + test ! -f $GAME_SETTINGS_PATH/GameUserSettings.ini.backup && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +perform_test "Removing CONFIG_ Enviroment Variable Between Run Deletes Config From File" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + unset CONFIG_GameUserSettings_ServerSettings_XPMultiplier && \ + $ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -vq \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +perform_test "Mega Test - Lots Of Vars" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e CONFIG_GameUserSettings_ServerSettings_DifficultyOffset=0.20 \ + -e CONFIG_GameUserSettings_ServerSettings_PlayerDamageMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_StructureResistanceMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_XPMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_TamingSpeedMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_HarvestAmountMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PlayerCharacterWaterDrainMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PlayerCharacterFoodDrainMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_DinoCharacterFoodDrainMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PlayerCharacterStaminaDrainMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_DinoCharacterStaminaDrainMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PlayerCharacterHealthRecoveryMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_DinoCharacterHealthRecoveryMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_HarvestHealthMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_StartTimeOverride=False \ + -e CONFIG_GameUserSettings_ServerSettings_StartTimeHour=10.0 \ + -e CONFIG_GameUserSettings_ServerSettings_ListenServerTetherDistanceMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_RaidDinoCharacterFoodDrainMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_StructurePreventResourceRadiusMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PvEDinoDecayPeriodMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_AllowRaidDinoFeeding=False \ + -e CONFIG_GameUserSettings_ServerSettings_PerPlatformMaxStructuresMultiplier=1 \ + -e CONFIG_GameUserSettings_ServerSettings_GlobalVoiceChat=False \ + -e CONFIG_GameUserSettings_ServerSettings_ProximityChat=False \ + -e CONFIG_GameUserSettings_ServerSettings_NoTributeDownloads=False \ + -e CONFIG_GameUserSettings_ServerSettings_AllowThirdPersonPlayer=True \ + -e CONFIG_GameUserSettings_ServerSettings_AlwaysNotifyPlayerLeft=False \ + -e CONFIG_GameUserSettings_ServerSettings_DontAlwaysNotifyPlayerJoined=False \ + -e CONFIG_GameUserSettings_ServerSettings_ServerHardcore=False \ + -e CONFIG_GameUserSettings_ServerSettings_ServerCrosshair=True \ + -e CONFIG_GameUserSettings_ServerSettings_ServerForceNoHUD=False \ + -e CONFIG_GameUserSettings_ServerSettings_ShowMapPlayerLocation=True \ + -e CONFIG_GameUserSettings_ServerSettings_EnablePvPGamma=True \ + -e CONFIG_GameUserSettings_ServerSettings_DisableStructureDecayPvE=False \ + -e CONFIG_GameUserSettings_ServerSettings_AllowFlyerCarryPvE=False \ + -e CONFIG_GameUserSettings_ServerSettings_OnlyAllowSpecifiedEngrams=False \ + -e CONFIG_GameUserSettings_ServerSettings_AllowHideDamageSourceFromLogs=True \ + -e CONFIG_GameUserSettings_ServerSettings_RandomSupplyCratePoints=False \ + -e CONFIG_GameUserSettings_ServerSettings_DisableWeatherFog=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventDownloadSurvivors=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventDownloadItems=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventDownloadDinos=False \ + -e CONFIG_GameUserSettings_ServerSettings_DisablePvEGamma=False \ + -e CONFIG_GameUserSettings_ServerSettings_DisableDinoDecayPvE=False \ + -e CONFIG_GameUserSettings_ServerSettings_AdminLogging=False \ + -e CONFIG_GameUserSettings_ServerSettings_AllowCaveBuildingPvE=False \ + -e CONFIG_GameUserSettings_ServerSettings_ForceAllowCaveFlyers=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventOfflinePvP=False \ + -e CONFIG_GameUserSettings_ServerSettings_PvPDinoDecay=False \ + -e CONFIG_GameUserSettings_ServerSettings_OverrideStructurePlatformPrevention=False \ + -e CONFIG_GameUserSettings_ServerSettings_AllowAnyoneBabyImprintCuddle=False \ + -e CONFIG_GameUserSettings_ServerSettings_DisableImprintDinoBuff=False \ + -e CONFIG_GameUserSettings_ServerSettings_ShowFloatingDamageText=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventDiseases=False \ + -e CONFIG_GameUserSettings_ServerSettings_NonPermanentDiseases=False \ + -e CONFIG_GameUserSettings_ServerSettings_EnableExtraStructurePreventionVolumes=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventTribeAlliances=False \ + -e CONFIG_GameUserSettings_ServerSettings_bAllowSpeedLeveling=False \ + -e CONFIG_GameUserSettings_ServerSettings_bAllowFlyerSpeedLeveling=False \ + -e CONFIG_GameUserSettings_ServerSettings_PreventOfflinePvPInterval=-0 \ + -e CONFIG_GameUserSettings_ServerSettings_CraftingSkillBonusMultiplier=1 \ + -e CONFIG_GameUserSettings_ServerSettings_SupplyCrateLootQualityMultiplier=1 \ + -e CONFIG_GameUserSettings_ServerSettings_ActiveEvent= \ + -e CONFIG_GameUserSettings_ServerSettings_OverrideStartTime=False \ + -e CONFIG_GameUserSettings_ServerSettings_ActiveMapMod=0 \ + -e CONFIG_GameUserSettings_ServerSettings_TheMaxStructuresInRange=10500 \ + -e CONFIG_GameUserSettings_ServerSettings_OxygenSwimSpeedStatMultiplier=1 \ + -e CONFIG_GameUserSettings_ServerSettings_TribeNameChangeCooldown=15 \ + -e CONFIG_GameUserSettings_ServerSettings_PlatformSaddleBuildAreaBoundsMultiplier=1 \ + -e CONFIG_GameUserSettings_ServerSettings_AlwaysAllowStructurePickup=True \ + -e CONFIG_GameUserSettings_ServerSettings_StructurePickupTimeAfterPlacement=30 \ + -e CONFIG_GameUserSettings_ServerSettings_StructurePickupHoldDuration=0.5 \ + -e CONFIG_GameUserSettings_ServerSettings_KickIdlePlayersPeriod=3600 \ + -e CONFIG_GameUserSettings_ServerSettings_AutoSavePeriodMinutes=15 \ + -e CONFIG_GameUserSettings_ServerSettings_MaxTamedDinos=5000 \ + -e CONFIG_GameUserSettings_ServerSettings_ItemStackSizeMultiplier=1 \ + -e CONFIG_GameUserSettings_ServerSettings_RCONServerGameLogBuffer=600 \ + -e CONFIG_GameUserSettings_ServerSettings_ImplantSuicideCD=28800 \ + -e CONFIG_GameUserSettings_ServerSettings_AllowHitMarkers=True \ + -e CONFIG_GameUserSettings_ServerSettings_DayCycleSpeedScale=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_DayTimeSpeedScale=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_NightTimeSpeedScale=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_DinoDamageMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_StructureDamageMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PlayerResistanceMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_DinoResistanceMultiplier=1.0 \ + -e CONFIG_GameUserSettings_ServerSettings_PvEStructureDecayPeriodMultiplier=1.0 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + test -f $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[ServerSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[SessionSettings]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"[/Script/ShooterGame.ShooterGameMode]\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DifficultyOffset=0.20\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlayerDamageMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StructureResistanceMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"XPMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"TamingSpeedMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"HarvestAmountMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlayerCharacterWaterDrainMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlayerCharacterFoodDrainMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DinoCharacterFoodDrainMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlayerCharacterStaminaDrainMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DinoCharacterStaminaDrainMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlayerCharacterHealthRecoveryMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DinoCharacterHealthRecoveryMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"HarvestHealthMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StartTimeOverride=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StartTimeHour=10.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ListenServerTetherDistanceMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"RaidDinoCharacterFoodDrainMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StructurePreventResourceRadiusMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PvEDinoDecayPeriodMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowRaidDinoFeeding=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PerPlatformMaxStructuresMultiplier=1\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"GlobalVoiceChat=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ProximityChat=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"NoTributeDownloads=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowThirdPersonPlayer=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AlwaysNotifyPlayerLeft=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DontAlwaysNotifyPlayerJoined=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ServerHardcore=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ServerCrosshair=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ServerForceNoHUD=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ShowMapPlayerLocation=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"EnablePvPGamma=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DisableStructureDecayPvE=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowFlyerCarryPvE=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"OnlyAllowSpecifiedEngrams=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowHideDamageSourceFromLogs=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"RandomSupplyCratePoints=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DisableWeatherFog=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventDownloadSurvivors=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventDownloadItems=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventDownloadDinos=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DisablePvEGamma=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DisableDinoDecayPvE=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AdminLogging=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowCaveBuildingPvE=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ForceAllowCaveFlyers=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventOfflinePvP=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PvPDinoDecay=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"OverrideStructurePlatformPrevention=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowAnyoneBabyImprintCuddle=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DisableImprintDinoBuff=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ShowFloatingDamageText=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventDiseases=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"NonPermanentDiseases=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"EnableExtraStructurePreventionVolumes=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventTribeAlliances=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"bAllowSpeedLeveling=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"bAllowFlyerSpeedLeveling=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PreventOfflinePvPInterval=-0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"CraftingSkillBonusMultiplier=1\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"SupplyCrateLootQualityMultiplier=1\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ActiveEvent=\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"OverrideStartTime=False\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ActiveMapMod=0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"TheMaxStructuresInRange=10500\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"OxygenSwimSpeedStatMultiplier=1\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"TribeNameChangeCooldown=15\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlatformSaddleBuildAreaBoundsMultiplier=1\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AlwaysAllowStructurePickup=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StructurePickupTimeAfterPlacement=30\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StructurePickupHoldDuration=0.5\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"KickIdlePlayersPeriod=3600\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AutoSavePeriodMinutes=15\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"MaxTamedDinos=5000\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ItemStackSizeMultiplier=1\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"RCONServerGameLogBuffer=600\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"ImplantSuicideCD=28800\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"AllowHitMarkers=True\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DayCycleSpeedScale=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DayTimeSpeedScale=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"NightTimeSpeedScale=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DinoDamageMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"StructureDamageMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PlayerResistanceMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"DinoResistanceMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini && \ + grep -q \"PvEStructureDecayPeriodMultiplier=1.0\" $GAME_SETTINGS_PATH/GameUserSettings.ini"' + +log_failed_tests diff --git a/tests/config_file_generation_core.sh b/tests/config_file_generation_core.sh new file mode 100644 index 0000000..77bc798 --- /dev/null +++ b/tests/config_file_generation_core.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +################################### +# Core Environment Variable Tests # +################################### + +GAME_SETTINGS_PATH="/ark-server/server/ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini" +ARK_SA_BOOTSTRAP_PATH="/usr/local/bin/ark-sa-bootstrap.sh" + +perform_test "Default Values In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"SessionName=Ark Server\" $GAME_SETTINGS_PATH && \ + grep -q \"RCONEnabled=True\" $GAME_SETTINGS_PATH && \ + grep -q \"RCONPort=27020\" $GAME_SETTINGS_PATH && \ + grep -q \"MaxPlayers=70\" $GAME_SETTINGS_PATH && \ + grep -q \"ServerPassword=$\" $GAME_SETTINGS_PATH && \ + grep -q \"ServerAdminPassword=adminpassword\" $GAME_SETTINGS_PATH && \ + grep -q \"ServerPVE=False\" $GAME_SETTINGS_PATH"' + +perform_test "ENABLE_PVE=True - ServerPVE=True In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e ENABLE_PVE=True \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"ServerPVE=True\" $GAME_SETTINGS_PATH"' + +perform_test "RCON_PORT=12345 - RCONPort=12345 In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e RCON_PORT=12345 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"RCONPort=12345\" $GAME_SETTINGS_PATH"' + +perform_test "ENABLE_RCON=False - RCONEnabled=False In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e ENABLE_RCON=False \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q 'RCONEnabled=False' $GAME_SETTINGS_PATH && + grep -q 'RCONPort=27020' $GAME_SETTINGS_PATH"' + +perform_test "SERVER_PASSWORD=password123 - ServerPassword=password123 In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SERVER_PASSWORD=password123 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"ServerPassword=password123\" $GAME_SETTINGS_PATH"' + +perform_test "ADMIN_PASSWORD=password123 - ServerAdminPassword=password123 In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e ADMIN_PASSWORD=password123 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"ServerAdminPassword=password123\" $GAME_SETTINGS_PATH"' + +perform_test "MAX_PLAYERS=25 - MaxPlayers=25 In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e MAX_PLAYERS=25 \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"MaxPlayers=25\" $GAME_SETTINGS_PATH"' + +perform_test "SERVER_NAME=Test_Server - SessionName=Test_Server In GameUserSettings.ini" \ + 'docker run --rm \ + -e DRY_RUN=True \ + -e SERVER_NAME=Test_Server \ + -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ + -e ARK_SA_BOOTSTRAP_PATH=${ARK_SA_BOOTSTRAP_PATH} \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "$ARK_SA_BOOTSTRAP_PATH > /dev/null 2>&1 && \ + grep -q \"SessionName=Test_Server\" $GAME_SETTINGS_PATH"' + +log_failed_tests diff --git a/tests/config_from_env_vars/__init__.py b/tests/config_from_env_vars/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/config_from_env_vars/test_main.py b/tests/config_from_env_vars/test_main.py new file mode 100644 index 0000000..924df46 --- /dev/null +++ b/tests/config_from_env_vars/test_main.py @@ -0,0 +1,328 @@ +# test_main.py + +import logging +from pathlib import Path +import unittest +from unittest import mock +from unittest.mock import MagicMock, patch, mock_open +from config_from_env_vars.main import ( + process_env_vars, + update_ini_files, + backup_existing_ini_files, + compare_and_cleanup_configs, + get_latest_backup_file, +) + +# logging.disable(logging.CRITICAL) + + +class TestConfigFromEnvVars(unittest.TestCase): + def setUp(self): + self.env_patcher = patch.dict("os.environ", clear=True) + self.env_patcher.start() + logging.disable(logging.NOTSET) + + def tearDown(self): + self.env_patcher.stop() + logging.disable(logging.ERROR) + + @patch.dict( + "os.environ", + {"CONFIG_GameUserSettings_ServerSettings_DifficultyOffset": "0.25"}, + ) + def test_process_env_vars_simple(self): + result = process_env_vars() + self.assertEqual( + result, + {"GameUserSettings": {"ServerSettings": {"DifficultyOffset": "0.25"}}}, + ) + + @patch.dict( + "os.environ", + { + "CONFIG_GameUserSettings_ServerSettings_DifficultyOffset": "0.25", + "CONFIG_AppearanceSettings_ServerSettings_DifficultyOffset": "0.75", + }, + ) + def test_process_env_vars_with_two_files(self): + result = process_env_vars() + expected = { + "GameUserSettings": {"ServerSettings": {"DifficultyOffset": "0.25"}}, + "AppearanceSettings": {"ServerSettings": {"DifficultyOffset": "0.75"}}, + } + self.assertEqual(result, expected) + + @patch.dict( + "os.environ", + { + "CONFIG_GameUserSettings_ServerSettings_DifficultyOffset": "0.25", + "CONFIG_GameUserSettings_PlayerSettings_DifficultyOffset": "0.75", + }, + ) + def test_process_env_vars_with_two_sections(self): + result = process_env_vars() + expected = { + "GameUserSettings": { + "ServerSettings": {"DifficultyOffset": "0.25"}, + "PlayerSettings": {"DifficultyOffset": "0.75"}, + } + } + self.assertEqual(result, expected) + + @patch.dict( + "os.environ", + { + "CONFIG_GameUserSettings_ServerSettings_DifficultyOffset": "0.25", + "CONFIG_GameUserSettings_ServerSettings_XPOffset": "0.75", + }, + ) + def test_process_env_vars_with_two_vars(self): + result = process_env_vars() + expected = { + "GameUserSettings": { + "ServerSettings": {"DifficultyOffset": "0.25", "XPOffset": "0.75"} + } + } + self.assertEqual(result, expected) + + @patch.dict( + "os.environ", + {"CONFIG_GameUserSettings_Server_DOT_Settings_DifficultyOffset": "0.25"}, + ) + def test_process_env_vars_with_dot_in_section(self): + result = process_env_vars() + self.assertEqual( + result, + {"GameUserSettings": {"Server.Settings": {"DifficultyOffset": "0.25"}}}, + ) + + @patch.dict( + "os.environ", + {"CONFIG_GameUserSettings_Server_SLASH_Settings_DifficultyOffset": "0.25"}, + ) + def test_process_env_vars_with_slash_in_section(self): + result = process_env_vars() + self.assertEqual( + result, + {"GameUserSettings": {"Server/Settings": {"DifficultyOffset": "0.25"}}}, + ) + + @patch.dict("os.environ", {"CONFIG_GameUserSettings_DifficultyOffset": "0.25"}) + def test_process_env_vars_with_missing_variable_template_section(self): + result = process_env_vars() + self.assertEqual(result, {}) + + @patch("builtins.open", new_callable=mock_open) + @patch("config_from_env_vars.main.MaintainCaseConfigParser") + def test_update_ini_files_single_file(self, mock_config, mock_file): + mock_config_data = { + "GameUserSettings": { + "ServerSettings": { + "DifficultyOffset": "0.25", + } + } + } + update_ini_files(mock_config_data, "/fake/path") + mock_file.assert_called_once_with("/fake/path/GameUserSettings.ini", "w") + mock_config.return_value.set.assert_called_once_with( + "ServerSettings", "DifficultyOffset", "0.25" + ) + mock_config.return_value.write.assert_called_once() + + @patch("builtins.open", new_callable=mock_open) + @patch("config_from_env_vars.main.MaintainCaseConfigParser") + def test_update_ini_files_multiple_file(self, mock_config, mock_file): + mock_config_data = { + "GameUserSettings": { + "ServerSettings": { + "DifficultyOffset": "0.25", + } + }, + "GameAppearance": { + "Logo": { + "Path": "/path/to/logo.png", + } + }, + } + update_ini_files(mock_config_data, "/fake/path") + mock_file.assert_any_call("/fake/path/GameUserSettings.ini", "w") + mock_file.assert_any_call("/fake/path/GameAppearance.ini", "w") + self.assertEqual(mock_config.return_value.write.call_count, 2) + + @patch("builtins.open", new_callable=mock_open) + @patch("config_from_env_vars.main.MaintainCaseConfigParser") + def test_update_ini_files_no_data_to_write(self, mock_config, mock_file): + mock_config_data = {} + update_ini_files(mock_config_data, "/fake/path") + mock_file.assert_not_called() + mock_config.return_value.set.assert_not_called() + mock_config.return_value.write.assert_not_called() + + @patch("config_from_env_vars.main.os.listdir") + @patch("config_from_env_vars.main.os.path.join") + @patch("config_from_env_vars.main.shutil.move") + @patch("config_from_env_vars.main.Path") + def test_backup_existing_ini_files( + self, mock_path, mock_move, mock_join, mock_listdir + ): + mock_listdir.return_value = ["config.ini"] + mock_join.side_effect = lambda a, b: f"{a}/{b}" + mock_path_obj = mock.Mock() + mock_path.return_value = mock_path_obj + mock_path_obj.exists.side_effect = [ + True, + False, + ] + + backup_existing_ini_files("/fake/path") + + mock_move.assert_called_once_with( + "/fake/path/config.ini", "/fake/path/config.ini.backup1" + ) + + @patch("config_from_env_vars.main.os.listdir") + @patch("config_from_env_vars.main.shutil.move") + def test_backup_existing_ini_files_empty_directory( + self, mock_move, mock_listdir + ): + mock_listdir.return_value = [] + + backup_existing_ini_files("/fake/path") + + mock_move.never_called() + + @patch("config_from_env_vars.main.os.listdir") + @patch("config_from_env_vars.main.shutil.move") + def test_backup_existing_ini_files_no_ini_files(self, mock_move, mock_listdir): + mock_listdir.return_value = ["config.txt"] + + backup_existing_ini_files("/fake/path") + + mock_move.never_called() + + @patch("config_from_env_vars.main.shutil.move") + @patch("config_from_env_vars.main.Path") + @patch("config_from_env_vars.main.os.listdir") + def test_backup_existing_ini_files_increment( + self, mock_listdir, mock_path, mock_move + ): + mock_listdir.return_value = ["config.ini"] + path_instance = MagicMock() + mock_path.return_value = path_instance + + path_instance.exists.side_effect = [True, True, False] + + backup_existing_ini_files("/fake/path") + + mock_move.assert_called_once_with( + "/fake/path/config.ini", "/fake/path/config.ini.backup2" + ) + + @patch("config_from_env_vars.main.shutil.move") + @patch("config_from_env_vars.main.Path") + @patch("config_from_env_vars.main.os.listdir") + def test_backup_existing_ini_files_multiple_ini( + self, mock_listdir, mock_path, mock_move + ): + mock_listdir.return_value = ["config.ini", "settings.ini"] + path_instance = MagicMock() + mock_path.return_value = path_instance + + path_instance.exists.side_effect = [False, False] + + backup_existing_ini_files("/fake/path") + + mock_move.assert_any_call( + "/fake/path/config.ini", "/fake/path/config.ini.backup" + ) + + mock_move.assert_any_call( + "/fake/path/settings.ini", "/fake/path/settings.ini.backup" + ) + + @patch("config_from_env_vars.main.Path") + def test_get_latest_backup_file_with_multiple_backups(self, mock_path): + directory = "/fake/path" + base_file_name = "config.ini" + mock_backup_files = [ + Path(f"{directory}/{base_file_name}.backup{i}") for i in range(1, 4) + ] + mock_path.return_value.glob.return_value = mock_backup_files + + latest_backup = get_latest_backup_file(f"{directory}/{base_file_name}") + + self.assertEqual(latest_backup, f"{directory}/{base_file_name}.backup3") + + @patch("config_from_env_vars.main.Path") + def test_get_latest_backup_file_with_single_backup(self, mock_path): + directory = "/fake/path" + base_file_name = "config.ini" + mock_backup_files = [Path(f"{directory}/{base_file_name}.backup")] + mock_path.return_value.glob.return_value = mock_backup_files + + latest_backup = get_latest_backup_file(f"{directory}/{base_file_name}") + + self.assertEqual(latest_backup, f"{directory}/{base_file_name}.backup") + + @patch("config_from_env_vars.main.shutil.move") + @patch("config_from_env_vars.main.Path") + @patch("config_from_env_vars.main.os.listdir") + def test_backup_existing_ini_files_error( + self, mock_listdir, mock_path, mock_move + ): + mock_listdir.return_value = ["config.ini"] + mock_path_obj = MagicMock() + mock_path.return_value = mock_path_obj + mock_path_obj.exists.return_value = False + + mock_move.side_effect = OSError("Mocked file access error") + + with self.assertLogs(level="ERROR") as log: + backup_existing_ini_files("/fake/path") + + self.assertIn("Mocked file access error", log.output[0]) + + @patch("config_from_env_vars.main.get_latest_backup_file") + @patch("config_from_env_vars.main.filecmp.cmp") + @patch("config_from_env_vars.main.os.remove") + @patch("config_from_env_vars.main.Path") + def test_compare_and_cleanup_configs_files_same( + self, mock_path, mock_remove, mock_cmp, mock_get_latest_backup_file + ): + mock_cmp.return_value = True + mock_path().glob.return_value = ["/fake/path/config.ini"] + mock_get_latest_backup_file.return_value = "/fake/path/config.ini.backup" + + compare_and_cleanup_configs("/fake/path") + + mock_remove.assert_called_once_with("/fake/path/config.ini.backup") + + @patch("config_from_env_vars.main.get_latest_backup_file") + @patch("config_from_env_vars.main.filecmp.cmp") + @patch("config_from_env_vars.main.os.remove") + @patch("config_from_env_vars.main.Path") + def test_compare_and_cleanup_configs_files_different( + self, mock_path, mock_remove, mock_cmp, mock_get_latest_backup_file + ): + mock_cmp.return_value = False + mock_path().glob.return_value = ["/fake/path/config.ini"] + mock_get_latest_backup_file.return_value = "/fake/path/config.ini.backup" + + compare_and_cleanup_configs("/fake/path") + + mock_remove.not_called() + + @patch("config_from_env_vars.main.filecmp.cmp") + @patch("config_from_env_vars.main.os.remove") + @patch("config_from_env_vars.main.Path") + def test_compare_and_cleanup_configs_backup_file_does_not_exist( + self, mock_path, mock_remove, mock_cmp + ): + mock_path().glob.return_value = [] + compare_and_cleanup_configs("/fake/path") + mock_cmp.assert_not_called() + mock_remove.assert_not_called() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/dependencies.sh b/tests/dependencies.sh new file mode 100644 index 0000000..0597c31 --- /dev/null +++ b/tests/dependencies.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +source ./tests/test_helper_functions.sh + +perform_test "Verify SteamCMD is Installed" \ + "docker run --rm \ + --entrypoint steamcmd \ + johnnyknighten/ark-sa-server:latest \ + +quit > /dev/null 2>&1" + +perform_test "Verify xvfb is Installed" \ + "docker run --rm \ + --entrypoint xvfb-run \ + johnnyknighten/ark-sa-server:latest \ + -h > /dev/null 2>&1" + +perform_test "Verify Supervisor is Installed" \ + "docker run --rm \ + --entrypoint supervisord \ + johnnyknighten/ark-sa-server:latest \ + -v > /dev/null 2>&1" + +perform_test "Verify CRON is Installed" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "echo \"* * * * * test\" > crontab.txt && crontab crontab.txt && crontab -l" > /dev/null 2>&1' + +perform_test "Verify tzdata is Installed" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "cat /usr/share/zoneinfo/tzdata.zi | head -n 1 | grep \"# version 2023c\"" > /dev/null 2>&1' + +perform_test "Verify tar is Installed" \ + 'docker run --rm \ + --entrypoint tar \ + johnnyknighten/ark-sa-server:latest \ + --version > /dev/null 2>&1' + +perform_test "Verify zip is Installed" \ + 'docker run --rm \ + --entrypoint zip \ + johnnyknighten/ark-sa-server:latest \ + -v > /dev/null 2>&1' + +perform_test "Verify unzip is Installed" \ + 'docker run --rm \ + --entrypoint unzip \ + johnnyknighten/ark-sa-server:latest \ + -v > /dev/null 2>&1' + +perform_test "Verify GE's Wine Proton Fork's Directory Exists" \ + "docker run --rm \ + --entrypoint test \ + johnnyknighten/ark-sa-server:latest \ + -d /opt/glorious_eggroll/proton" + +perform_test "Verify GE's Wine Proton Fork's Wine Executable Exists" \ + "docker run --rm \ + --entrypoint test \ + johnnyknighten/ark-sa-server:latest \ + -f /opt/glorious_eggroll/proton/bin/wine" + +perform_test "Verify supervisord.conf Is Present" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "test -f /usr/local/etc/supervisord.conf"' + +perform_test "Verify Python Is Installed" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "python3 --version"' + +perform_test "Verify ark-sa-container/bin Content is Present" \ + 'docker run --rm \ + --entrypoint bash \ + johnnyknighten/ark-sa-server:latest \ + -c "test -f /usr/local/bin/system-bootstrap.sh && \ + test -f /usr/local/bin/ark-sa-bootstrap.sh && \ + test -f /usr/local/bin/ark-sa-server.sh && \ + test -f /usr/local/bin/ark-sa-updater.sh && \ + test -f /usr/local/bin/ark-sa-backup.sh && \ + test -f /usr/local/bin/config_from_env_vars/__init__.py && \ + test -f /usr/local/bin/config_from_env_vars/main.py"' + +log_failed_tests diff --git a/tests/docker_dependencies.sh b/tests/docker_dependencies.sh deleted file mode 100644 index d6f8bb7..0000000 --- a/tests/docker_dependencies.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -source ./tests/test_helper_functions.sh - -perform_test "Verify SteamCMD is Installed" \ - "docker run --rm \ - --entrypoint steamcmd \ - johnnyknighten/ark-sa-server:latest \ - +quit > /dev/null 2>&1" - -perform_test "Verify xvfb is Installed" \ - "docker run --rm \ - --entrypoint xvfb-run \ - johnnyknighten/ark-sa-server:latest \ - -h > /dev/null 2>&1" - -perform_test "Verify GE's Wine Proton Fork's Directory Exists" \ - "docker run --rm \ - --entrypoint test \ - johnnyknighten/ark-sa-server:latest \ - -d /opt/glorious_eggroll/proton" - -perform_test "Verify GE's Wine Proton Fork's Wine Executable Exists" \ - "docker run --rm \ - --entrypoint test \ - johnnyknighten/ark-sa-server:latest \ - -f /opt/glorious_eggroll/proton/bin/wine" - -perform_test "Verify ark-sa-container/bin Content is Present" \ - "docker run --rm \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"test -f /opt/ark-sa-container/bin/docker-entrypoint.sh && \ - test -f /opt/ark-sa-container/bin/launch-ark-sa.sh && \ - test -f /opt/ark-sa-container/bin/steam-cmd-install.sh && \ - test -f /opt/ark-sa-container/bin/ark-sa/config-templating/bootstrap-configs.sh && \ - test -f /opt/ark-sa-container/bin/ark-sa/config-templating/GameUserSettings.template.ini \"" - -log_failed_tests diff --git a/tests/docker_environment_variables.sh b/tests/docker_environment_variables.sh deleted file mode 100644 index 5df9562..0000000 --- a/tests/docker_environment_variables.sh +++ /dev/null @@ -1,283 +0,0 @@ -#!/bin/bash - -source ./tests/test_helper_functions.sh - -###################### -# Command Args Tests # -###################### - -perform_test "STEAMCMD_SKIP_VALIDATION=True - Skips Steam Validation" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e STEAMCMD_SKIP_VALIDATION=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'SteamCMD Will Not Validate Ark SA Server Files'" - -perform_test "STEAMCMD_SKIP_VALIDATION=False - Steam Validation Is Performed" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e STEAMCMD_SKIP_VALIDATION=False \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'SteamCMD Will Validate Ark SA Server Files'" - -perform_test "ARK_PREVENT_AUTO_UPDATE=True - Update Is Skipped" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_PREVENT_AUTO_UPDATE=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'Skipping Auto-Update of Ark SA Server'" - -perform_test "ARK_MAP=Not_TheIsland_WP - Map Is Not_TheIsland_WP" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_MAP='Not_TheIsland_WP' \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'Not_TheIsland_WP'" - -perform_test "ARK_MAP Not Set - Defaults To TheIsland_WP" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'TheIsland_WP'" - -perform_test "ARK_GAME_PORT=12345 - Game Port Is Set To 12345" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_GAME_PORT=12345 \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\?Port=12345'" - -perform_test "ARK_GAME_PORT Not Set - Defaults To 7777" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\?Port=7777'" - -perform_test "ARK_QUERY_PORT=12345 - Query Port Is Set To 12345" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_QUERY_PORT=12345 \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'QueryPort=12345'" - -perform_test "ARK_QUERY_PORT Not Set - Defaults To 27015" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q 'QueryPort=27015'" - -perform_test "ARK_EXTRA_LAUNCH_OPTIONS='-ExtraFlag' - '-ExtraFlag' Appears In Launch Command" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_EXTRA_LAUNCH_OPTIONS='-ExtraFlag' \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\-ExtraFlag'" - -perform_test "ARK_MULTI_HOME='192.168.1.2' - MultiHome Value Is 192.168.1.2" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_MULTI_HOME=192.168.1.2 \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '?MultiHome=192.168.1.2'" - -perform_test "ARK_MULTI_HOME Not Set - No MultiHome Param" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -qv '?MultiHome='" - -perform_test "ARK_NO_BATTLEYE=False - Battleye Flag Is Not Present" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_NO_BATTLEYE=False \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -qv '\-NoBattlEye'" - -perform_test "ARK_NO_BATTLEYE Not Set - Battleye Flag Is Present" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\-NoBattlEye'" - -perform_test "ARK_EPIC_PUBLIC_IP=192.168.1.2 - --PublicIPforEpic Set to 192.168.1.2" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_EPIC_PUBLIC_IP=192.168.1.2 \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\--PublicIPforEpic 192.168.1.2'" - -perform_test "ARK_EPIC_PUBLIC_IP Not Set - --PublicIPforEpic Flag Is Not Present" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -qv '\--PublicIPforEpic'" - -perform_test "ARK_MOD_LIST=\"1234,5678\" - -automanagedmods Flag Present With -mods=1234,5678" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_MOD_LIST='1234,5678' \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\-automanagedmods \-mods=1234,5678'" - -perform_test "ARK_MOD_LIST=\" 1234 , 5678\" - -automanagedmods Flag Present With -mods=1234,5678 (Spaces Removed)" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_MOD_LIST='1234,5678' \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\-automanagedmods \-mods=1234,5678'" - - perform_test "ARK_MOD_LIST Not Set - Not Mod Flags Present" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -qv '\-automanagedmods' &&\ - echo \$OUTPUT | grep -qv '\-mods='" - -perform_test "ARK_MAX_PLAYERS=25 - Player Count Is 25" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - -e ARK_MAX_PLAYERS=25 \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\-WinLiveMaxPlayers=25'" - -perform_test "ARK_MAX_PLAYERS Not Set - Default Player Count Is 70" \ - "OUTPUT=\$(docker run --rm \ - -e TEST_DRY_RUN=True \ - johnnyknighten/ark-sa-server:latest); - echo \$OUTPUT | grep -q '\-WinLiveMaxPlayers=70'" - -##################### -# Config File Tests # -##################### - -GAME_SETTINGS_PATH="/ark-server/server/ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini" -CONFIG_BOOTSTRAP_PATH="/opt/ark-sa-container/bin/ark-sa/config-templating/bootstrap-configs.sh" - -perform_test "ARK_SERVER_NAME='Test Server' - SessionName='Test Server' In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_SERVER_NAME='Test Server' \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'SessionName=Test Server' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_SERVER_NAME Not Set - Defaults To SessionName='Ark Server' In GameUserSettings.ini" \ - "docker run --rm \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'SessionName=Ark Server' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_ENABLE_PVE=True - ServerPVE=True In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_ENABLE_PVE=True \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'ServerPVE=True' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_ENABLE_PVE Not Set - ServerPVE=False In GameUserSettings.ini" \ - "docker run --rm \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'ServerPVE=False' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_ENABLE_RCON Not Set, ARK_RCON_PORT Not Set - RCONEnabled=True and RCONPort=27020 In GameUserSettings.ini" \ - "docker run --rm \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'RCONEnabled=True' $GAME_SETTINGS_PATH && - grep -q 'RCONPort=27020' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_ENABLE_RCON Not Set, ARK_RCON_PORT=12345 - RCONEnabled=True and RCONPort=12345 In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_RCON_PORT=12345 \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'RCONEnabled=True' $GAME_SETTINGS_PATH && - grep -q 'RCONPort=12345' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_ENABLE_RCON=False ARK_RCON_PORT Not Set - RCONEnabled=False and RCONPort=27020 In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_ENABLE_RCON=False \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'RCONEnabled=False' $GAME_SETTINGS_PATH && - grep -q 'RCONPort=27020' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_SERVER_PASSWORD=password123 - ServerPassword=password123 In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_SERVER_PASSWORD=password123 \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'ServerPassword=password123' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_SERVER_PASSWORD Not Set - ServerPassword is Blank In GameUserSettings.ini" \ - "docker run --rm \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q '^ServerPassword=\s*$' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_SERVER_ADMIN_PASSWORD=password123 - ServerAdminPassword=password123 In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_SERVER_ADMIN_PASSWORD=password123 \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'ServerAdminPassword=password123' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_SERVER_ADMIN_PASSWORD Not Set - ServerAdminPassword=adminpassword In GameUserSettings.ini" \ - "docker run --rm \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'ServerAdminPassword=adminpassword' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_MAX_PLAYERS=25 - MaxPlayers=25 In GameUserSettings.ini" \ - "docker run --rm \ - -e ARK_MAX_PLAYERS=25 \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'MaxPlayers=25' $GAME_SETTINGS_PATH\"" - -perform_test "ARK_MAX_PLAYERS Not Set - MaxPlayers=70 In GameUserSettings.ini" \ - "docker run --rm \ - -e GAME_SETTINGS_PATH=${GAME_SETTINGS_PATH} \ - -e CONFIG_BOOTSTRAP_PATH=${CONFIG_BOOTSTRAP_PATH} \ - --entrypoint bash \ - johnnyknighten/ark-sa-server:latest \ - -c \"$CONFIG_BOOTSTRAP_PATH > /dev/null 2>&1 && \ - grep -q 'MaxPlayers=70' $GAME_SETTINGS_PATH\"" - -log_failed_tests