WIP: Functions proxy

This commit is contained in:
Matej Baco
2022-07-11 10:22:48 +02:00
parent b037c3953e
commit 3d5d45d676
3 changed files with 164 additions and 52 deletions
+2 -1
View File
@@ -83,4 +83,5 @@ _APP_LOGGING_CONFIG=
DOCKERHUB_PULL_USERNAME=
DOCKERHUB_PULL_PASSWORD=
DOCKERHUB_PULL_EMAIL=
_APP_EXECUTORS=exec_fra_01=http://appwrite-executor/v1,exec_fra_02=http://appwrite-executor2/v1
#_APP_EXECUTORS=exec_fra_01=http://appwrite-executor/v1,exec_fra_02=http://appwrite-executor2/v1
_APP_EXECUTORS=exec_fra_01=http://appwrite-executor/v1
+159 -4
View File
@@ -1,14 +1,23 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Swoole\Http\Server;
use Utopia\Swoole\Request;
use Utopia\Swoole\Response;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Swoole\Timer;
use Utopia\App;
use Utopia\Cache\Adapter\Redis;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
// Redis setup
$redisHost = App::getEnv('_APP_REDIS_HOST', '');
$redisPort = App::getEnv('_APP_REDIS_PORT', '');
@@ -94,14 +103,160 @@ function fetchExecutorsState(RedisPool $redisPool)
}
});
}
};
Timer::tick(30000, fn (int $timerId, array $params) => fetchExecutorsState($params[0]), [$redisPool]);
Swoole\Event::wait();
}
/**
* Create logger instance
*/
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
$logger = null;
if (!empty($providerName) && !empty($providerConfig) && Logger::hasProvider($providerName)) {
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
$adapter = new $classname($providerConfig);
$logger = new Logger($adapter);
}
function logError(Throwable $error, string $action, Utopia\Route $route = null)
{
global $logger;
if ($logger) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace("executor");
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
$log->setMessage($error->getMessage());
if ($route) {
$log->addTag('method', $route->getMethod());
$log->addTag('url', $route->getPath());
}
$log->addTag('code', $error->getCode());
$log->addTag('verboseType', get_class($error));
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
$log->setAction($action);
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Executor log pushed with status code: ' . $responseCode);
}
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());
}
Console::success("Waiting for executors to start...");
\sleep(5); // Wait a little so executors can start
fetchExecutorsState($redisPool);
// fetchExecutorsState($redisPool);
// Timer::tick(30000, fn (int $timerId, array $params) => fetchExecutorsState($params[0]), [$redisPool]);
Console::success("Functions proxy is ready.");
App::setMode(App::MODE_TYPE_PRODUCTION); // Define Mode
$http = new Server("0.0.0.0", 80);
/** Set callbacks */
App::error(function ($utopia, $error, $request, $response) {
$route = $utopia->match($request);
logError($error, "httpError", $route);
switch ($error->getCode()) {
case 400: // Error allowed publicly
case 401: // Error allowed publicly
case 402: // Error allowed publicly
case 403: // Error allowed publicly
case 404: // Error allowed publicly
case 406: // Error allowed publicly
case 409: // Error allowed publicly
case 412: // Error allowed publicly
case 425: // Error allowed publicly
case 429: // Error allowed publicly
case 501: // Error allowed publicly
case 503: // Error allowed publicly
$code = $error->getCode();
break;
default:
$code = 500; // All other errors get the generic 500 server error status code
}
$output = [
'message' => $error->getMessage(),
'code' => $error->getCode(),
'file' => $error->getFile(),
'line' => $error->getLine(),
'trace' => $error->getTrace(),
'version' => App::getEnv('_APP_VERSION', 'UNKNOWN')
];
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Expires', '0')
->addHeader('Pragma', 'no-cache')
->setStatusCode($code);
$response->json($output);
}, ['utopia', 'error', 'request', 'response']);
App::get('/')
->desc("Proxy request into executor")
->inject('response')
->action(function (Response $response) {
$response
->setStatusCode(Response::STATUS_CODE_OK)
->json([ 'works' => true ]);
});
App::init(function ($request, $response) {
$secretKey = $request->getHeader('x-appwrite-functions-proxy-key', '');
if (empty($secretKey)) {
throw new Exception('Missing proxy key', 401);
}
if ($secretKey !== App::getEnv('_APP_FUNCTIONS_PROXY_SECRET', '')) {
throw new Exception('Missing proxy key', 401);
}
}, ['request', 'response']);
$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) {
$request = new Request($swooleRequest);
$response = new Response($swooleResponse);
$app = new App('UTC');
try {
$app->run($request, $response);
} catch (\Throwable $th) {
logError($th, "serverError");
$swooleResponse->setStatusCode(500);
$output = [
'message' => 'Error: ' . $th->getMessage(),
'code' => 500,
'file' => $th->getFile(),
'line' => $th->getLine(),
'trace' => $th->getTrace()
];
$swooleResponse->end(\json_encode($output));
}
});
$http->start();
+3 -47
View File
@@ -420,6 +420,8 @@ services:
container_name: appwrite-functions-proxy
build:
context: .
ports:
- 5544:80
networks:
- appwrite
volumes:
@@ -436,6 +438,7 @@ services:
- _APP_REDIS_PASS
- _APP_EXECUTORS
- _APP_EXECUTOR_SECRET
- _APP_FUNCTIONS_PROXY_SECRET
appwrite-worker-functions:
entrypoint: worker-functions
@@ -518,53 +521,6 @@ services:
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
appwrite-executor2:
container_name: appwrite-executor2
<<: *x-logging
entrypoint: executor
stop_signal: SIGINT
build:
context: .
args:
- DEBUG=false
- TESTING=true
- VERSION=dev
networks:
appwrite:
runtimes:
ports:
- 9529:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
- appwrite-functions:/storage/functions:rw
- appwrite-builds:/storage/builds:rw
- /tmp:/tmp:rw
depends_on:
- redis
- mariadb
- appwrite
- appwrite-functions-proxy
environment:
- _APP_ENV
- _APP_VERSION
- _APP_FUNCTIONS_TIMEOUT
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CONTAINERS
- _APP_FUNCTIONS_RUNTIMES
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_INACTIVE_THRESHOLD
- _APP_EXECUTOR_SECRET
- OPEN_RUNTIMES_NETWORK
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- *x-env-storage
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
appwrite-worker-mails:
entrypoint: worker-mails
<<: *x-logging