2021-08-24 21:32:27 +12:00
|
|
|
<?php
|
|
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
use Swoole\ConnectionPool;
|
2021-09-13 22:50:45 +12:00
|
|
|
use Swoole\Coroutine as Co;
|
2021-08-24 21:32:27 +12:00
|
|
|
use Swoole\Http\Request as SwooleRequest;
|
|
|
|
use Swoole\Http\Response as SwooleResponse;
|
2022-01-24 06:21:23 +13:00
|
|
|
use Swoole\Http\Server;
|
|
|
|
use Swoole\Process;
|
|
|
|
use Utopia\App;
|
|
|
|
use Utopia\CLI\Console;
|
|
|
|
use Utopia\Config\Config;
|
2022-01-19 22:33:48 +13:00
|
|
|
use Utopia\Logger\Log;
|
2022-01-24 06:21:23 +13:00
|
|
|
use Utopia\Orchestration\Adapter\DockerCLI;
|
|
|
|
use Utopia\Orchestration\Orchestration;
|
|
|
|
use Utopia\Storage\Device\Local;
|
|
|
|
use Utopia\Storage\Storage;
|
|
|
|
use Utopia\Swoole\Request;
|
2022-02-14 01:03:25 +13:00
|
|
|
use Utopia\Swoole\Response;
|
2022-01-24 06:21:23 +13:00
|
|
|
use Utopia\Validator\ArrayList;
|
2022-02-03 07:58:03 +13:00
|
|
|
use Utopia\Validator\Assoc;
|
2022-02-03 13:05:03 +13:00
|
|
|
use Utopia\Validator\Range as ValidatorRange;
|
2022-01-24 06:21:23 +13:00
|
|
|
use Utopia\Validator\Text;
|
2021-08-27 21:21:28 +12:00
|
|
|
|
2022-01-21 00:34:50 +13:00
|
|
|
Swoole\Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
function logError(Throwable $error, string $action, Utopia\Route $route = null)
|
|
|
|
{
|
|
|
|
global $register;
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
$logger = $register->get('logger');
|
2022-01-21 00:34:50 +13:00
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
if ($logger) {
|
2022-01-19 05:55:53 +13:00
|
|
|
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$log = new Log();
|
|
|
|
$log->setNamespace("executor");
|
|
|
|
$log->setServer(\gethostname());
|
|
|
|
$log->setVersion($version);
|
|
|
|
$log->setType(Log::TYPE_ERROR);
|
|
|
|
$log->setMessage($error->getMessage());
|
2021-08-27 21:21:28 +12:00
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
if ($route) {
|
2022-01-19 05:55:53 +13:00
|
|
|
$log->addTag('method', $route->getMethod());
|
|
|
|
$log->addTag('url', $route->getPath());
|
|
|
|
}
|
2021-09-28 01:39:20 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$log->addTag('code', $error->getCode());
|
|
|
|
$log->addTag('verboseType', get_class($error));
|
2021-09-28 01:39:20 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$log->addExtra('file', $error->getFile());
|
|
|
|
$log->addExtra('line', $error->getLine());
|
|
|
|
$log->addExtra('trace', $error->getTraceAsString());
|
|
|
|
|
|
|
|
$log->setAction($action);
|
|
|
|
|
|
|
|
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
|
|
|
|
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
|
|
|
|
|
|
|
$responseCode = $logger->addLog($log);
|
2022-01-21 23:42:12 +13:00
|
|
|
Console::info('Executor log pushed with status code: ' . $responseCode);
|
2021-09-28 01:39:20 +13:00
|
|
|
}
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
Console::error('[Error] Type: ' . get_class($error));
|
|
|
|
Console::error('[Error] Message: ' . $error->getMessage());
|
|
|
|
Console::error('[Error] File: ' . $error->getFile());
|
|
|
|
Console::error('[Error] Line: ' . $error->getLine());
|
|
|
|
};
|
2022-01-17 23:18:31 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
$orchestrationPool = new ConnectionPool(function () {
|
2022-01-19 05:55:53 +13:00
|
|
|
$dockerUser = App::getEnv('DOCKERHUB_PULL_USERNAME', null);
|
|
|
|
$dockerPass = App::getEnv('DOCKERHUB_PULL_PASSWORD', null);
|
|
|
|
$orchestration = new Orchestration(new DockerCLI($dockerUser, $dockerPass));
|
2022-01-17 23:09:29 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
return $orchestration;
|
2022-01-22 07:56:37 +13:00
|
|
|
}, 6);
|
2022-01-24 06:21:23 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
try {
|
2022-01-19 05:55:53 +13:00
|
|
|
$runtimes = Config::getParam('runtimes');
|
2022-01-17 23:09:29 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
// Warmup: make sure images are ready to run fast 🚀
|
2022-01-22 03:33:58 +13:00
|
|
|
Co\run(function () use ($runtimes, $orchestrationPool) {
|
2022-01-19 05:55:53 +13:00
|
|
|
foreach ($runtimes as $runtime) {
|
2022-01-22 03:33:58 +13:00
|
|
|
go(function () use ($runtime, $orchestrationPool) {
|
2022-01-26 05:35:53 +13:00
|
|
|
try {
|
|
|
|
$orchestration = $orchestrationPool->get();
|
2022-01-22 03:33:58 +13:00
|
|
|
|
2022-01-26 05:35:53 +13:00
|
|
|
Console::info('Warming up ' . $runtime['name'] . ' ' . $runtime['version'] . ' environment...');
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-26 05:35:53 +13:00
|
|
|
$response = $orchestration->pull($runtime['image']);
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-26 05:35:53 +13:00
|
|
|
if ($response) {
|
|
|
|
Console::success("Successfully Warmed up {$runtime['name']} {$runtime['version']}!");
|
|
|
|
} else {
|
|
|
|
Console::warning("Failed to Warmup {$runtime['name']} {$runtime['version']}!");
|
|
|
|
}
|
|
|
|
} catch (\Throwable $th) {
|
|
|
|
} finally {
|
|
|
|
$orchestrationPool->put($orchestration);
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
|
|
|
});
|
2021-08-24 21:32:27 +12:00
|
|
|
}
|
2022-01-19 05:55:53 +13:00
|
|
|
});
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$activeFunctions = new Swoole\Table(1024);
|
|
|
|
$activeFunctions->column('id', Swoole\Table::TYPE_STRING, 512);
|
|
|
|
$activeFunctions->column('name', Swoole\Table::TYPE_STRING, 512);
|
|
|
|
$activeFunctions->column('status', Swoole\Table::TYPE_STRING, 512);
|
|
|
|
$activeFunctions->column('key', Swoole\Table::TYPE_STRING, 4096);
|
|
|
|
$activeFunctions->create();
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
Co\run(function () use ($orchestrationPool, $activeFunctions) {
|
2022-01-26 05:35:53 +13:00
|
|
|
try {
|
|
|
|
$orchestration = $orchestrationPool->get();
|
|
|
|
$executionStart = \microtime(true);
|
2022-02-13 11:34:16 +13:00
|
|
|
$residueList = $orchestration->list(['label' => 'openruntimes-type=function']);
|
2022-01-26 05:35:53 +13:00
|
|
|
} catch (\Throwable $th) {
|
|
|
|
} finally {
|
|
|
|
$orchestrationPool->put($orchestration);
|
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
foreach ($residueList as $value) {
|
2022-01-21 23:42:12 +13:00
|
|
|
go(fn () => $activeFunctions->set($value->getName(), [
|
2022-01-19 05:55:53 +13:00
|
|
|
'id' => $value->getId(),
|
|
|
|
'name' => $value->getName(),
|
|
|
|
'status' => $value->getStatus(),
|
|
|
|
'private-key' => ''
|
2022-01-21 23:42:12 +13:00
|
|
|
]));
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$executionEnd = \microtime(true);
|
|
|
|
Console::info(count($activeFunctions) . ' functions listed in ' . ($executionEnd - $executionStart) . ' seconds');
|
|
|
|
});
|
|
|
|
} catch (\Throwable $error) {
|
|
|
|
call_user_func($logError, $error, "startupError");
|
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-02-14 13:00:16 +13:00
|
|
|
function createRuntimeServer(string $runtimeId, string $buildOutputPath, array $vars, string $baseImage, string $runtime): bool
|
2022-01-19 05:55:53 +13:00
|
|
|
{
|
2022-01-22 03:33:58 +13:00
|
|
|
global $orchestrationPool;
|
2022-01-19 05:55:53 +13:00
|
|
|
global $activeFunctions;
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-02-03 07:58:03 +13:00
|
|
|
$orchestration = $orchestrationPool->get();
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-02-03 07:58:03 +13:00
|
|
|
try {
|
2022-02-13 11:34:16 +13:00
|
|
|
$container = 'runtime-' . $runtimeId;
|
2022-01-22 03:33:58 +13:00
|
|
|
if ($activeFunctions->exists($container) && !(\substr($activeFunctions->get($container)['status'], 0, 2) === 'Up')) { // Remove container if not online
|
|
|
|
// If container is online then stop and remove it
|
2022-01-19 05:55:53 +13:00
|
|
|
try {
|
2022-01-22 03:33:58 +13:00
|
|
|
$orchestration->remove($container, true);
|
|
|
|
} catch (Exception $e) {
|
2022-02-03 07:58:03 +13:00
|
|
|
throw new Exception('Failed to remove container: ' . $e->getMessage());
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
2022-01-22 03:33:58 +13:00
|
|
|
$activeFunctions->del($container);
|
|
|
|
}
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-14 10:26:36 +13:00
|
|
|
/**
|
2022-02-14 13:00:16 +13:00
|
|
|
* Copy code files from source to a temporary location on the executor
|
2022-02-14 10:26:36 +13:00
|
|
|
*/
|
2022-02-14 13:00:16 +13:00
|
|
|
$tmpBuild = "/tmp/$runtimeId/builds/code.tar.gz";
|
2022-02-13 14:29:28 +13:00
|
|
|
$device = new Local();
|
2022-02-14 13:00:16 +13:00
|
|
|
$buffer = $device->read($buildOutputPath);
|
|
|
|
if(!$device->write($tmpBuild, $buffer)) {
|
|
|
|
throw new Exception('Failed to write built code to temporary location.', 500);
|
2022-01-22 03:33:58 +13:00
|
|
|
};
|
2022-02-14 10:26:36 +13:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Launch Runtime
|
|
|
|
*/
|
2022-02-03 07:58:03 +13:00
|
|
|
|
|
|
|
$secret = \bin2hex(\random_bytes(16));
|
|
|
|
$vars = \array_merge($vars, [
|
|
|
|
'INTERNAL_RUNTIME_KEY' => $secret
|
|
|
|
]);
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-02-03 07:58:03 +13:00
|
|
|
if (!$activeFunctions->exists($container)) {
|
2022-01-22 03:33:58 +13:00
|
|
|
$executionStart = \microtime(true);
|
|
|
|
$executionTime = \time();
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-02-03 07:58:03 +13:00
|
|
|
$vars = array_map(fn ($v) => strval($v), $vars);
|
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
$orchestration
|
|
|
|
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', '1'))
|
|
|
|
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', '256'))
|
|
|
|
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', '256'));
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
$id = $orchestration->run(
|
2022-02-03 07:58:03 +13:00
|
|
|
image: $baseImage,
|
2022-01-22 03:33:58 +13:00
|
|
|
name: $container,
|
|
|
|
vars: $vars,
|
|
|
|
labels: [
|
2022-02-13 11:34:16 +13:00
|
|
|
'openruntimes-id' => $runtimeId,
|
|
|
|
'openruntimes-type' => 'function',
|
|
|
|
'openruntimes-created' => strval($executionTime),
|
|
|
|
'openruntimes-runtime' => $runtime
|
2022-01-22 03:33:58 +13:00
|
|
|
],
|
|
|
|
hostname: $container,
|
2022-02-14 13:00:16 +13:00
|
|
|
mountFolder: \dirname($tmpBuild),
|
2022-01-22 03:33:58 +13:00
|
|
|
);
|
|
|
|
|
|
|
|
if (empty($id)) {
|
|
|
|
throw new Exception('Failed to create container');
|
|
|
|
}
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
// Add to network
|
2022-01-26 23:23:30 +13:00
|
|
|
$orchestration->networkConnect($container, App::getEnv('_APP_EXECUTOR_RUNTIME_NETWORK', 'appwrite_runtimes'));
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
$executionEnd = \microtime(true);
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
$activeFunctions->set($container, [
|
|
|
|
'id' => $id,
|
|
|
|
'name' => $container,
|
|
|
|
'status' => 'Up ' . \round($executionEnd - $executionStart, 2) . 's',
|
|
|
|
'key' => $secret,
|
|
|
|
]);
|
|
|
|
}
|
2022-02-03 07:58:03 +13:00
|
|
|
Console::success('Runtime Server created in ' . ($executionEnd - $executionStart) . ' seconds');
|
2022-02-14 10:26:36 +13:00
|
|
|
return true;
|
2022-01-22 03:33:58 +13:00
|
|
|
} catch (\Throwable $th) {
|
2022-02-03 07:58:03 +13:00
|
|
|
Console::error('Runtime Server Creation Failed: '. $th->getMessage());
|
2022-02-14 10:26:36 +13:00
|
|
|
return false;
|
2022-02-03 07:58:03 +13:00
|
|
|
} finally {
|
|
|
|
$orchestrationPool->put($orchestration);
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-02-14 10:36:07 +13:00
|
|
|
function execute(string $runtimeId, string $path, array $vars, string $data, string $baseImage, string $runtime, string $entrypoint, int $timeout): array
|
2022-01-19 05:55:53 +13:00
|
|
|
{
|
2022-02-03 13:05:03 +13:00
|
|
|
|
2022-02-13 11:34:16 +13:00
|
|
|
Console::info('Executing Runtime: ' . $runtimeId);
|
2022-01-19 05:55:53 +13:00
|
|
|
|
|
|
|
global $activeFunctions;
|
2022-02-13 11:34:16 +13:00
|
|
|
$container = 'runtime-' . $runtimeId;
|
2022-01-19 05:55:53 +13:00
|
|
|
|
2022-02-03 13:05:03 +13:00
|
|
|
/** Create a new runtime server if there's none running */
|
|
|
|
if (!$activeFunctions->exists($container)) {
|
2022-02-13 11:34:16 +13:00
|
|
|
Console::info("Runtime server for $runtimeId not running. Creating new one...");
|
2022-02-14 10:36:07 +13:00
|
|
|
createRuntimeServer($runtimeId, $path, $vars, $baseImage, $runtime);
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-13 11:34:16 +13:00
|
|
|
$key = $activeFunctions->get('runtime-' . $runtimeId, 'key');
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
2021-12-09 04:08:53 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$executionStart = \microtime(true);
|
2021-12-09 04:08:53 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$statusCode = 0;
|
2021-12-09 04:08:53 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$errNo = -1;
|
|
|
|
$attempts = 0;
|
|
|
|
$max = 5;
|
2021-12-09 04:08:53 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$executorResponse = '';
|
2021-12-09 04:08:53 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
// cURL request to runtime
|
|
|
|
do {
|
|
|
|
$attempts++;
|
|
|
|
$ch = \curl_init();
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$body = \json_encode([
|
|
|
|
'path' => '/usr/code',
|
2022-02-06 08:49:57 +13:00
|
|
|
'file' => $entrypoint,
|
2022-01-19 05:55:53 +13:00
|
|
|
'env' => $vars,
|
|
|
|
'payload' => $data,
|
2022-02-03 13:05:03 +13:00
|
|
|
'timeout' => $timeout ?? (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)
|
2022-01-19 05:55:53 +13:00
|
|
|
]);
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
\curl_setopt($ch, CURLOPT_URL, "http://" . $container . ":3000/");
|
|
|
|
\curl_setopt($ch, CURLOPT_POST, true);
|
|
|
|
\curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
2021-09-28 01:39:20 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
2022-02-03 13:05:03 +13:00
|
|
|
\curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ?? (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900));
|
2022-01-19 05:55:53 +13:00
|
|
|
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
2021-09-28 01:39:20 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
|
|
'Content-Type: application/json',
|
|
|
|
'Content-Length: ' . \strlen($body),
|
|
|
|
'x-internal-challenge: ' . $key,
|
|
|
|
'host: null'
|
|
|
|
]);
|
2021-10-14 22:37:00 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$executorResponse = \curl_exec($ch);
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$error = \curl_error($ch);
|
|
|
|
|
|
|
|
$errNo = \curl_errno($ch);
|
|
|
|
|
|
|
|
\curl_close($ch);
|
|
|
|
if ($errNo != CURLE_COULDNT_CONNECT && $errNo != 111) {
|
|
|
|
break;
|
2021-09-22 22:03:04 +12:00
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
sleep(1);
|
|
|
|
} while ($attempts < $max);
|
2021-12-07 03:12:41 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
if ($attempts >= 5) {
|
|
|
|
$stderr = 'Failed to connect to executor runtime after 5 attempts.';
|
|
|
|
$statusCode = 124;
|
|
|
|
}
|
2021-12-07 03:12:41 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
// If timeout error
|
2022-01-21 23:42:12 +13:00
|
|
|
if (in_array($errNo, [CURLE_OPERATION_TIMEDOUT, 110])) {
|
2022-01-19 05:55:53 +13:00
|
|
|
$statusCode = 124;
|
|
|
|
}
|
2021-11-30 22:39:50 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
// 110 is the Swoole error code for timeout, see: https://www.swoole.co.uk/docs/swoole-error-code
|
2022-01-21 03:41:27 +13:00
|
|
|
if ($errNo !== 0 && $errNo !== CURLE_COULDNT_CONNECT && $errNo !== CURLE_OPERATION_TIMEDOUT && $errNo !== 110) {
|
2022-01-19 05:55:53 +13:00
|
|
|
throw new Exception('An internal curl error has occurred within the executor! Error Msg: ' . $error, 500);
|
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$executionData = [];
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
if (!empty($executorResponse)) {
|
|
|
|
$executionData = json_decode($executorResponse, true);
|
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
if (isset($executionData['code'])) {
|
|
|
|
$statusCode = $executionData['code'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($statusCode === 500) {
|
|
|
|
if (isset($executionData['message'])) {
|
|
|
|
$stderr = $executionData['message'];
|
|
|
|
} else {
|
|
|
|
$stderr = 'Internal Runtime error';
|
2021-12-07 03:12:41 +13:00
|
|
|
}
|
2022-01-19 05:55:53 +13:00
|
|
|
} else if ($statusCode === 124) {
|
|
|
|
$stderr = 'Execution timed out.';
|
|
|
|
} else if ($statusCode === 0) {
|
|
|
|
$stderr = 'Execution failed.';
|
|
|
|
} else if ($statusCode >= 200 && $statusCode < 300) {
|
|
|
|
$stdout = $executorResponse;
|
|
|
|
} else {
|
|
|
|
$stderr = 'Execution failed.';
|
|
|
|
}
|
2021-12-07 03:12:41 +13:00
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
$executionEnd = \microtime(true);
|
|
|
|
$executionTime = ($executionEnd - $executionStart);
|
|
|
|
$functionStatus = ($statusCode >= 200 && $statusCode < 300) ? 'completed' : 'failed';
|
2021-12-07 03:12:41 +13:00
|
|
|
|
2022-02-03 13:05:03 +13:00
|
|
|
Console::success('Function executed in ' . $executionTime . ' seconds, status: ' . $functionStatus);
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-02-03 13:05:03 +13:00
|
|
|
$execution = [
|
|
|
|
'status' => $functionStatus,
|
|
|
|
'statusCode' => $statusCode,
|
|
|
|
'stdout' => \utf8_encode(\mb_substr($stdout, -8000)),
|
|
|
|
'stderr' => \utf8_encode(\mb_substr($stderr, -8000)),
|
|
|
|
'time' => $executionTime,
|
|
|
|
];
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-02-03 13:05:03 +13:00
|
|
|
return $execution;
|
2022-01-19 05:55:53 +13:00
|
|
|
};
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2022-01-22 03:33:58 +13:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
// POST /v1/runtimes
|
|
|
|
App::post('/v1/runtimes')
|
|
|
|
->desc("Create a new runtime server")
|
|
|
|
->param('runtimeId', '', new Text(128), 'Unique runtime ID.')
|
|
|
|
->param('source', '', new Text(0), 'Path to source files.')
|
|
|
|
->param('destination', '', new Text(0), 'Destination folder to store build files into.')
|
|
|
|
->param('vars', '', new Assoc(), 'Environment Variables required for the build')
|
|
|
|
->param('runtime', '', new Text(128), 'Runtime for the cloud function')
|
|
|
|
->param('baseImage', '', new Text(128), 'Base image name of the runtime')
|
|
|
|
->inject('response')
|
|
|
|
->action(function (string $runtimeId, string $source, string $destination, array $vars, string $runtime, string $baseImage, Response $response) {
|
|
|
|
|
|
|
|
// TODO: Check if runtime already exists..
|
2022-01-26 23:09:11 +13:00
|
|
|
|
2022-02-14 10:26:36 +13:00
|
|
|
// TODO: Move orchestration pool and swoole table to a utopia resource
|
2022-02-14 01:31:44 +13:00
|
|
|
global $orchestrationPool;
|
2022-02-14 10:26:36 +13:00
|
|
|
global $activeFunctions;
|
2022-02-14 01:31:44 +13:00
|
|
|
$orchestration = $orchestrationPool->get();
|
|
|
|
|
|
|
|
$build = [];
|
|
|
|
$id = '';
|
|
|
|
$buildStdout = '';
|
|
|
|
$buildStderr = '';
|
|
|
|
$buildStart = \time();
|
|
|
|
$buildEnd = 0;
|
|
|
|
|
|
|
|
try {
|
|
|
|
Console::info('Building runtime with ID : ' . $runtimeId);
|
|
|
|
/**
|
|
|
|
* Temporary file paths in the executor
|
|
|
|
*/
|
|
|
|
$tmpSource = "/tmp/$runtimeId/code.tar.gz";
|
|
|
|
$tmpBuildDir = "/tmp/$runtimeId/builds";
|
|
|
|
$tmpBuild = "/tmp/$runtimeId/builds/code.tar.gz";
|
|
|
|
|
|
|
|
/**
|
2022-02-14 10:26:36 +13:00
|
|
|
* Copy code files from source to a temporary location on the executor
|
2022-02-14 01:31:44 +13:00
|
|
|
*/
|
|
|
|
$device = new Local($destination);
|
2022-02-14 10:26:36 +13:00
|
|
|
$buffer = $device->read($source);
|
|
|
|
if(!$device->write($tmpSource, $buffer)) {
|
|
|
|
throw new Exception('Failed to write source code to temporary location.', 500);
|
|
|
|
};
|
2021-12-07 03:12:41 +13:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
/**
|
|
|
|
* Create a temporary folder to store builds
|
|
|
|
*/
|
|
|
|
if (!\file_exists($tmpBuildDir)) {
|
|
|
|
if (!@\mkdir($tmpBuildDir, 0755, true)) {
|
|
|
|
throw new Exception("Can't create directory : $tmpBuildDir", 500);
|
|
|
|
}
|
|
|
|
}
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
/**
|
|
|
|
* Create container
|
|
|
|
*/
|
|
|
|
$container = 'build-' . $runtimeId;
|
|
|
|
$vars = array_map(fn ($v) => strval($v), $vars);
|
|
|
|
$orchestration
|
|
|
|
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 0))
|
|
|
|
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', 256))
|
|
|
|
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256));
|
|
|
|
$id = $orchestration->run(
|
|
|
|
image: $baseImage,
|
|
|
|
name: $container,
|
|
|
|
vars: $vars,
|
|
|
|
workdir: '/usr/code',
|
|
|
|
labels: [
|
|
|
|
'openruntimes-id' => $runtimeId,
|
|
|
|
'openruntimes-type' => 'build',
|
|
|
|
'openruntimes-created' => strval($buildStart),
|
|
|
|
'openruntimes-runtime' => $runtime,
|
|
|
|
],
|
|
|
|
command: [
|
|
|
|
'tail',
|
|
|
|
'-f',
|
|
|
|
'/dev/null'
|
|
|
|
],
|
|
|
|
hostname: $container,
|
|
|
|
mountFolder: \dirname($tmpSource),
|
|
|
|
volumes: [
|
|
|
|
"$tmpBuildDir:/usr/builds:rw"
|
|
|
|
]
|
|
|
|
);
|
2021-08-27 22:55:22 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
if (empty($id)) {
|
|
|
|
throw new Exception('Failed to create build container', 500);
|
|
|
|
}
|
2021-09-21 03:52:12 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
/**
|
|
|
|
* Extract user code into build container
|
|
|
|
*/
|
|
|
|
$untarStdout = '';
|
|
|
|
$untarStderr = '';
|
|
|
|
$untarSuccess = $orchestration->execute(
|
|
|
|
name: $container,
|
|
|
|
command: [
|
|
|
|
'sh',
|
|
|
|
'-c',
|
|
|
|
'mkdir -p /usr/code && cp /tmp/code.tar.gz /usr/workspace/code.tar.gz && cd /usr/workspace/ && tar -zxf /usr/workspace/code.tar.gz -C /usr/code && rm /usr/workspace/code.tar.gz'
|
|
|
|
],
|
|
|
|
stdout: $untarStdout,
|
|
|
|
stderr: $untarStderr,
|
|
|
|
timeout: 60
|
|
|
|
);
|
2021-09-21 03:52:12 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
if (!$untarSuccess) {
|
|
|
|
throw new Exception('Failed to extract tar: ' . $untarStderr);
|
|
|
|
}
|
2021-10-01 03:36:09 +13:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
/**
|
|
|
|
* Build code and install dependenices
|
|
|
|
*/
|
|
|
|
$buildSuccess = $orchestration->execute(
|
|
|
|
name: $container,
|
|
|
|
command: ['sh', '-c', 'cd /usr/local/src && ./build.sh'],
|
|
|
|
stdout: $buildStdout,
|
|
|
|
stderr: $buildStderr,
|
|
|
|
timeout: App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900)
|
|
|
|
);
|
2021-08-27 22:55:22 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
if (!$buildSuccess) {
|
|
|
|
throw new Exception('Failed to build dependencies: ' . $buildStderr);
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
2022-02-14 01:31:44 +13:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Repackage code and save
|
|
|
|
*/
|
|
|
|
$compressStdout = '';
|
|
|
|
$compressStderr = '';
|
|
|
|
$compressSuccess = $orchestration->execute(
|
|
|
|
name: $container,
|
|
|
|
command: [
|
|
|
|
'tar', '-C', '/usr/code', '-czvf', '/usr/builds/code.tar.gz', './'
|
|
|
|
],
|
|
|
|
stdout: $compressStdout,
|
|
|
|
stderr: $compressStderr,
|
|
|
|
timeout: 60
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!$compressSuccess) {
|
|
|
|
throw new Exception('Failed to compress built code: ' . $compressStderr);
|
2022-01-19 05:55:53 +13:00
|
|
|
}
|
2021-08-27 22:55:22 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
// Check if the build was successful by checking if file exists
|
|
|
|
if (!\file_exists($tmpBuild)) {
|
|
|
|
throw new Exception('Something went wrong during the build process.');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-02-14 10:26:36 +13:00
|
|
|
* Move built code to expected build directory
|
2022-02-14 01:31:44 +13:00
|
|
|
*/
|
2022-02-14 10:26:36 +13:00
|
|
|
$outputPath = $device->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
2022-02-14 01:31:44 +13:00
|
|
|
|
|
|
|
if (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) {
|
2022-02-14 10:26:36 +13:00
|
|
|
if (!$device->move($tmpBuild, $outputPath)) {
|
2022-02-14 01:31:44 +13:00
|
|
|
throw new Exception('Failed to move built code to storage', 500);
|
|
|
|
}
|
|
|
|
} else {
|
2022-02-14 10:26:36 +13:00
|
|
|
if (!$device->upload($tmpBuild, $outputPath)) {
|
2022-02-14 01:31:44 +13:00
|
|
|
throw new Exception('Failed to upload built code upload to storage', 500);
|
|
|
|
}
|
|
|
|
}
|
2021-09-06 12:37:20 +12:00
|
|
|
|
2022-02-14 01:31:44 +13:00
|
|
|
if ($buildStdout === '') {
|
|
|
|
$buildStdout = 'Build Successful!';
|
|
|
|
}
|
|
|
|
|
|
|
|
$buildEnd = \time();
|
|
|
|
$build = [
|
2022-02-14 10:26:36 +13:00
|
|
|
'outputPath' => $outputPath,
|
2022-02-14 01:31:44 +13:00
|
|
|
'status' => 'ready',
|
|
|
|
'stdout' => \utf8_encode(\mb_substr($buildStdout, -4096)),
|
|
|
|
'stderr' => \utf8_encode(\mb_substr($buildStderr, -4096)),
|
|
|
|
'startTime' => $buildStart,
|
|
|
|
'endTime' => $buildEnd,
|
|
|
|
'duration' => $buildEnd - $buildStart,
|
|
|
|
];
|
|
|
|
|
|
|
|
Console::success('Build Stage Ran in ' . ($buildEnd - $buildStart) . ' seconds');
|
|
|
|
|
|
|
|
} catch (Throwable $th) {
|
|
|
|
$buildEnd = \time();
|
|
|
|
$buildStderr = $th->getMessage();
|
|
|
|
$build = [
|
|
|
|
'status' => 'failed',
|
|
|
|
'stdout' => \utf8_encode(\mb_substr($buildStdout, -4096)),
|
|
|
|
'stderr' => \utf8_encode(\mb_substr($buildStderr, -4096)),
|
|
|
|
'startTime' => $buildStart,
|
|
|
|
'endTime' => $buildEnd,
|
|
|
|
'duration' => $buildEnd - $buildStart,
|
|
|
|
];
|
|
|
|
|
|
|
|
Console::error('Build failed: ' . $th->getMessage());
|
|
|
|
} finally {
|
|
|
|
if (!empty($id)) {
|
|
|
|
$orchestration->remove($id, true);
|
|
|
|
}
|
2022-02-14 13:00:16 +13:00
|
|
|
$orchestrationPool->put($orchestration);
|
2021-12-10 23:43:56 +13:00
|
|
|
}
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-14 10:26:36 +13:00
|
|
|
if ( $build['status'] !== 'ready') {
|
|
|
|
return $response
|
|
|
|
->setStatusCode(201)
|
|
|
|
->json($build);
|
|
|
|
}
|
|
|
|
|
|
|
|
$status = createRuntimeServer($runtimeId, $outputPath, $vars, $baseImage, $runtime);
|
|
|
|
|
|
|
|
if (!$status) {
|
|
|
|
Console::error('Failed to create runtime server');
|
2022-02-11 23:31:53 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
$response
|
|
|
|
->setStatusCode(201)
|
|
|
|
->json($build);
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// GET /v1/runtimes
|
|
|
|
App::get('/v1/runtimes')
|
|
|
|
->desc("Get the list of currently active runtimes")
|
|
|
|
->inject('response')
|
|
|
|
->action(function (Response $response) {
|
|
|
|
// TODO : Get list of active runtimes from swoole table
|
|
|
|
$runtimes = [];
|
|
|
|
|
|
|
|
$response
|
|
|
|
->setStatusCode(200)
|
|
|
|
->json($runtimes);
|
|
|
|
});
|
|
|
|
|
2022-02-13 21:25:48 +13:00
|
|
|
// GET /v1/runtimes/:runtimeId
|
2022-02-11 23:31:53 +13:00
|
|
|
App::get('/v1/runtimes/:runtimeId')
|
|
|
|
->desc("Get a runtime by its ID")
|
2022-02-13 11:34:16 +13:00
|
|
|
->param('runtimeId', '', new Text(128), 'Runtime unique ID.')
|
2022-02-11 23:31:53 +13:00
|
|
|
->inject('response')
|
|
|
|
->action(function (Response $response) {
|
|
|
|
|
|
|
|
// Get a runtime by its ID
|
|
|
|
$runtime = [];
|
|
|
|
|
|
|
|
$response
|
|
|
|
->setStatusCode(200)
|
|
|
|
->json($runtime);
|
|
|
|
});
|
|
|
|
|
2022-02-13 21:25:48 +13:00
|
|
|
// DELETE /v1/runtimes/:runtimeId
|
2022-02-13 11:34:16 +13:00
|
|
|
App::delete('/v1/runtimes/:runtimeId')
|
|
|
|
->desc('Delete a runtime')
|
|
|
|
->param('runtimeId', '', new Text(128), 'Runtime unique ID.', false)
|
2022-02-03 08:38:47 +13:00
|
|
|
->param('buildIds', [], new ArrayList(new Text(0), 100), 'List of build IDs to delete.', false)
|
2022-01-24 06:21:23 +13:00
|
|
|
->inject('response')
|
2022-02-13 11:34:16 +13:00
|
|
|
->action(function (string $runtimeId, array $buildIds, Response $response) use ($orchestrationPool) {
|
2022-02-03 08:38:47 +13:00
|
|
|
|
2022-02-13 11:34:16 +13:00
|
|
|
Console::info('Deleting runtime: ' . $runtimeId);
|
2022-02-03 08:38:47 +13:00
|
|
|
$orchestration = $orchestrationPool->get();
|
|
|
|
|
|
|
|
// Remove the container of the deployment
|
2022-02-13 11:34:16 +13:00
|
|
|
$status = $orchestration->remove('runtime-' . $runtimeId , true);
|
2022-02-03 08:38:47 +13:00
|
|
|
if ($status) {
|
2022-02-13 11:34:16 +13:00
|
|
|
Console::success('Removed runtime container: ' . $runtimeId);
|
2022-02-03 08:38:47 +13:00
|
|
|
} else {
|
2022-02-13 11:34:16 +13:00
|
|
|
Console::error('Failed to remove runtime container: ' . $runtimeId);
|
2022-02-03 08:38:47 +13:00
|
|
|
}
|
|
|
|
|
2022-02-13 11:34:16 +13:00
|
|
|
// Remove all the build containers with that same ID
|
2022-02-13 14:29:28 +13:00
|
|
|
// TODO:: Delete build containers
|
|
|
|
// foreach ($buildIds as $buildId) {
|
|
|
|
// try {
|
|
|
|
// Console::info('Deleting build container : ' . $buildId);
|
|
|
|
// $status = $orchestration->remove('build-' . $buildId, true);
|
|
|
|
// } catch (Throwable $th) {
|
|
|
|
// Console::error($th->getMessage());
|
|
|
|
// }
|
|
|
|
// }
|
2022-02-06 23:00:44 +13:00
|
|
|
|
2022-02-03 08:38:47 +13:00
|
|
|
$orchestrationPool->put($orchestration);
|
2022-01-24 06:21:23 +13:00
|
|
|
|
2022-01-28 14:40:53 +13:00
|
|
|
$response
|
|
|
|
->setStatusCode(Response::STATUS_CODE_OK)
|
|
|
|
->send();
|
2022-01-24 06:21:23 +13:00
|
|
|
});
|
|
|
|
|
2022-02-13 11:34:16 +13:00
|
|
|
|
|
|
|
// POST /v1/execution (get runtime as param, if 404 or 501/503, go and create a runtime first)
|
|
|
|
App::post('/v1/execution')
|
|
|
|
->desc('Create an execution')
|
|
|
|
->param('runtimeId', '', new Text(1024), 'The runtimeID to execute')
|
|
|
|
->param('path', '', new Text(0), 'Path to built files.', false)
|
|
|
|
->param('vars', '', new Assoc(), 'Environment Variables required for the build', false)
|
|
|
|
->param('data', '', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true)
|
|
|
|
->param('runtime', '', new Text(128), 'Runtime for the cloud function', false)
|
|
|
|
->param('entrypoint', '', new Text(256), 'Entrypoint of the code file')
|
|
|
|
->param('timeout', 15, new ValidatorRange(1, 900), 'Function maximum execution time in seconds.', true)
|
|
|
|
->param('baseImage', '', new Text(128), 'Base image name of the runtime', false)
|
|
|
|
->inject('response')
|
|
|
|
->action(
|
|
|
|
function (string $runtimeId, string $path, array $vars, string $data, string $runtime, string $entrypoint, $timeout, string $baseImage, Response $response) {
|
|
|
|
|
|
|
|
// Send both data and vars from the caller
|
2022-02-14 10:36:07 +13:00
|
|
|
$execution = execute($runtimeId, $path, $vars, $data, $baseImage, $runtime, $entrypoint, $timeout);
|
2022-02-13 11:34:16 +13:00
|
|
|
|
|
|
|
$response
|
|
|
|
->setStatusCode(Response::STATUS_CODE_OK)
|
|
|
|
->json($execution);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-01-19 05:55:53 +13:00
|
|
|
App::setMode(App::MODE_TYPE_PRODUCTION); // Define Mode
|
2021-10-14 22:37:00 +13:00
|
|
|
|
2022-01-24 06:21:23 +13:00
|
|
|
$http = new Server("0.0.0.0", 80);
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// function handleShutdown()
|
|
|
|
// {
|
|
|
|
// global $orchestrationPool;
|
|
|
|
// global $register;
|
2022-01-21 23:42:12 +13:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// try {
|
|
|
|
// Console::info('Cleaning up containers before shutdown...');
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// // Remove all containers.
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// /** @var Orchestration $orchestration */
|
|
|
|
// $orchestration = $orchestrationPool->get();
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// $functionsToRemove = $orchestration->list(['label' => 'appwrite-type=function']);
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// foreach ($functionsToRemove as $container) {
|
|
|
|
// go(fn () => $orchestration->remove($container->getId(), true));
|
2021-10-13 00:54:50 +13:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// // Get a database instance
|
|
|
|
// $db = $register->get('dbPool')->get();
|
|
|
|
// $cache = $register->get('redisPool')->get();
|
2021-10-13 00:54:50 +13:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// $cache = new Cache(new RedisCache($cache));
|
|
|
|
// $database = new Database(new MariaDB($db), $cache);
|
|
|
|
// $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
|
|
|
// $database->setNamespace('_project_' . $container->getLabels()["appwrite-project"]);
|
2021-10-13 00:54:50 +13:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// // Get list of all processing executions
|
|
|
|
// $executions = $database->find('executions', [
|
|
|
|
// new Query('deploymentId', Query::TYPE_EQUAL, [$container->getLabels()["appwrite-deployment"]]),
|
|
|
|
// new Query('status', Query::TYPE_EQUAL, ['waiting'])
|
|
|
|
// ]);
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// // Mark all processing executions as failed
|
|
|
|
// foreach ($executions as $execution) {
|
|
|
|
// $execution
|
|
|
|
// ->setAttribute('status', 'failed')
|
|
|
|
// ->setAttribute('statusCode', 1)
|
|
|
|
// ->setAttribute('stderr', 'Appwrite was shutdown during execution');
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// $database->updateDocument('executions', $execution->getId(), $execution);
|
|
|
|
// }
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-04 05:22:56 +13:00
|
|
|
// Console::info('Removed container ' . $container->getName());
|
|
|
|
// }
|
|
|
|
// } catch (\Throwable $error) {
|
|
|
|
// logError($error, 'shutdownError');
|
|
|
|
// } finally {
|
|
|
|
// $orchestrationPool->put($orchestration);
|
|
|
|
// }
|
|
|
|
// };
|
2021-08-24 21:32:27 +12:00
|
|
|
|
2022-02-14 13:00:16 +13:00
|
|
|
App::error(function ($error, $utopia, $request, $response) {
|
|
|
|
/** @var Exception $error */
|
|
|
|
/** @var Utopia\App $utopia */
|
|
|
|
/** @var Utopia\Swoole\Request $request */
|
|
|
|
/** @var Appwrite\Utopia\Response $response */
|
|
|
|
|
|
|
|
if ($error instanceof PDOException) {
|
|
|
|
throw $error;
|
|
|
|
}
|
|
|
|
|
|
|
|
$route = $utopia->match($request);
|
|
|
|
// logError($error, "httpError", $route);
|
|
|
|
|
|
|
|
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
|
|
|
|
|
|
|
$code = $error->getCode();
|
|
|
|
$message = $error->getMessage();
|
|
|
|
|
|
|
|
$output = ((App::isDevelopment())) ? [
|
|
|
|
'message' => $error->getMessage(),
|
|
|
|
'code' => $error->getCode(),
|
|
|
|
'file' => $error->getFile(),
|
|
|
|
'line' => $error->getLine(),
|
|
|
|
'trace' => $error->getTrace(),
|
|
|
|
'version' => $version,
|
|
|
|
] : [
|
|
|
|
'message' => $message,
|
|
|
|
'code' => $code,
|
|
|
|
'version' => $version,
|
|
|
|
];
|
|
|
|
|
|
|
|
$response
|
|
|
|
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
|
|
->addHeader('Expires', '0')
|
|
|
|
->addHeader('Pragma', 'no-cache')
|
|
|
|
->setStatusCode(500);
|
|
|
|
|
|
|
|
$response->json($output);
|
|
|
|
}, ['error', 'utopia', 'request', 'response']);
|
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
$http->on('start', function ($http) {
|
|
|
|
@Process::signal(SIGINT, function () use ($http) {
|
2022-02-04 05:22:56 +13:00
|
|
|
// handleShutdown();
|
2021-09-06 12:37:20 +12:00
|
|
|
$http->shutdown();
|
|
|
|
});
|
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
@Process::signal(SIGQUIT, function () use ($http) {
|
2022-02-04 05:22:56 +13:00
|
|
|
// handleShutdown();
|
2021-09-06 12:37:20 +12:00
|
|
|
$http->shutdown();
|
|
|
|
});
|
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
@Process::signal(SIGKILL, function () use ($http) {
|
2022-02-04 05:22:56 +13:00
|
|
|
// handleShutdown();
|
2021-09-06 12:37:20 +12:00
|
|
|
$http->shutdown();
|
|
|
|
});
|
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
@Process::signal(SIGTERM, function () use ($http) {
|
2022-02-04 05:22:56 +13:00
|
|
|
// handleShutdown();
|
2021-09-06 12:37:20 +12:00
|
|
|
$http->shutdown();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-01-21 23:42:12 +13:00
|
|
|
$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) {
|
2021-08-24 21:32:27 +12:00
|
|
|
$request = new Request($swooleRequest);
|
|
|
|
$response = new Response($swooleResponse);
|
|
|
|
$app = new App('UTC');
|
|
|
|
|
2021-09-22 22:03:04 +12:00
|
|
|
// Check environment variable key
|
|
|
|
$secretKey = $request->getHeader('x-appwrite-executor-key', '');
|
|
|
|
|
|
|
|
if (empty($secretKey)) {
|
|
|
|
$swooleResponse->status(401);
|
|
|
|
return $swooleResponse->end('401: Authentication Error');
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($secretKey !== App::getEnv('_APP_EXECUTOR_SECRET', '')) {
|
|
|
|
$swooleResponse->status(401);
|
|
|
|
return $swooleResponse->end('401: Authentication Error');
|
|
|
|
}
|
2021-09-13 22:50:45 +12:00
|
|
|
|
2021-08-24 21:32:27 +12:00
|
|
|
try {
|
|
|
|
$app->run($request, $response);
|
|
|
|
} catch (Exception $e) {
|
2022-02-14 13:00:16 +13:00
|
|
|
// logError($e, "serverError");
|
2021-08-24 21:32:27 +12:00
|
|
|
$swooleResponse->end('500: Server Error');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-01-26 05:35:53 +13:00
|
|
|
$http->start();
|