1
0
Fork 0
mirror of synced 2024-06-27 02:31:04 +12:00

Add authentication between appwrite and the executor

+ Add authentication between appwrite and the executor
+ Add built status and build stdout/stderr to tag for later use
+ Changes to executor to implement new build stages
This commit is contained in:
Bradley Schofield 2021-09-22 11:03:04 +01:00
parent fc17e12547
commit 715a8ac729
8 changed files with 260 additions and 203 deletions

7
.env
View file

@ -35,9 +35,10 @@ _APP_SMTP_PASSWORD=
_APP_STORAGE_LIMIT=10000000
_APP_FUNCTIONS_TIMEOUT=900
_APP_FUNCTIONS_CONTAINERS=10
_APP_FUNCTIONS_CPUS=1
_APP_FUNCTIONS_MEMORY=256
_APP_FUNCTIONS_MEMORY_SWAP=256
_APP_FUNCTIONS_CPUS=12
_APP_FUNCTIONS_MEMORY=2000
_APP_FUNCTIONS_MEMORY_SWAP=2000
_APP_EXECUTOR_SECRET=a-randomly-generated-key
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400

View file

@ -1640,7 +1640,25 @@ $collections = [
'default' => '',
'required' => false,
'array' => false,
]
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Build Stdout',
'key' => 'buildStdout',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Build Stderr',
'key' => 'buildStderr',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
],
],
Database::SYSTEM_COLLECTION_EXECUTIONS => [

View file

@ -369,6 +369,7 @@ App::patch('/v1/functions/:functionId/tag')
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Appwrite-Project: '.$project->getId(),
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$executorResponse = \curl_exec($ch);
@ -426,6 +427,7 @@ App::delete('/v1/functions/:functionId')
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Appwrite-Project: '.$project->getId(),
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$executorResponse = \curl_exec($ch);
@ -540,7 +542,9 @@ App::post('/v1/functions/:functionId/tags')
'path' => $path,
'size' => $size,
'status' => 'pending',
'builtPath' => ''
'builtPath' => '',
'buildStdout' => '',
'buildStderr' => ''
]);
if (false === $tag) {
@ -691,6 +695,7 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Appwrite-Project: '.$project->getId(),
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$executorResponse = \curl_exec($ch);
@ -867,6 +872,7 @@ App::post('/v1/functions/:functionId/executions')
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$responseExecute = \curl_exec($ch);

View file

@ -150,8 +150,8 @@ App::post('/v1/cleanup/function')
'offset' => 0,
'orderType' => 'ASC',
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_TAGS,
'functionId='.$functionId,
'$collection=' . Database::SYSTEM_COLLECTION_TAGS,
'functionId=' . $functionId,
],
]);
Authorization::reset();
@ -164,7 +164,7 @@ App::post('/v1/cleanup/function')
// Delete the containers of all tags
foreach ($results as $tag) {
try {
$orchestration->remove('appwrite-function-'.$tag['$id'], true);
$orchestration->remove('appwrite-function-' . $tag['$id'], true);
Console::info('Removed container for tag ' . $tag['$id']);
} catch (Exception $e) {
// Do nothing, we don't care that much if it fails
@ -201,7 +201,7 @@ App::post('/v1/cleanup/tag')
}
try {
$orchestration->remove('appwrite-function-'.$tag['$id'], true);
$orchestration->remove('appwrite-function-' . $tag['$id'], true);
Console::info('Removed container for tag ' . $tag['$id']);
} catch (Exception $e) {
// Do nothing, we don't care that much if it fails
@ -247,7 +247,7 @@ App::post('/v1/tag')
Authorization::reset();
// Build Code
go(function() use ($projectDB, $projectID, $function, $tagId, $functionId) {
go(function () use ($projectDB, $projectID, $function, $tagId, $functionId) {
// Build Code
$tag = runBuildStage($tagId, $function, $projectID, $projectDB);
@ -290,6 +290,10 @@ function runBuildStage(string $tagID, Document $function, string $projectID, Dat
global $runtimes;
global $orchestration;
$buildStdout = '';
$buildStderr = '';
try {
// Update Tag Status
Authorization::disable();
$tag = $database->getDocument($tagID);
@ -372,7 +376,7 @@ function runBuildStage(string $tagID, Document $function, string $projectID, Dat
hostname: $container,
mountFolder: $tagPathTargetDir,
volumes: [
'/tmp/project-' . $projectID . '/' . $tag->getId() . '/builtCode'. ':/usr/builtCode:rw'
'/tmp/project-' . $projectID . '/' . $tag->getId() . '/builtCode' . ':/usr/builtCode:rw'
]
);
@ -396,12 +400,9 @@ function runBuildStage(string $tagID, Document $function, string $projectID, Dat
}
// Build Code / Install Dependencies
$buildStdout = '';
$buildStderr = '';
$buildSuccess = $orchestration->execute(
name: $container,
command: $runtime['buildCommand'],
command: ['sh', '-c', 'cd /usr/local/src && ./build.sh'],
stdout: $buildStdout,
stderr: $buildStderr,
timeout: 600 //TODO: Make this configurable
@ -442,7 +443,7 @@ function runBuildStage(string $tagID, Document $function, string $projectID, Dat
// Upload new code
$device = Storage::getDevice('functions');
$path = $device->getPath(\uniqid().'.'.\pathinfo('code.tar.gz', PATHINFO_EXTENSION));
$path = $device->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
if (!\file_exists(\dirname($path))) { // Checks if directory path to file exists
if (!@\mkdir(\dirname($path), 0755, true)) {
@ -458,13 +459,25 @@ function runBuildStage(string $tagID, Document $function, string $projectID, Dat
Authorization::disable();
$tag = $database->updateDocument(array_merge($tag->getArrayCopy(), [
'builtPath' => $path,
'status' => 'ready'
'status' => 'ready',
'buildStdout' => $buildStdout,
'buildStderr' => $buildStderr
]));
Authorization::enable();
$buildEnd = \microtime(true);
Console::info('Tag Built in ' . ($buildEnd - $buildStart) . ' seconds');
} catch (Exception $e) {
Console::error('Tag build failed: ' . $e->getMessage());
Authorization::disable();
$tag = $database->updateDocument(array_merge($tag->getArrayCopy(), [
'status' => 'failed',
'buildStdout' => $buildStdout,
'buildStderr' => $buildStderr,
]));
Authorization::enable();
}
return $tag;
}
@ -588,26 +601,6 @@ function createRuntimeServer(string $functionId, string $projectId, Document $ta
// Add to network
$orchestration->networkConnect($container, 'appwrite_runtimes');
// Handled by Dockerfiles
// $untarStdout = '';
// $untarStderr = '';
// $untarSuccess = $orchestration->execute(
// name: $container,
// command: [
// 'sh',
// '-c',
// 'mkdir /usr/code -p && cp /tmp/code.tar.gz /usr/code/code.tar.gz && cd /usr/code && tar -zxf /usr/code/code.tar.gz --strip 1 && rm /usr/code/code.tar.gz'
// ],
// stdout: $untarStdout,
// stderr: $untarStderr,
// timeout: 60
// );
// if (!$untarSuccess) {
// throw new Exception('Failed to extract tar: ' . $untarStderr);
// }
$executionEnd = \microtime(true);
$activeFunctions[$container] = new Container(
@ -724,6 +717,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
}
} catch (Exception $e) {
Console::error('Something went wrong building the runtime server. ' . $e->getMessage());
Authorization::disable();
$execution = $database->updateDocument(array_merge($execution->getArrayCopy(), [
'tagId' => $tag->getId(),
'status' => 'failed',
@ -731,6 +725,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
'stderr' => \utf8_encode(\mb_substr($e->getMessage(), -4000)), // log last 4000 chars output
'time' => 0
]));
Authorization::enable();
}
$stdout = '';
@ -917,7 +912,20 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$projectId = $request->getHeader('x-appwrite-project', '');
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId));
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId));
// 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');
}
App::setResource('projectDB', function ($db, $cache) use ($projectId) {
$projectDB = new Database();

View file

@ -99,6 +99,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
<button>Activate</button>
</form>
<p data-ls-bind="{{tag.status}}"></p>
<b data-ls-bind="{{tag.$id}}"></b> &nbsp;
<span class="text-fade" data-ls-bind="{{tag.entrypoint}}"></span>
<div class="text-size-small margin-top-small clear">

View file

@ -298,6 +298,7 @@ class FunctionsV1 extends Worker
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
\curl_exec($ch);

View file

@ -129,6 +129,7 @@ services:
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
appwrite-realtime:
entrypoint: realtime
@ -360,6 +361,7 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_EXECUTOR_SECRET
- _APP_USAGE_STATS
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
@ -410,6 +412,7 @@ services:
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_EXECUTOR_SECRET
- _APP_USAGE_STATS
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD

View file

@ -40,6 +40,24 @@ class Tag extends Model
'default' => '',
'example' => 'python-3.8',
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'The tags current built status',
'default' => '',
'example' => 'ready',
])
->addRule('buildStdout', [
'type' => self::TYPE_STRING,
'description' => 'The stdout of the build.',
'default' => '',
'example' => '',
])
->addRule('buildStderr', [
'type' => self::TYPE_STRING,
'description' => 'The stderr of the build.',
'default' => '',
'example' => '',
])
;
}