From 30b49340136f8965954174dcbda842293fda3486 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 27 Apr 2017 16:16:57 -0400 Subject: [PATCH] Include default installation scripts, as well as ability to symlink a script --- .../Controllers/Admin/OptionController.php | 16 +++- .../Controllers/Daemon/OptionController.php | 6 +- app/Models/ServiceOption.php | 38 ++++++++++ app/Repositories/OptionRepository.php | 17 ++++- ...7_04_27_145300_AddCopyScriptFromColumn.php | 35 +++++++++ .../seeds/MinecraftServiceTableSeeder.php | 74 +++++++++++++++++++ database/seeds/SourceServiceTableSeeder.php | 54 ++++++++++++++ database/seeds/TerrariaServiceTableSeeder.php | 16 ++++ database/seeds/VoiceServiceTableSeeder.php | 55 ++++++++++++++ .../admin/servers/view/index.blade.php | 5 +- .../admin/services/options/new.blade.php | 2 +- .../admin/services/options/scripts.blade.php | 39 +++++++++- 12 files changed, 346 insertions(+), 11 deletions(-) create mode 100644 database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index f7c5ac6db..f4a70363a 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -146,7 +146,16 @@ class OptionController extends Controller */ public function viewScripts(Request $request, $id) { - return view('admin.services.options.scripts', ['option' => ServiceOption::findOrFail($id)]); + $option = ServiceOption::with('copyFrom')->findOrFail($id); + + return view('admin.services.options.scripts', [ + 'copyFromOptions' => ServiceOption::whereNull('copy_script_from')->where([ + ['service_id', $option->service_id], + ['id', '!=', $option->id], + ])->get(), + 'relyOnScript' => ServiceOption::where('copy_script_from', $option->id)->get(), + 'option' => $option, + ]); } /** @@ -234,11 +243,14 @@ class OptionController extends Controller try { $repo->scripts($id, $request->only([ - 'script_install', 'script_entry', 'script_container', + 'script_install', 'script_entry', + 'script_container', 'copy_script_from', ])); Alert::success('Successfully updated option scripts to be run when servers are installed.')->flash(); } catch (DisplayValidationException $ex) { return redirect()->route('admin.services.option.scripts', $id)->withErrors(json_decode($ex->getMessage())); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception was encountered while attempting to process that request. This error has been logged.')->flash(); diff --git a/app/Http/Controllers/Daemon/OptionController.php b/app/Http/Controllers/Daemon/OptionController.php index 7d658672a..e7aac2feb 100644 --- a/app/Http/Controllers/Daemon/OptionController.php +++ b/app/Http/Controllers/Daemon/OptionController.php @@ -40,12 +40,12 @@ class OptionController extends Controller return response()->json([ 'scripts' => [ - 'install' => (! $server->option->script_install) ? null : str_replace(["\r\n", "\n", "\r"], "\n", $server->option->script_install), + 'install' => (! $server->option->copy_script_install) ? null : str_replace(["\r\n", "\n", "\r"], "\n", $server->option->copy_script_install), 'privileged' => $server->option->script_is_privileged, ], 'config' => [ - 'container' => $server->option->script_container, - 'entry' => $server->option->script_entry, + 'container' => $server->option->copy_script_container, + 'entry' => $server->option->copy_script_entry, ], 'env' => $environment->merge([ 'STARTUP=' . $server->startup, diff --git a/app/Models/ServiceOption.php b/app/Models/ServiceOption.php index bbd18e675..07f21b448 100644 --- a/app/Models/ServiceOption.php +++ b/app/Models/ServiceOption.php @@ -63,6 +63,39 @@ class ServiceOption extends Model return (is_null($this->startup)) ? $this->service->startup : $this->startup; } + /** + * Returns the install script for the option; if option is copying from another + * it will return the copied script. + * + * @return string + */ + public function getCopyScriptInstallAttribute($value) + { + return (is_null($this->copy_script_from)) ? $this->script_install : $this->copyFrom->script_install; + } + + /** + * Returns the entry command for the option; if option is copying from another + * it will return the copied entry command. + * + * @return string + */ + public function getCopyScriptEntryAttribute($value) + { + return (is_null($this->copy_script_from)) ? $this->script_entry : $this->copyFrom->script_entry; + } + + /** + * Returns the install container for the option; if option is copying from another + * it will return the copied install container. + * + * @return string + */ + public function getCopyScriptContainerAttribute($value) + { + return (is_null($this->copy_script_from)) ? $this->script_container : $this->copyFrom->script_container; + } + /** * Gets service associated with a service option. * @@ -102,4 +135,9 @@ class ServiceOption extends Model { return $this->hasMany(Pack::class, 'option_id'); } + + public function copyFrom() + { + return $this->belongsTo(ServiceOption::class, 'copy_script_from'); + } } diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/OptionRepository.php index 53dab8c57..346a452c8 100644 --- a/app/Repositories/OptionRepository.php +++ b/app/Repositories/OptionRepository.php @@ -49,7 +49,7 @@ class OptionRepository 'description' => 'required|string', 'tag' => 'required|string|max:255|unique:service_options,tag', 'docker_image' => 'required|string|max:255', - 'startup' => 'required|string', + 'startup' => 'sometimes|nullable|string', 'config_from' => 'sometimes|required|numeric|exists:service_options,id', 'config_startup' => 'required_without:config_from|json', 'config_stop' => 'required_without:config_from|string|max:255', @@ -162,6 +162,7 @@ class OptionRepository * @param array $data * @return \Pterodactyl\Models\ServiceOption * + * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function scripts($id, array $data) @@ -175,8 +176,22 @@ class OptionRepository 'script_is_privileged' => 'sometimes|required|boolean', 'script_entry' => 'sometimes|required|string', 'script_container' => 'sometimes|required|string', + 'copy_script_from' => 'sometimes|nullable|numeric', ]); + if (isset($data['copy_script_from']) && ! empty($data['copy_script_from'])) { + $select = ServiceOption::whereNull('copy_script_from')->where([ + ['id', $data['copy_script_from']], + ['service_id', $option->service_id], + ])->first(); + + if (! $select) { + throw new DisplayException('The service option selected to copy a script from either does not exist, or is copying from a higher level.'); + } + } else { + $data['copy_script_from'] = null; + } + if ($validator->fails()) { throw new DisplayValidationException(json_encode($validator->errors())); } diff --git a/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php b/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php new file mode 100644 index 000000000..027d1964b --- /dev/null +++ b/database/migrations/2017_04_27_145300_AddCopyScriptFromColumn.php @@ -0,0 +1,35 @@ +unsignedInteger('copy_script_from')->nullable()->after('script_container'); + + $table->foreign('copy_script_from')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign(['copy_script_from']); + $table->dropColumn('copy_script_from'); + }); + } +} diff --git a/database/seeds/MinecraftServiceTableSeeder.php b/database/seeds/MinecraftServiceTableSeeder.php index 9086c3fbb..c2ed986e3 100644 --- a/database/seeds/MinecraftServiceTableSeeder.php +++ b/database/seeds/MinecraftServiceTableSeeder.php @@ -110,6 +110,27 @@ EOF; private function addCoreOptions() { + $script = <<<'EOF' +#!/bin/ash +# Vanilla MC Installation Script +# +# Server Files: /mnt/server +apk update +apk add curl + +cd /mnt/server + +LATEST_VERSION=`curl -s https://s3.amazonaws.com/Minecraft.Download/versions/versions.json | grep -o "[[:digit:]]\.[0-9]*\.[0-9]" | head -n 1` + +if [ -z "$VANILLA_VERSION" ] || [ "$VANILLA_VERSION" == "latest" ]; then + DL_VERSION=$LATEST_VERSION +else + DL_VERSION=$VANILLA_VERSION +fi + +curl -o ${SERVER_JARFILE} https://s3.amazonaws.com/Minecraft.Download/versions/${DL_VERSION}/minecraft_server.${DL_VERSION}.jar +EOF; + $this->option['vanilla'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'vanilla', @@ -123,8 +144,27 @@ EOF; 'config_stop' => 'stop', 'config_from' => null, 'startup' => null, + 'script_install' => $script, ]); + $script = <<<'EOF' +#!/bin/ash +# Spigot Installation Script +# +# Server Files: /mnt/server + +## Only download if a path is provided, otherwise continue. +if [ ! -z "${DL_PATH}" ]; then + apk update + apk add curl + + cd /mnt/server + + MODIFIED_DOWNLOAD=`eval echo $(echo ${DL_PATH} | sed -e 's/{{/${/g' -e 's/}}/}/g')` + curl -sSL -o ${SERVER_JARFILE} ${MODIFIED_DOWNLOAD} +fi +EOF; + $this->option['spigot'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'spigot', @@ -138,8 +178,23 @@ EOF; 'config_stop' => null, 'config_from' => $this->option['vanilla']->id, 'startup' => null, + 'script_install' => $script, ]); + $script = <<<'EOF' +#!/bin/ash +# Sponge Installation Script +# +# Server Files: /mnt/server + +apk update +apk add curl + +cd /mnt/server + +curl -sSL "https://repo.spongepowered.org/maven/org/spongepowered/spongevanilla/${SPONGE_VERSION}/spongevanilla-${SPONGE_VERSION}.jar" -o ${SERVER_JARFILE} +EOF; + $this->option['sponge'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'sponge', @@ -153,8 +208,26 @@ EOF; 'config_stop' => null, 'config_from' => $this->option['vanilla']->id, 'startup' => null, + 'script_install' => $script, ]); + $script = <<<'EOF' +#!/bin/ash +# Bungeecord Installation Script +# +# Server Files: /mnt/server +apk update +apk add curl + +cd /mnt/server + +if [ -z "${BUNGEE_VERSION}" ] || [ "${BUNGEE_VERSION}" == "latest" ]; then + BUNGEE_VERSION="lastStableBuild" +fi + +curl -o ${SERVER_JARFILE} https://ci.md-5.net/job/BungeeCord/${BUNGEE_VERSION}/artifact/bootstrap/target/BungeeCord.jar +EOF; + $this->option['bungeecord'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'bungeecord', @@ -168,6 +241,7 @@ EOF; 'config_stop' => 'end', 'config_from' => null, 'startup' => null, + 'script_install' => $script, ]); } diff --git a/database/seeds/SourceServiceTableSeeder.php b/database/seeds/SourceServiceTableSeeder.php index 46f061365..5130308e7 100644 --- a/database/seeds/SourceServiceTableSeeder.php +++ b/database/seeds/SourceServiceTableSeeder.php @@ -69,6 +69,27 @@ class SourceServiceTableSeeder extends Seeder private function addCoreOptions() { + $script = <<<'EOF' +#!/bin/bash +# SRCDS Base Installation Script +# +# Server Files: /mnt/server +apt update +apt install curl + +cd /tmp +curl -sSL -o steamcmd.tar.gz http://media.steampowered.com/installer/steamcmd_linux.tar.gz + +mkdir /mnt/server/steamcmd +tar -xzvf steamcmd.tar.gz -C /mnt/server/steamcmd +cd /mnt/server/steamcmd + +./steamcmd.sh +login anonymous +force_install_dir /mnt/server +app_update ${SRCDS_APPID} +quit + +mkdir -p /mnt/server/.steam/sdk32 +cp -v linux32/steamclient.so ../.steam/sdk32/steamclient.so +EOF; + $this->option['source'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'source', @@ -82,6 +103,9 @@ class SourceServiceTableSeeder extends Seeder 'config_stop' => 'quit', 'config_from' => null, 'startup' => null, + 'script_install' => $script, + 'script_entry' => 'bash', + 'script_container' => 'ubuntu:16.04', ]); $this->option['insurgency'] = ServiceOption::updateOrCreate([ @@ -97,6 +121,7 @@ class SourceServiceTableSeeder extends Seeder 'config_stop' => null, 'config_from' => $this->option['source']->id, 'startup' => './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart', + 'copy_script_from' => $this->option['source']->id, ]); $this->option['tf2'] = ServiceOption::updateOrCreate([ @@ -112,8 +137,34 @@ class SourceServiceTableSeeder extends Seeder 'config_stop' => null, 'config_from' => $this->option['source']->id, 'startup' => './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart', + 'copy_script_from' => $this->option['source']->id, ]); + $script = <<<'EOF' +#!/bin/bash +# ARK: Installation Script +# +# Server Files: /mnt/server +apt update +apt install curl + +cd /tmp +curl -sSL -o steamcmd.tar.gz http://media.steampowered.com/installer/steamcmd_linux.tar.gz + +mkdir /mnt/server/steamcmd +mkdir -p /mnt/server/Engine/Binaries/ThirdParty/SteamCMD/Linux + +tar -xzvf steamcmd.tar.gz -C /mnt/server/steamcmd +tar -xzvf steamcmd.tar.gz -C /mnt/server/Engine/Binaries/ThirdParty/SteamCMD/Linux + +cd /mnt/server/steamcmd + +./steamcmd.sh +login anonymous +force_install_dir /mnt/server +app_update 376030 +quit + +mkdir -p /mnt/server/.steam/sdk32 +cp -v linux32/steamclient.so ../.steam/sdk32/steamclient.so +EOF; + $this->option['ark'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'ark', @@ -127,6 +178,9 @@ class SourceServiceTableSeeder extends Seeder 'config_stop' => '^C', 'config_from' => $this->option['source']->id, 'startup' => './ShooterGame/Binaries/Linux/ShooterGameServer TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}', + 'script_install' => $script, + 'script_entry' => 'bash', + 'script_container' => 'ubuntu:16.04', ]); } diff --git a/database/seeds/TerrariaServiceTableSeeder.php b/database/seeds/TerrariaServiceTableSeeder.php index ec4e0d52f..6d451f12b 100644 --- a/database/seeds/TerrariaServiceTableSeeder.php +++ b/database/seeds/TerrariaServiceTableSeeder.php @@ -69,6 +69,21 @@ class TerrariaServiceTableSeeder extends Seeder private function addCoreOptions() { + $script = <<<'EOF' +#!/bin/ash +# TShock Installation Script +# +# Server Files: /mnt/server +apk update +apk add curl unzip + +cd /tmp + +curl -sSLO https://github.com/NyxStudios/TShock/releases/download/v${T_VERSION}/tshock_${T_VERSION}.zip + +unzip -o tshock_${T_VERSION}.zip -d /mnt/server +EOF; + $this->option['tshock'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'tshock', @@ -82,6 +97,7 @@ class TerrariaServiceTableSeeder extends Seeder 'config_logs' => '{"custom": false, "location": "ServerLog.txt"}', 'config_stop' => 'exit', 'startup' => null, + 'script_install' => $script, ]); } diff --git a/database/seeds/VoiceServiceTableSeeder.php b/database/seeds/VoiceServiceTableSeeder.php index 52c121463..07a8d0049 100644 --- a/database/seeds/VoiceServiceTableSeeder.php +++ b/database/seeds/VoiceServiceTableSeeder.php @@ -69,6 +69,22 @@ class VoiceServiceTableSeeder extends Seeder private function addCoreOptions() { + $script = <<<'EOF' +#!/bin/ash +# Mumble Installation Script +# +# Server Files: /mnt/server +apk update +apk add tar curl + +cd /tmp + +curl -sSLO https://github.com/mumble-voip/mumble/releases/download/${MUMBLE_VERSION}/murmur-static_x86-${MUMBLE_VERSION}.tar.bz2 + +tar -xjvf murmur-static_x86-${MUMBLE_VERSION}.tar.bz2 +cp -r murmur-static_x86-${MUMBLE_VERSION}/* /mnt/server +EOF; + $this->option['mumble'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'mumble', @@ -82,8 +98,46 @@ class VoiceServiceTableSeeder extends Seeder 'config_stop' => '^C', 'config_from' => null, 'startup' => './murmur.x86 -fg', + 'script_install' => $script, ]); + $script = <<<'EOF' +#!/bin/ash +# TS3 Installation Script +# +# Server Files: /mnt/server +apk update +apk add tar curl + +cd /tmp + +curl -sSLO http://dl.4players.de/ts/releases/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 + +tar -xjvf teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 +cp -r teamspeak3-server_linux_amd64/* /mnt/server + +echo "machine_id= +default_voice_port=${SERVER_PORT} +voice_ip=0.0.0.0 +licensepath= +filetransfer_port=30033 +filetransfer_ip= +query_port=${SERVER_PORT} +query_ip=0.0.0.0 +query_ip_whitelist=query_ip_whitelist.txt +query_ip_blacklist=query_ip_blacklist.txt +dbplugin=ts3db_sqlite3 +dbpluginparameter= +dbsqlpath=sql/ +dbsqlcreatepath=create_sqlite/ +dbconnections=10 +logpath=logs +logquerycommands=0 +dbclientkeepdays=30 +logappend=0 +query_skipbruteforcecheck=0" > /mnt/server/ts3server.ini +EOF; + $this->option['ts3'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, 'tag' => 'ts3', @@ -97,6 +151,7 @@ class VoiceServiceTableSeeder extends Seeder 'config_stop' => '^C', 'config_from' => null, 'startup' => './ts3server_minimal_runscript.sh default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}}', + 'script_install' => $script, ]); } diff --git a/resources/themes/pterodactyl/admin/servers/view/index.blade.php b/resources/themes/pterodactyl/admin/servers/view/index.blade.php index 6c78636ab..ee492c0ca 100644 --- a/resources/themes/pterodactyl/admin/servers/view/index.blade.php +++ b/resources/themes/pterodactyl/admin/servers/view/index.blade.php @@ -79,7 +79,10 @@ Service - {{ $server->option->service->name }} :: {{ $server->option->name }} + + {{ $server->option->service->name }} :: + {{ $server->option->name }} + Name diff --git a/resources/themes/pterodactyl/admin/services/options/new.blade.php b/resources/themes/pterodactyl/admin/services/options/new.blade.php index 763e6096c..7c4da15ee 100644 --- a/resources/themes/pterodactyl/admin/services/options/new.blade.php +++ b/resources/themes/pterodactyl/admin/services/options/new.blade.php @@ -47,7 +47,7 @@ diff --git a/resources/themes/pterodactyl/admin/services/options/scripts.blade.php b/resources/themes/pterodactyl/admin/services/options/scripts.blade.php index 998247f70..f94f9f9a0 100644 --- a/resources/themes/pterodactyl/admin/services/options/scripts.blade.php +++ b/resources/themes/pterodactyl/admin/services/options/scripts.blade.php @@ -52,27 +52,58 @@

Install Script

+ @if(! is_null($option->copyFrom)) +
+
+ This service option is copying installation scripts and containe options from {{ $option->copyFrom->name }}. Any changes you make to this script will not apply unless you select "None" from the dropdown box below. +
+
+ @endif
{{ $option->script_install }}
-
+
+ + +

If selected, script above will be ignored and script from selected option will be used in place.

+
+

Docker container to use when running this script for the server.

-
+

The entrypoint command to use for this script.

+
+
+ The following service options rely on this script: + @if(count($relyOnScript) > 0) + @foreach($relyOnScript as $rely) + + {{ $rely->name }}  + + @endforeach + @else + none + @endif +
+
@@ -86,6 +117,8 @@ {!! Theme::js('vendor/ace/ext-modelist.js') !!}