Add ability to change service implementation for a server.

This commit is contained in:
Dane Everitt 2017-04-21 17:52:44 -04:00
parent e0958414d7
commit a5a1ea3165
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
5 changed files with 383 additions and 85 deletions

View file

@ -198,7 +198,24 @@ class ServersController extends Controller
return $item;
});
return view('admin.servers.view.startup', ['server' => $server]);
$services = Models\Service::with('options.packs', 'options.variables')->get();
Javascript::put([
'services' => $services->map(function ($item) {
return array_merge($item->toArray(), [
'options' => $item->options->keyBy('id')->toArray(),
]);
})->keyBy('id'),
'server_variables' => $server->variables->mapWithKeys(function ($item) {
return ['env_' . $item->variable_id => [
'value' => $item->variable_value,
]];
})->toArray(),
]);
return view('admin.servers.view.startup', [
'server' => $server,
'services' => $services,
]);
}
/**
@ -479,9 +496,13 @@ class ServersController extends Controller
$repo = new ServerRepository;
try {
$repo->updateStartup($id, $request->except('_token'), true);
if ($repo->updateStartup($id, $request->except('_token'), true)) {
Alert::success('Service configuration successfully modfied for this server, reinstalling now.')->flash();
Alert::success('Startup variables were successfully modified and assigned for this server.')->flash();
return redirect()->route('admin.servers.view', $id);
} else {
Alert::success('Startup variables were successfully modified and assigned for this server.')->flash();
}
} catch (DisplayValidationException $ex) {
return redirect()->route('admin.servers.view.startup', $id)->withErrors(json_decode($ex->getMessage()));
} catch (DisplayException $ex) {

View file

@ -72,6 +72,7 @@ class Server extends Model
*/
protected $casts = [
'node_id' => 'integer',
'skip_scripts' => 'boolean',
'suspended' => 'integer',
'owner_id' => 'integer',
'memory' => 'integer',

View file

@ -103,7 +103,7 @@ class ServerRepository
'startup' => 'string',
'auto_deploy' => 'sometimes|required|accepted',
'custom_id' => 'sometimes|required|numeric|unique:servers,id',
'skip_scripting' => 'sometimes|required|boolean',
'skip_scripts' => 'sometimes|required|boolean',
]);
$validator->sometimes('node_id', 'required|numeric|min:1|exists:nodes,id', function ($input) {
@ -250,14 +250,15 @@ class ServerRepository
'node_id' => $node->id,
'name' => $data['name'],
'description' => $data['description'],
'suspended' => 0,
'skip_scripts' => isset($data['skip_scripts']),
'suspended' => false,
'owner_id' => $user->id,
'memory' => $data['memory'],
'swap' => $data['swap'],
'disk' => $data['disk'],
'io' => $data['io'],
'cpu' => $data['cpu'],
'oom_disabled' => (isset($data['oom_disabled'])) ? true : false,
'oom_disabled' => isset($data['oom_disabled']),
'allocation_id' => $allocation->id,
'service_id' => $data['service_id'],
'option_id' => $data['option_id'],
@ -327,7 +328,7 @@ class ServerRepository
'type' => $service->folder,
'option' => $option->tag,
'pack' => (isset($pack)) ? $pack->uuid : null,
'skip_scripting' => isset($data['skip_scripting']),
'skip_scripts' => $server->skip_scripts,
],
'keys' => [
(string) $server->daemonSecret => $this->daemonPermissions,
@ -621,13 +622,93 @@ class ServerRepository
}
}
/**
* Update the service configuration for a server.
*
* @param int $id
* @param array $data
* @return void
*
* @throws \GuzzleHttp\Exception\RequestException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\DisplayValidationException
*/
protected function changeService($id, array $data)
{
}
protected function processVariables(Models\Server $server, $data, $admin = false)
{
$server->load('option.variables');
if ($admin) {
$server->startup = $data['startup'];
$server->save();
}
if ($server->option->variables) {
foreach ($server->option->variables as &$variable) {
$set = isset($data['env_' . $variable->id]);
// If user is not an admin and are trying to edit a non-editable field
// or an invisible field just silently skip the variable.
if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) {
continue;
}
// Perform Field Validation
$validator = Validator::make([
'variable_value' => ($set) ? $data['env_' . $variable->id] : null,
], [
'variable_value' => $variable->rules,
]);
if ($validator->fails()) {
throw new DisplayValidationException(json_encode(
collect([
'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'],
])->merge($validator->errors()->toArray())
));
}
$svar = Models\ServerVariable::firstOrNew([
'server_id' => $server->id,
'variable_id' => $variable->id,
]);
// Set the value; if one was not passed set it to the default value
if ($set) {
$svar->variable_value = $data['env_' . $variable->id];
// Not passed, check if this record exists if so keep value, otherwise set default
} else {
$svar->variable_value = ($svar->exists) ? $svar->variable_value : $variable->default_value;
}
$svar->save();
}
}
// Reload Variables
$server->load('variables');
return $server->option->variables->map(function ($item, $key) use ($server) {
$display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
return [
'variable' => $item->env_variable,
'value' => (! is_null($display)) ? $display : $item->default_value,
];
});
}
/**
* Update the startup details for a server.
*
* @param int $id
* @param array $data
* @param bool $admin
* @return void
* @return bool
*
* @throws \GuzzleHttp\Exception\RequestException
* @throws \Pterodactyl\Exceptions\DisplayException
@ -636,78 +717,110 @@ class ServerRepository
public function updateStartup($id, array $data, $admin = false)
{
$server = Models\Server::with('variables', 'option.variables')->findOrFail($id);
$hasServiceChanges = false;
DB::transaction(function () use ($admin, $data, $server) {
if (isset($data['startup']) && $admin) {
$server->startup = $data['startup'];
$server->save();
if ($admin) {
// User is an admin, lots of things to do here.
$validator = Validator::make($data, [
'startup' => 'required|string',
'skip_scripts' => 'sometimes|required|boolean',
'service_id' => 'required|numeric|min:1|exists:services,id',
'option_id' => 'required|numeric|min:1|exists:service_options,id',
'pack_id' => 'sometimes|nullable|numeric|min:0',
]);
if ((int) $data['pack_id'] < 1) {
$data['pack_id'] = null;
}
if ($server->option->variables) {
foreach ($server->option->variables as &$variable) {
$set = isset($data['env_' . $variable->id]);
// If user is not an admin and are trying to edit a non-editable field
// or an invisible field just silently skip the variable.
if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) {
continue;
}
// Perform Field Validation
$validator = Validator::make([
'variable_value' => ($set) ? $data['env_' . $variable->id] : null,
], [
'variable_value' => $variable->rules,
]);
if ($validator->fails()) {
throw new DisplayValidationException(json_encode(
collect([
'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'],
])->merge($validator->errors()->toArray())
));
}
$svar = Models\ServerVariable::firstOrNew([
'server_id' => $server->id,
'variable_id' => $variable->id,
]);
// Set the value; if one was not passed set it to the default value
if ($set) {
$svar->variable_value = $data['env_' . $variable->id];
// Not passed, check if this record exists if so keep value, otherwise set default
} else {
$svar->variable_value = ($svar->exists) ? $svar->variable_value : $variable->default_value;
}
$svar->save();
}
if ($validator->fails()) {
throw new DisplayValidationException(json_encode($validator->errors()));
}
// Reload Variables
$server->load('variables');
$environment = $server->option->variables->map(function ($item, $key) use ($server) {
$display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
if (
$server->service_id != $data['service_id'] ||
$server->option_id != $data['option_id'] ||
$server->pack_id != $data['pack_id']
) {
$hasServiceChanges = true;
}
}
return [
'variable' => $item->env_variable,
'value' => (! is_null($display)) ? $display : $item->default_value,
];
// If user isn't an administrator, this function is being access from the front-end
// Just try to update specific variables.
if (! $admin || ! $hasServiceChanges) {
return DB::transaction(function () use ($admin, $data, $server) {
$environment = $this->processVariables($server, $data, $admin);
$server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
'X-Access-Token' => $server->node->daemonSecret,
])->request('PATCH', '/server', [
'json' => [
'build' => [
'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup])->toArray(),
],
],
]);
return false;
});
}
// Validate those Service Option Variables
// We know the service and option exists because of the validation.
// We need to verify that the option exists for the service, and then check for
// any required variable fields. (fields are labeled env_<env_variable>)
$option = Models\ServiceOption::where('id', $data['option_id'])->where('service_id', $data['service_id'])->first();
if (! $option) {
throw new DisplayException('The requested service option does not exist for the specified service.');
}
// Validate the Pack
if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) {
$data['pack_id'] = null;
} else {
$pack = Models\Pack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first();
if (! $pack) {
throw new DisplayException('The requested service pack does not seem to exist for this combination.');
}
}
return DB::transaction(function () use ($admin, $data, $server) {
$server->installed = 0;
$server->service_id = $data['service_id'];
$server->option_id = $data['option_id'];
$server->pack_id = $data['pack_id'];
$server->skip_scripts = isset($data['skip_scripts']);
$server->save();
$server->variables->each->delete();
$server->load('service', 'pack');
// Send New Environment
$environment = $this->processVariables($server, $data, $admin);
$server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
'X-Access-Token' => $server->node->daemonSecret,
])->request('PATCH', '/server', [
])->request('POST', '/server/reinstall', [
'json' => [
'build' => [
'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup]),
'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup])->toArray(),
],
'service' => [
'type' => $server->option->service->folder,
'option' => $server->option->tag,
'pack' => (! is_null($server->pack_id)) ? $server->pack->uuid : null,
'skip_scripts' => $server->skip_scripts,
],
],
]);
return true;
});
}
/**