2020-05-05 01:34:31 +12:00
|
|
|
<?php
|
2020-07-17 00:04:06 +12:00
|
|
|
ini_set('display_errors', 1);
|
|
|
|
ini_set('display_startup_errors', 1);
|
|
|
|
error_reporting(E_ALL);
|
|
|
|
|
|
|
|
use Appwrite\Database\Database;
|
2020-08-04 17:23:38 +12:00
|
|
|
use Appwrite\Database\Document;
|
2020-07-17 00:04:06 +12:00
|
|
|
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
|
|
|
|
use Appwrite\Database\Adapter\Redis as RedisAdapter;
|
|
|
|
use Appwrite\Database\Validator\Authorization;
|
2020-07-20 02:43:59 +12:00
|
|
|
use Utopia\App;
|
2020-05-10 10:12:00 +12:00
|
|
|
use Utopia\CLI\Console;
|
2020-05-05 01:34:31 +12:00
|
|
|
use Utopia\Config\Config;
|
2020-05-09 18:26:18 +12:00
|
|
|
|
2020-05-10 04:39:50 +12:00
|
|
|
require_once __DIR__.'/../init.php';
|
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
\cli_set_process_title('Functions V1 Worker');
|
2020-05-10 04:39:50 +12:00
|
|
|
|
|
|
|
Console::success(APP_NAME.' functions worker v1 has started');
|
|
|
|
|
2020-05-10 10:12:00 +12:00
|
|
|
$environments = Config::getParam('environments');
|
2020-05-09 18:26:18 +12:00
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
$warmupStart = \microtime(true);
|
2020-07-16 00:49:10 +12:00
|
|
|
|
2020-10-02 21:25:57 +13:00
|
|
|
Co\run(function() use ($environments) { // Warmup: make sure images are ready to run fast 🚀
|
2020-07-21 19:57:28 +12:00
|
|
|
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
|
|
|
|
2020-10-02 21:25:57 +13:00
|
|
|
foreach($environments as $environment) {
|
2020-07-16 01:34:28 +12:00
|
|
|
go(function() use ($environment) {
|
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
|
|
|
|
|
|
|
Console::info('Warming up '.$environment['name'].' environment');
|
|
|
|
|
|
|
|
Console::execute('docker pull '.$environment['image'], null, $stdout, $stderr);
|
|
|
|
|
|
|
|
if(!empty($stdout)) {
|
|
|
|
Console::log($stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!empty($stderr)) {
|
|
|
|
Console::error($stderr);
|
|
|
|
}
|
|
|
|
});
|
2020-05-10 10:12:00 +12:00
|
|
|
}
|
2020-07-16 01:34:28 +12:00
|
|
|
});
|
2020-05-05 01:34:31 +12:00
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
$warmupEnd = \microtime(true);
|
2020-07-16 00:49:10 +12:00
|
|
|
$warmupTime = $warmupEnd - $warmupStart;
|
|
|
|
|
|
|
|
Console::success('Finished warmup in '.$warmupTime.' seconds');
|
|
|
|
|
2020-07-18 08:41:01 +12:00
|
|
|
/*
|
|
|
|
* 1. Get Original Task
|
|
|
|
* 2. Check for updates
|
|
|
|
* If has updates skip task and don't reschedule
|
|
|
|
* If status not equal to play skip task
|
|
|
|
* 3. Check next run date, update task and add new job at the given date
|
|
|
|
* 4. Execute task (set optional timeout)
|
|
|
|
* 5. Update task response to log
|
|
|
|
* On success reset error count
|
|
|
|
* On failure add error count
|
|
|
|
* If error count bigger than allowed change status to pause
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 1. Get event args - DONE
|
|
|
|
* 2. Unpackage code in the isolated container - DONE
|
|
|
|
* 3. Execute in container with timeout
|
|
|
|
* + messure execution time - DONE
|
|
|
|
* + pass env vars - DONE
|
|
|
|
* + pass one-time api key
|
2020-07-20 18:43:25 +12:00
|
|
|
* 4. Update execution status - DONE
|
|
|
|
* 5. Update execution stdout & stderr - DONE
|
2020-07-23 17:52:03 +12:00
|
|
|
* 6. Trigger audit log - DONE
|
|
|
|
* 7. Trigger usage log - DONE
|
2020-07-18 08:41:01 +12:00
|
|
|
*/
|
|
|
|
|
|
|
|
//TODO aviod scheduled execution if delay is bigger than X offest
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Limit CPU Usage - DONE
|
|
|
|
* Limit Memory Usage - DONE
|
|
|
|
* Limit Network Usage
|
|
|
|
* Limit Storage Usage (//--storage-opt size=120m \)
|
|
|
|
* Make sure no access to redis, mariadb, influxdb or other system services
|
|
|
|
* Make sure no access to NFS server / storage volumes
|
|
|
|
* Access Appwrite REST from internal network for improved performance
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get Usage Stats
|
|
|
|
* -> Network (docker stats --no-stream --format="{{.NetIO}}" appwrite)
|
2020-07-23 17:52:03 +12:00
|
|
|
* -> CPU Time - DONE
|
|
|
|
* -> Invoctions (+1) - DONE
|
2020-07-18 08:41:01 +12:00
|
|
|
*/
|
|
|
|
|
2020-05-05 01:34:31 +12:00
|
|
|
class FunctionsV1
|
|
|
|
{
|
|
|
|
public $args = [];
|
|
|
|
|
2020-07-22 08:10:31 +12:00
|
|
|
public $allowed = [];
|
|
|
|
|
2020-10-01 10:59:48 +13:00
|
|
|
public function setUp(): void
|
2020-05-05 01:34:31 +12:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public function perform()
|
|
|
|
{
|
2020-08-04 17:23:38 +12:00
|
|
|
global $register;
|
2020-05-10 22:30:07 +12:00
|
|
|
|
2020-07-17 00:04:06 +12:00
|
|
|
$projectId = $this->args['projectId'];
|
2020-07-16 08:29:55 +12:00
|
|
|
$functionId = $this->args['functionId'];
|
2020-07-17 09:51:26 +12:00
|
|
|
$executionId = $this->args['executionId'];
|
2020-08-04 17:23:38 +12:00
|
|
|
$trigger = $this->args['trigger'];
|
2020-08-05 08:09:01 +12:00
|
|
|
$event = $this->args['event'];
|
2020-08-06 01:12:23 +12:00
|
|
|
$payload = (!empty($this->args['payload'])) ? json_encode($this->args['payload']) : '';
|
2020-07-16 08:29:55 +12:00
|
|
|
|
2020-08-04 17:23:38 +12:00
|
|
|
$database = new Database();
|
|
|
|
$database->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
|
|
|
|
$database->setNamespace('app_'.$projectId);
|
|
|
|
$database->setMocks(Config::getParam('collections', []));
|
2020-07-17 00:04:06 +12:00
|
|
|
|
2020-08-04 17:23:38 +12:00
|
|
|
switch ($trigger) {
|
|
|
|
case 'event':
|
2020-08-05 08:09:01 +12:00
|
|
|
|
|
|
|
$limit = 30;
|
|
|
|
$sum = 30;
|
|
|
|
$offset = 0;
|
|
|
|
$functions = []; /** @var Document[] $functions */
|
|
|
|
|
|
|
|
while ($sum >= $limit) {
|
|
|
|
|
|
|
|
Authorization::disable();
|
|
|
|
|
|
|
|
$functions = $database->getCollection([
|
|
|
|
'limit' => $limit,
|
|
|
|
'offset' => $offset,
|
|
|
|
'orderField' => 'name',
|
|
|
|
'orderType' => 'ASC',
|
|
|
|
'orderCast' => 'string',
|
|
|
|
'filters' => [
|
|
|
|
'$collection='.Database::SYSTEM_COLLECTION_FUNCTIONS,
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
|
|
|
Authorization::reset();
|
|
|
|
|
|
|
|
$sum = \count($functions);
|
|
|
|
$offset = $offset + $limit;
|
|
|
|
|
|
|
|
Console::log('Fetched '.$sum.' functions...');
|
2020-08-05 17:18:45 +12:00
|
|
|
|
|
|
|
foreach($functions as $function) {
|
|
|
|
$events = $function->getAttribute('events', []);
|
|
|
|
$tag = $function->getAttribute('tag', []);
|
|
|
|
|
|
|
|
Console::success('Itterating function: '.$function->getAttribute('name'));
|
|
|
|
|
|
|
|
if(!\in_array($event, $events) || empty($tag)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Console::success('Triggered function: '.$event);
|
|
|
|
|
2020-08-06 01:12:23 +12:00
|
|
|
$this->execute('event', $projectId, '', $database, $function, $event, $payload);
|
2020-08-05 17:18:45 +12:00
|
|
|
}
|
2020-08-05 08:09:01 +12:00
|
|
|
}
|
2020-08-04 17:23:38 +12:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'schedule':
|
2020-10-02 21:25:57 +13:00
|
|
|
/*
|
|
|
|
* 1. Get Original Task
|
|
|
|
* 2. Check for updates
|
|
|
|
* If has updates skip task and don't reschedule
|
|
|
|
* If status not equal to play skip task
|
|
|
|
* 3. Check next run date, update task and add new job at the given date
|
|
|
|
* 4. Execute task (set optional timeout)
|
|
|
|
* 5. Update task response to log
|
|
|
|
* On success reset error count
|
|
|
|
* On failure add error count
|
|
|
|
* If error count bigger than allowed change status to pause
|
|
|
|
*/
|
|
|
|
|
2020-08-04 17:23:38 +12:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'http':
|
|
|
|
Authorization::disable();
|
|
|
|
$function = $database->getDocument($functionId);
|
|
|
|
Authorization::reset();
|
|
|
|
|
|
|
|
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
|
|
|
|
throw new Exception('Function not found');
|
|
|
|
}
|
2020-07-17 00:04:06 +12:00
|
|
|
|
2020-08-04 17:23:38 +12:00
|
|
|
$this->execute($trigger, $projectId, $executionId, $database, $function);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
# code...
|
|
|
|
break;
|
2020-07-17 00:04:06 +12:00
|
|
|
}
|
2020-08-04 17:23:38 +12:00
|
|
|
}
|
|
|
|
|
2020-08-06 01:12:23 +12:00
|
|
|
public function execute(
|
|
|
|
string $trigger,
|
|
|
|
string $projectId,
|
|
|
|
string $executionId,
|
|
|
|
Database $database,
|
|
|
|
Document $function,
|
|
|
|
string $event = '',
|
|
|
|
string $payload = ''
|
|
|
|
)
|
2020-08-04 17:23:38 +12:00
|
|
|
{
|
2020-08-05 08:09:01 +12:00
|
|
|
global $register;
|
|
|
|
|
2020-08-04 17:23:38 +12:00
|
|
|
$environments = Config::getParam('environments');
|
2020-07-17 00:04:06 +12:00
|
|
|
|
|
|
|
Authorization::disable();
|
2020-08-04 17:23:38 +12:00
|
|
|
$tag = $database->getDocument($function->getAttribute('tag', ''));
|
2020-07-17 00:04:06 +12:00
|
|
|
Authorization::reset();
|
|
|
|
|
|
|
|
if($tag->getAttribute('functionId') !== $function->getId()) {
|
|
|
|
throw new Exception('Tag not found', 404);
|
|
|
|
}
|
|
|
|
|
2020-07-17 09:51:26 +12:00
|
|
|
Authorization::disable();
|
2020-07-18 08:41:01 +12:00
|
|
|
|
2020-08-05 17:18:45 +12:00
|
|
|
$execution = (!empty($executionId)) ? $database->getDocument($executionId) : $database->createDocument([
|
|
|
|
'$collection' => Database::SYSTEM_COLLECTION_EXECUTIONS,
|
|
|
|
'$permissions' => [
|
|
|
|
'read' => [],
|
|
|
|
'write' => [],
|
|
|
|
],
|
|
|
|
'dateCreated' => time(),
|
|
|
|
'functionId' => $function->getId(),
|
|
|
|
'trigger' => $trigger, // http / schedule / event
|
|
|
|
'status' => 'processing', // waiting / processing / completed / failed
|
|
|
|
'exitCode' => 0,
|
|
|
|
'stdout' => '',
|
|
|
|
'stderr' => '',
|
|
|
|
'time' => 0,
|
|
|
|
]);
|
|
|
|
|
|
|
|
if(false === $execution) {
|
|
|
|
throw new Exception('Failed to create execution');
|
2020-07-17 09:51:26 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
Authorization::reset();
|
|
|
|
|
2020-07-17 00:04:06 +12:00
|
|
|
$environment = (isset($environments[$function->getAttribute('env', '')]))
|
|
|
|
? $environments[$function->getAttribute('env', '')]
|
|
|
|
: null;
|
2020-07-17 03:20:51 +12:00
|
|
|
|
2020-07-17 00:04:06 +12:00
|
|
|
if(\is_null($environment)) {
|
|
|
|
throw new Exception('Environment "'.$function->getAttribute('env', '').' is not supported');
|
2020-07-16 08:29:55 +12:00
|
|
|
}
|
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
$vars = \array_merge($function->getAttribute('vars', []), [
|
2020-08-04 17:23:38 +12:00
|
|
|
'APPWRITE_FUNCTION_ID' => $function->getId(),
|
2020-07-20 15:59:33 +12:00
|
|
|
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
|
2020-08-04 17:23:38 +12:00
|
|
|
'APPWRITE_FUNCTION_TAG' => $tag->getId(),
|
|
|
|
'APPWRITE_FUNCTION_TRIGGER' => $trigger,
|
2020-07-17 03:20:51 +12:00
|
|
|
'APPWRITE_FUNCTION_ENV_NAME' => $environment['name'],
|
|
|
|
'APPWRITE_FUNCTION_ENV_VERSION' => $environment['version'],
|
|
|
|
]);
|
|
|
|
|
2020-08-06 01:12:23 +12:00
|
|
|
if('event' === $trigger) {
|
|
|
|
$vars = \array_merge($vars, [
|
|
|
|
'APPWRITE_FUNCTION_EVENT' => $event,
|
|
|
|
'APPWRITE_FUNCTION_EVENT_PAYLOAD' => $payload,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
\array_walk($vars, function (&$value, $key) {
|
2020-07-22 08:10:31 +12:00
|
|
|
$key = $this->filterEnvKey($key);
|
2020-07-22 06:43:53 +12:00
|
|
|
$value = \escapeshellarg((empty($value)) ? 'null' : $value);
|
2020-07-17 03:20:51 +12:00
|
|
|
$value = "\t\t\t--env {$key}={$value} \\";
|
|
|
|
});
|
|
|
|
|
2020-08-08 22:41:17 +12:00
|
|
|
$tagPath = $tag->getAttribute('path', '');
|
2020-07-17 00:04:06 +12:00
|
|
|
$tagPathTarget = '/tmp/project-'.$projectId.'/'.$tag->getId().'/code.tar.gz';
|
|
|
|
$tagPathTargetDir = \pathinfo($tagPathTarget, PATHINFO_DIRNAME);
|
2020-07-18 08:41:01 +12:00
|
|
|
$container = 'appwrite-function-'.$tag->getId();
|
2020-07-20 18:43:25 +12:00
|
|
|
$command = \escapeshellcmd($tag->getAttribute('command', ''));
|
2020-07-17 00:04:06 +12:00
|
|
|
|
|
|
|
if(!\is_readable($tagPath)) {
|
2020-08-08 22:41:17 +12:00
|
|
|
throw new Exception('Code is not readable: '.$tag->getAttribute('path', ''));
|
2020-07-17 00:04:06 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!\file_exists($tagPathTargetDir)) {
|
|
|
|
if (!\mkdir($tagPathTargetDir, 0755, true)) {
|
|
|
|
throw new Exception('Can\'t create directory '.$tagPathTargetDir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!\file_exists($tagPathTarget)) {
|
|
|
|
if(!\copy($tagPath, $tagPathTarget)) {
|
|
|
|
throw new Exception('Can\'t create temporary code file '.$tagPathTarget);
|
|
|
|
}
|
|
|
|
}
|
2020-07-17 22:30:08 +12:00
|
|
|
|
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
|
|
|
|
|
|
|
$executionStart = \microtime(true);
|
2020-07-16 08:29:55 +12:00
|
|
|
|
2020-07-18 08:41:01 +12:00
|
|
|
$exitCode = Console::execute('docker ps --all --format "name={{.Names}}&status={{.Status}}&labels={{.Labels}}" --filter label=appwrite-type=function'
|
2020-07-17 22:30:08 +12:00
|
|
|
, null, $stdout, $stderr, 30);
|
2020-05-10 22:30:07 +12:00
|
|
|
|
2020-07-17 22:30:08 +12:00
|
|
|
$executionEnd = \microtime(true);
|
|
|
|
|
2020-07-18 08:41:01 +12:00
|
|
|
$list = [];
|
2020-07-20 18:43:25 +12:00
|
|
|
$stdout = \explode("\n", $stdout);
|
2020-07-18 08:41:01 +12:00
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
\array_map(function($value) use (&$list) {
|
2020-07-18 08:41:01 +12:00
|
|
|
$container = [];
|
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
\parse_str($value, $container);
|
2020-07-18 08:41:01 +12:00
|
|
|
|
|
|
|
if(isset($container['name'])) {
|
|
|
|
$container = [
|
|
|
|
'name' => $container['name'],
|
2020-07-20 18:43:25 +12:00
|
|
|
'online' => (\substr($container['status'], 0, 2) === 'Up'),
|
2020-07-18 08:41:01 +12:00
|
|
|
'status' => $container['status'],
|
|
|
|
'labels' => $container['labels'],
|
|
|
|
];
|
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
\array_map(function($value) use (&$container) {
|
|
|
|
$value = \explode('=', $value);
|
2020-07-18 08:41:01 +12:00
|
|
|
|
|
|
|
if(isset($value[0]) && isset($value[1])) {
|
|
|
|
$container[$value[0]] = $value[1];
|
|
|
|
}
|
2020-07-20 18:43:25 +12:00
|
|
|
}, \explode(',', $container['labels']));
|
2020-07-18 08:41:01 +12:00
|
|
|
|
|
|
|
$list[$container['name']] = $container;
|
|
|
|
}
|
|
|
|
}, $stdout);
|
|
|
|
|
|
|
|
Console::info("Functions listed in " . ($executionEnd - $executionStart) . " seconds with exit code {$exitCode}");
|
|
|
|
|
|
|
|
if(isset($list[$container]) && !$list[$container]['online']) {
|
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
|
|
|
|
|
|
|
if(Console::execute("docker rm {$container}", null, $stdout, $stderr, 30) !== 0) {
|
|
|
|
throw new Exception('Failed to remove offline container: '.$stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($list[$container]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!isset($list[$container])) { // Create contianer if not ready
|
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
|
|
|
|
|
|
|
$executionStart = \microtime(true);
|
|
|
|
|
|
|
|
$exitCode = Console::execute("docker run \
|
|
|
|
-d \
|
2020-07-20 02:43:59 +12:00
|
|
|
--entrypoint=\"\" \
|
2020-07-18 20:13:15 +12:00
|
|
|
--cpus=4 \
|
|
|
|
--memory=128m \
|
|
|
|
--memory-swap=128m \
|
2020-07-18 08:41:01 +12:00
|
|
|
--rm \
|
|
|
|
--name={$container} \
|
|
|
|
--label appwrite-type=function \
|
2020-07-20 18:43:25 +12:00
|
|
|
--label appwrite-created=".\time()." \
|
2020-07-18 08:41:01 +12:00
|
|
|
--volume {$tagPathTargetDir}:/tmp:rw \
|
|
|
|
--workdir /usr/local/src \
|
2020-07-20 18:43:25 +12:00
|
|
|
".\implode("\n", $vars)."
|
2020-07-18 08:41:01 +12:00
|
|
|
{$environment['image']} \
|
|
|
|
sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null'"
|
|
|
|
, null, $stdout, $stderr, 30);
|
2020-07-20 02:43:59 +12:00
|
|
|
|
2020-07-18 08:41:01 +12:00
|
|
|
$executionEnd = \microtime(true);
|
|
|
|
|
|
|
|
if($exitCode !== 0) {
|
|
|
|
throw new Exception('Failed to create function environment: '.$stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Console::info("Function created in " . ($executionEnd - $executionStart) . " seconds with exit code {$exitCode}");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Console::info('Container is ready to run');
|
|
|
|
}
|
|
|
|
|
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
|
|
|
|
|
|
|
$executionStart = \microtime(true);
|
|
|
|
|
2020-07-23 02:50:17 +12:00
|
|
|
$exitCode = Console::execute("docker exec \
|
|
|
|
".\implode("\n", $vars)."
|
|
|
|
{$container} \
|
|
|
|
{$command}"
|
2020-07-20 18:43:25 +12:00
|
|
|
, null, $stdout, $stderr, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)));
|
2020-07-18 08:41:01 +12:00
|
|
|
|
|
|
|
$executionEnd = \microtime(true);
|
2020-07-21 22:33:23 +12:00
|
|
|
$executionTime = ($executionEnd - $executionStart);
|
2020-07-24 02:59:44 +12:00
|
|
|
$functionStatus = ($exitCode === 0) ? 'completed' : 'failed';
|
2020-07-18 08:41:01 +12:00
|
|
|
|
2020-07-17 22:30:08 +12:00
|
|
|
Console::info("Function executed in " . ($executionEnd - $executionStart) . " seconds with exit code {$exitCode}");
|
2020-07-17 00:04:06 +12:00
|
|
|
|
2020-07-17 09:51:26 +12:00
|
|
|
Authorization::disable();
|
|
|
|
|
2020-08-04 17:23:38 +12:00
|
|
|
$execution = $database->updateDocument(array_merge($execution->getArrayCopy(), [
|
2020-07-19 01:49:20 +12:00
|
|
|
'tagId' => $tag->getId(),
|
2020-07-24 02:59:44 +12:00
|
|
|
'status' => $functionStatus,
|
2020-07-17 09:51:26 +12:00
|
|
|
'exitCode' => $exitCode,
|
2020-08-05 08:09:01 +12:00
|
|
|
'stdout' => \mb_substr($stdout, -4000), // log last 4000 chars output
|
|
|
|
'stderr' => \mb_substr($stderr, -4000), // log last 4000 chars output
|
2020-07-21 22:33:23 +12:00
|
|
|
'time' => $executionTime,
|
2020-07-17 09:51:26 +12:00
|
|
|
]));
|
|
|
|
|
|
|
|
Authorization::reset();
|
|
|
|
|
|
|
|
if (false === $function) {
|
|
|
|
throw new Exception('Failed saving execution to DB', 500);
|
|
|
|
}
|
|
|
|
|
2020-07-21 22:33:23 +12:00
|
|
|
$usage = $register->get('queue-usage');
|
|
|
|
|
|
|
|
$usage
|
|
|
|
->setParam('projectId', $projectId)
|
|
|
|
->setParam('functionId', $function->getId())
|
|
|
|
->setParam('functionExecution', 1)
|
2020-07-24 02:59:44 +12:00
|
|
|
->setParam('functionStatus', $functionStatus)
|
2020-07-21 23:53:48 +12:00
|
|
|
->setParam('functionExecutionTime', $executionTime * 1000) // ms
|
2020-07-21 22:33:23 +12:00
|
|
|
->setParam('networkRequestSize', 0)
|
|
|
|
->setParam('networkResponseSize', 0)
|
|
|
|
;
|
|
|
|
|
|
|
|
$usage->trigger();
|
|
|
|
|
2020-07-18 08:41:01 +12:00
|
|
|
Console::success(count($list).' running containers counted');
|
2020-07-17 03:20:51 +12:00
|
|
|
|
2020-07-20 02:43:59 +12:00
|
|
|
$max = (int) App::getEnv('_APP_FUNCTIONS_CONTAINERS');
|
2020-07-18 08:41:01 +12:00
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
if(\count($list) > $max) {
|
2020-07-18 08:41:01 +12:00
|
|
|
Console::info('Starting containers cleanup');
|
|
|
|
|
|
|
|
$sorted = [];
|
|
|
|
|
|
|
|
foreach($list as $env) {
|
|
|
|
$sorted[] = [
|
|
|
|
'name' => $env['name'],
|
|
|
|
'created' => (int)$env['appwrite-created']
|
|
|
|
];
|
|
|
|
}
|
2020-05-11 07:56:36 +12:00
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
\usort($sorted, function ($item1, $item2) {
|
2020-07-18 08:41:01 +12:00
|
|
|
return $item1['created'] <=> $item2['created'];
|
|
|
|
});
|
|
|
|
|
2020-07-20 18:43:25 +12:00
|
|
|
while(\count($sorted) > $max) {
|
|
|
|
$first = \array_shift($sorted);
|
2020-07-18 08:41:01 +12:00
|
|
|
$stdout = '';
|
|
|
|
$stderr = '';
|
|
|
|
|
|
|
|
if(Console::execute("docker stop {$first['name']}", null, $stdout, $stderr, 30) !== 0) {
|
|
|
|
Console::error('Failed to remove container: '.$stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Console::info('Removed container: '.$first['name']);
|
|
|
|
}
|
|
|
|
}
|
2020-05-05 01:34:31 +12:00
|
|
|
}
|
|
|
|
|
2020-07-22 08:10:31 +12:00
|
|
|
public function filterEnvKey(string $string): string
|
|
|
|
{
|
|
|
|
if(empty($this->allowed)) {
|
2020-08-05 08:09:01 +12:00
|
|
|
$this->allowed = array_fill_keys(\str_split('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'), true);
|
2020-07-22 08:10:31 +12:00
|
|
|
}
|
|
|
|
|
2020-08-05 08:09:01 +12:00
|
|
|
$string = \str_split($string);
|
2020-07-22 08:10:31 +12:00
|
|
|
$output = '';
|
|
|
|
|
|
|
|
foreach ($string as $char) {
|
2020-08-05 08:09:01 +12:00
|
|
|
if(\array_key_exists($char, $this->allowed)) {
|
2020-07-22 08:10:31 +12:00
|
|
|
$output .= $char;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
2020-10-01 10:59:48 +13:00
|
|
|
public function tearDown(): void
|
2020-05-05 01:34:31 +12:00
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|