Merge branch 'feat-functions-refactor' into feat-add-network-variable
This commit is contained in:
commit
ca5ec8905c
20 changed files with 383 additions and 303 deletions
2
.env
2
.env
|
@ -39,8 +39,8 @@ _APP_FUNCTIONS_CONTAINERS=10
|
|||
_APP_FUNCTIONS_CPUS=4
|
||||
_APP_FUNCTIONS_MEMORY=2000
|
||||
_APP_FUNCTIONS_MEMORY_SWAP=2000
|
||||
_APP_EXECUTOR_SECRET=a-randomly-generated-key
|
||||
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
||||
_APP_EXECUTOR_SECRET=a-random-secret
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE=86400
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
- Introduced new execution model for functions
|
||||
- Improved functions execution times
|
||||
- Improved functions execution times
|
||||
- Create a new builds worker to handle building of deployments
|
||||
- **[ Breaking ]** **Tags** have been renamed to **Deployments**
|
||||
- Rename `tagId` to `deplyomentId` in collections
|
||||
- Rename tags to deployments in the docs
|
||||
|
|
|
@ -270,6 +270,7 @@ RUN chmod +x /usr/local/bin/doctor && \
|
|||
chmod +x /usr/local/bin/worker-database && \
|
||||
chmod +x /usr/local/bin/worker-deletes && \
|
||||
chmod +x /usr/local/bin/worker-functions && \
|
||||
chmod +x /usr/local/bin/worker-builds && \
|
||||
chmod +x /usr/local/bin/worker-mails && \
|
||||
chmod +x /usr/local/bin/worker-webhooks && \
|
||||
chmod +x /usr/local/bin/executor
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -367,7 +367,7 @@ App::patch('/v1/functions/:functionId/deployment')
|
|||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
$deployment = $dbForProject->getDocument('deployments', $deployment);
|
||||
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId'));
|
||||
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
|
@ -433,11 +433,8 @@ App::delete('/v1/functions/:functionId')
|
|||
|
||||
// Request executor to delete deployment containers
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/cleanup/function");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'functionId' => $functionId
|
||||
]));
|
||||
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/$functionId");
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, 900);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
|
@ -582,45 +579,18 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
'deploy' => ($deploy === 'true'),
|
||||
]));
|
||||
|
||||
// Enqueue a message to start the build
|
||||
Resque::enqueue('v1-builds', 'BuildsV1', [
|
||||
'projectId' => $project->getId(),
|
||||
'functionId' => $function->getId(),
|
||||
'deploymentId' => $deploymentId,
|
||||
'type' => BUILD_TYPE_DEPLOYMENT
|
||||
]);
|
||||
|
||||
$usage
|
||||
->setParam('storage', $deployment->getAttribute('size', 0))
|
||||
;
|
||||
|
||||
// Send start build reqeust to executor using /v1/deployment
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/deployment");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'functionId' => $function->getId(),
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'userId' => $user->getId(),
|
||||
]));
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, 900);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
\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);
|
||||
|
||||
$error = \curl_error($ch);
|
||||
|
||||
if (!empty($error)) {
|
||||
throw new Exception('Executor Communication Error: ' . $error, 500);
|
||||
}
|
||||
|
||||
// Check status code
|
||||
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if (200 !== $statusCode) {
|
||||
throw new Exception('Executor error: ' . $executorResponse, $statusCode);
|
||||
}
|
||||
|
||||
\curl_close($ch);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
|
@ -769,11 +739,8 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
|
||||
// Request executor to delete deployment containers
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/cleanup/deployment");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'deploymentId' => $deploymentId
|
||||
]));
|
||||
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/deployments/$deploymentId");
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, 900);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
|
@ -927,13 +894,12 @@ App::post('/v1/functions/:functionId/executions')
|
|||
|
||||
// Directly execute function.
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/execute");
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/{$function->getId()}/executions");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'trigger' => 'http',
|
||||
'projectId' => $project->getId(),
|
||||
'executionId' => $execution->getId(),
|
||||
'functionId' => $function->getId(),
|
||||
'data' => $data,
|
||||
'webhooks' => $project->getAttribute('webhooks', []),
|
||||
'userId' => $user->getId(),
|
||||
|
@ -958,8 +924,8 @@ App::post('/v1/functions/:functionId/executions')
|
|||
\curl_close($ch);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic(new Document(json_decode($responseExecute, true)), Response::MODEL_SYNC_EXECUTION);
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic(new Document(json_decode($responseExecute, true)), Response::MODEL_SYNC_EXECUTION);
|
||||
});
|
||||
|
||||
App::get('/v1/functions/:functionId/executions')
|
||||
|
@ -1115,9 +1081,7 @@ App::get('/v1/builds/:buildId')
|
|||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
|
||||
$build = Authorization::skip(function () use ($dbForProject, $buildId) {
|
||||
return $dbForProject->getDocument('builds', $buildId);
|
||||
});
|
||||
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $buildId));
|
||||
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
|
@ -1125,6 +1089,7 @@ App::get('/v1/builds/:buildId')
|
|||
|
||||
$response->dynamic($build, Response::MODEL_BUILD);
|
||||
});
|
||||
|
||||
App::post('/v1/builds/:buildId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Retry Build')
|
||||
|
@ -1145,9 +1110,7 @@ App::post('/v1/builds/:buildId')
|
|||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
|
||||
$build = Authorization::skip(function () use ($dbForProject, $buildId) {
|
||||
return $dbForProject->getDocument('builds', $buildId);
|
||||
});
|
||||
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $buildId));
|
||||
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
|
@ -1157,34 +1120,12 @@ App::post('/v1/builds/:buildId')
|
|||
throw new Exception('Build not failed', 400);
|
||||
}
|
||||
|
||||
// Retry build
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/build/{$buildId}");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, 900);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'x-appwrite-project: '.$project->getId(),
|
||||
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
|
||||
// Enqueue a message to start the build
|
||||
Resque::enqueue('v1-builds', 'BuildsV1', [
|
||||
'projectId' => $project->getId(),
|
||||
'buildId' => $buildId,
|
||||
'type' => BUILD_TYPE_RETRY
|
||||
]);
|
||||
|
||||
$executorResponse = \curl_exec($ch);
|
||||
|
||||
$error = \curl_error($ch);
|
||||
|
||||
if (!empty($error)) {
|
||||
throw new Exception('Executor Communication Error: ' . $error, 500);
|
||||
}
|
||||
|
||||
// Check status code
|
||||
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if (200 !== $statusCode) {
|
||||
throw new Exception('Executor error: ' . $executorResponse, $statusCode);
|
||||
}
|
||||
|
||||
\curl_close($ch);
|
||||
|
||||
$response->noContent();
|
||||
});
|
306
app/executor.php
306
app/executor.php
|
@ -312,12 +312,13 @@ function createRuntimeServer(string $functionId, string $projectId, string $depl
|
|||
'key' => $secret,
|
||||
]);
|
||||
|
||||
Console::info('Runtime Server created in ' . ($executionEnd - $executionStart) . ' seconds');
|
||||
Console::success('Runtime Server created in ' . ($executionEnd - $executionStart) . ' seconds');
|
||||
} else {
|
||||
Console::info('Runtime server is ready to run');
|
||||
Console::success('Runtime server is ready to run');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$orchestrationPool->put($orchestration);
|
||||
Console::error($th->getMessage());
|
||||
$orchestrationPool->put($orchestration ?? null);
|
||||
throw $th;
|
||||
}
|
||||
$orchestrationPool->put($orchestration);
|
||||
|
@ -435,7 +436,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
|
|||
|
||||
$database->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
runBuildStage($buildId, $projectId);
|
||||
runBuildStage($buildId, $deployment->getId(), $projectId);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$execution
|
||||
|
@ -600,7 +601,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
|
|||
$executionTime = ($executionEnd - $executionStart);
|
||||
$functionStatus = ($statusCode >= 200 && $statusCode < 300) ? 'completed' : 'failed';
|
||||
|
||||
Console::info('Function executed in ' . ($executionEnd - $executionStart) . ' seconds, status: ' . $functionStatus);
|
||||
Console::success('Function executed in ' . ($executionEnd - $executionStart) . ' seconds, status: ' . $functionStatus);
|
||||
|
||||
$execution->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('status', $functionStatus)
|
||||
|
@ -658,7 +659,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
|
|||
];
|
||||
};
|
||||
|
||||
function runBuildStage(string $buildId, string $projectID): Document
|
||||
function runBuildStage(string $buildId, string $deploymentId, string $projectID): Document
|
||||
{
|
||||
global $runtimes;
|
||||
global $orchestrationPool;
|
||||
|
@ -680,6 +681,7 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
|
||||
// Check if build has already been run
|
||||
$build = $database->getDocument('builds', $buildId);
|
||||
$deployment = $database->getDocument('deployments', $deploymentId);
|
||||
|
||||
try {
|
||||
// If we already have a built package ready there is no need to rebuild.
|
||||
|
@ -687,10 +689,11 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
return $build;
|
||||
}
|
||||
|
||||
// Update Tag Status
|
||||
// Update deployment Status
|
||||
$build->setAttribute('status', 'building');
|
||||
|
||||
$database->updateDocument('builds', $build->getId(), $build);
|
||||
$deployment->setAttribute('status', 'building');
|
||||
$database->updateDocument('builds', $buildId, $build);
|
||||
$database->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
// Check if runtime is active
|
||||
$runtime = $runtimes[$build->getAttribute('runtime', '')] ?? null;
|
||||
|
@ -699,16 +702,14 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
throw new Exception('Runtime "' . $build->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
// Grab Tag Files
|
||||
// Grab Deployment Files
|
||||
$deploymentPath = $build->getAttribute('source', '');
|
||||
$sourceType = $build->getAttribute('sourceType', '');
|
||||
|
||||
$device = Storage::getDevice('builds');
|
||||
|
||||
$deploymentPathTarget = '/tmp/project-' . $projectID . '/' . $build->getId() . '/code.tar.gz';
|
||||
$deploymentPathTarget = '/tmp/project-' . $projectID . '/' . $buildId . '/code.tar.gz';
|
||||
$deploymentPathTargetDir = \pathinfo($deploymentPathTarget, PATHINFO_DIRNAME);
|
||||
|
||||
$container = 'build-stage-' . $build->getId();
|
||||
$container = 'build-stage-' . $buildId;
|
||||
|
||||
// Perform various checks
|
||||
if (!\file_exists($deploymentPathTargetDir)) {
|
||||
|
@ -746,13 +747,13 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256));
|
||||
|
||||
$vars = array_map(fn ($v) => strval($v), $vars);
|
||||
$path = '/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode';
|
||||
$path = '/tmp/project-' . $projectID . '/' . $buildId . '/builtCode';
|
||||
|
||||
if (!\file_exists($path)) {
|
||||
if (@\mkdir($path, 0777, true)) {
|
||||
\chmod($path, 0777);
|
||||
} else {
|
||||
throw new Exception('Can\'t create directory /tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode');
|
||||
throw new Exception('Can\'t create directory /tmp/project-' . $projectID . '/' . $buildId . '/builtCode');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -767,7 +768,7 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
'appwrite-created' => strval($time),
|
||||
'appwrite-runtime' => $build->getAttribute('runtime', ''),
|
||||
'appwrite-project' => $projectID,
|
||||
'appwrite-build' => $build->getId(),
|
||||
'appwrite-build' => $buildId,
|
||||
],
|
||||
command: [
|
||||
'tail',
|
||||
|
@ -777,7 +778,7 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
hostname: $container,
|
||||
mountFolder: $deploymentPathTargetDir,
|
||||
volumes: [
|
||||
'/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode' . ':/usr/builtCode:rw'
|
||||
'/tmp/project-' . $projectID . '/' . $buildId . '/builtCode' . ':/usr/builtCode:rw'
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -883,6 +884,9 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
// Update build with built code attribute
|
||||
$build = $database->updateDocument('builds', $buildId, $build);
|
||||
|
||||
$deployment->setAttribute('status', 'ready');
|
||||
$database->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
$buildEnd = \microtime(true);
|
||||
|
||||
Console::info('Build Stage Ran in ' . ($buildEnd - $buildStart) . ' seconds');
|
||||
|
@ -894,6 +898,9 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
|
||||
$build = $database->updateDocument('builds', $buildId, $build);
|
||||
|
||||
$deployment->setAttribute('status', 'failed');
|
||||
$database->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
// also remove the container if it exists
|
||||
if (isset($id)) {
|
||||
$orchestration->remove($id, true);
|
||||
|
@ -913,7 +920,7 @@ function runBuildStage(string $buildId, string $projectID): Document
|
|||
return $build;
|
||||
}
|
||||
|
||||
App::post('/v1/execute') // Define Route
|
||||
App::post('/v1/functions/:functionId/executions')
|
||||
->desc('Execute a function')
|
||||
->param('trigger', '', new Text(1024))
|
||||
->param('projectId', '', new Text(1024))
|
||||
|
@ -929,23 +936,15 @@ App::post('/v1/execute') // Define Route
|
|||
->inject('dbForProject')
|
||||
->action(
|
||||
function (string $trigger, string $projectId, string $executionId, string $functionId, string $event, string $eventData, string $data, array $webhooks, string $userId, string $jwt, Response $response, Database $dbForProject) {
|
||||
try {
|
||||
$data = execute($trigger, $projectId, $executionId, $functionId, $dbForProject, $event, $eventData, $data, $webhooks, $userId, $jwt);
|
||||
$response->json($data);
|
||||
} catch (Exception $e) {
|
||||
logError($e, 'executeEndpoint');
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Expires', '0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->json(['error' => $e->getMessage()]);
|
||||
}
|
||||
$data = execute($trigger, $projectId, $executionId, $functionId, $dbForProject, $event, $eventData, $data, $webhooks, $userId, $jwt);
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_OK)
|
||||
->json($data);
|
||||
}
|
||||
);
|
||||
|
||||
// Cleanup Endpoints used internally by appwrite when a function or deployment gets deleted to also clean up their containers
|
||||
App::post('/v1/cleanup/function')
|
||||
App::delete('/v1/functions/:functionId')
|
||||
->desc('Delete a function')
|
||||
->param('functionId', '', new UID())
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
@ -1002,7 +1001,40 @@ App::post('/v1/cleanup/function')
|
|||
}
|
||||
);
|
||||
|
||||
App::post('/v1/cleanup/deployment')
|
||||
App::post('/v1/functions/:functionId/deployments/:deploymentId/runtime')
|
||||
->desc('Create a new runtime server for a deployment')
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('deploymentId', '', new UID(), 'Deployment unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('projectId')
|
||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, string $projectID) use ($runtimes) {
|
||||
// Get function document
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
}
|
||||
|
||||
// Get deployment document
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
if ($deployment->isEmpty()) {
|
||||
throw new Exception('Deployment not found', 404);
|
||||
}
|
||||
|
||||
$runtime = $runtimes[$function->getAttribute('runtime')] ?? null;
|
||||
if (\is_null($runtime)) {
|
||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" not found.', 404);
|
||||
}
|
||||
|
||||
createRuntimeServer($functionId, $projectID, $deploymentId, $dbForProject);
|
||||
|
||||
$response
|
||||
->setStatusCode(201)
|
||||
->send();
|
||||
});
|
||||
|
||||
App::delete('/v1/deployments/:deploymentId')
|
||||
->desc('Delete a deployment')
|
||||
->param('deploymentId', '', new UID(), 'Deployment unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
@ -1048,181 +1080,81 @@ App::post('/v1/cleanup/deployment')
|
|||
return $response->json(['success' => true]);
|
||||
});
|
||||
|
||||
App::post('/v1/deployment')
|
||||
->param('functionId', '', new UID(), 'Function unique ID.')
|
||||
->param('deploymentId', '', new UID(), 'Deployment unique ID.')
|
||||
->param('userId', '', new UID(), 'User unique ID.', true)
|
||||
App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||
->desc("Create a new build")
|
||||
->param('functionId', '', new UID(), 'Function unique ID.', false)
|
||||
->param('deploymentId', '', new UID(), 'Deployment unique ID.', false)
|
||||
->param('buildId', '', new UID(), 'Build unique ID.', false)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('projectID')
|
||||
->inject('register')
|
||||
->action(function (string $functionId, string $deploymentId, string $userId, Response $response, Database $dbForProject, string $projectID, Registry $register) use ($runtimes) {
|
||||
// Get function document
|
||||
->inject('projectId')
|
||||
->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, string $projectId) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
// Get deployment document
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
// Check if both documents exist
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
if ($deployment->isEmpty()) {
|
||||
throw new Exception('Deployment not found', 404);
|
||||
}
|
||||
|
||||
$runtime = $runtimes[$function->getAttribute('runtime')] ?? null;
|
||||
|
||||
if (\is_null($runtime)) {
|
||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||
$build = $dbForProject->getDocument('builds', $buildId);
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
}
|
||||
|
||||
// Create a new build entry
|
||||
$buildId = $dbForProject->getId();
|
||||
if ($build->getAttribute('status') === 'building') {
|
||||
throw new Exception('Build is already running', 409);
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('buildId')) {
|
||||
$buildId = $deployment->getAttribute('buildId');
|
||||
} else {
|
||||
try {
|
||||
$dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$read' => (!empty($userId)) ? ['user:' . $userId] : [],
|
||||
'$write' => ['role:all'],
|
||||
'dateCreated' => time(),
|
||||
'status' => 'processing',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'outputPath' => '',
|
||||
'source' => $deployment->getAttribute('path'),
|
||||
'sourceType' => Storage::DEVICE_LOCAL,
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'time' => 0,
|
||||
'vars' => [
|
||||
'ENTRYPOINT_NAME' => $deployment->getAttribute('entrypoint'),
|
||||
'APPWRITE_FUNCTION_ID' => $function->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $projectID,
|
||||
]
|
||||
]));
|
||||
// Check if build is already finished
|
||||
if ($build->getAttribute('status') === 'ready') {
|
||||
throw new Exception('Build is already finished', 409);
|
||||
}
|
||||
|
||||
$deployment->setAttribute('buildId', $buildId);
|
||||
go(function() use ($functionId, $deploymentId, $buildId, $projectId, $dbForProject, $function, $deployment) {
|
||||
Console::info('Starting build for deployment ' . $deployment['$id']);
|
||||
runBuildStage($buildId, $deploymentId, $projectId);
|
||||
|
||||
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
} catch (\Throwable $th) {
|
||||
var_dump($deployment->getArrayCopy());
|
||||
throw $th;
|
||||
// Update the schedule
|
||||
$schedule = $function->getAttribute('schedule', '');
|
||||
$cron = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
|
||||
$next = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
|
||||
|
||||
// Grab build
|
||||
$build = $dbForProject->getDocument('builds', $buildId);
|
||||
|
||||
// If the build failed, it won't be possible to deploy
|
||||
if ($build->getAttribute('status') !== 'ready') {
|
||||
throw new Exception('Build failed', 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Build Code
|
||||
go(function () use ($projectID, $deploymentId, $buildId, $functionId, $function, $register) {
|
||||
if ($deployment->getAttribute('deploy') === true) {
|
||||
// Update the function document setting the deployment as the active one
|
||||
$function
|
||||
->setAttribute('deployment', $deployment->getId())
|
||||
->setAttribute('scheduleNext', (int)$next);
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $functionId, $function);
|
||||
}
|
||||
|
||||
// Deploy Runtime Server
|
||||
try {
|
||||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
$cache = new Cache(new RedisCache($redis));
|
||||
|
||||
$dbForProject = new Database(new MariaDB($db), $cache);
|
||||
$dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$dbForProject->setNamespace('_project_' . $projectID);
|
||||
// Build Code
|
||||
runBuildStage($buildId, $projectID);
|
||||
|
||||
// Update the schedule
|
||||
$schedule = $function->getAttribute('schedule', '');
|
||||
$cron = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
|
||||
$next = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
|
||||
|
||||
// Grab deployment
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
// Grab build
|
||||
$build = $dbForProject->getDocument('builds', $buildId);
|
||||
|
||||
// If the build failed, it won't be possible to deploy
|
||||
if ($build->getAttribute('status') !== 'ready') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('automaticDeploy') === true) {
|
||||
// Update the function document setting the deployment as the active one
|
||||
$function
|
||||
->setAttribute('deployment', $deployment->getId())
|
||||
->setAttribute('scheduleNext', (int)$next);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
}
|
||||
|
||||
// Deploy Runtime Server
|
||||
createRuntimeServer($functionId, $projectID, $deploymentId, $dbForProject);
|
||||
Console::info("[ INFO ] Creating runtime server");
|
||||
createRuntimeServer($functionId, $projectId, $deploymentId, $dbForProject);
|
||||
} catch (\Throwable $th) {
|
||||
} finally {
|
||||
$register->get('dbPool')->put($db);
|
||||
$register->get('redisPool')->put($redis);
|
||||
Console::error($th->getMessage());
|
||||
$deployment->setAttribute('status', 'failed');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
throw $th;
|
||||
}
|
||||
});
|
||||
|
||||
if (false === $function) {
|
||||
throw new Exception('Failed saving function to DB', 500);
|
||||
}
|
||||
|
||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||
});
|
||||
|
||||
App::get('/v1/')
|
||||
->inject('response')
|
||||
->action(
|
||||
function (Response $response) {
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Expires', '0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->json(['status' => 'online']);
|
||||
}
|
||||
);
|
||||
|
||||
// Build Endpoints
|
||||
App::post('/v1/build/:buildId') // Start a Build
|
||||
->param('buildId', '', new UID(), 'Build unique ID.', false)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('projectID')
|
||||
->action(function (string $buildId, Response $response, Database $dbForProject, string $projectID) {
|
||||
try {
|
||||
// Get build document
|
||||
$build = $dbForProject->getDocument('builds', $buildId);
|
||||
|
||||
// Check if build exists
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
}
|
||||
|
||||
// Check if build is already running
|
||||
if ($build->getAttribute('status') === 'running') {
|
||||
throw new Exception('Build is already running', 409);
|
||||
}
|
||||
|
||||
// Check if build is already finished
|
||||
if ($build->getAttribute('status') === 'finished') {
|
||||
throw new Exception('Build is already finished', 409);
|
||||
}
|
||||
|
||||
go(function () use ($buildId, $dbForProject, $projectID) {
|
||||
// Build Code
|
||||
runBuildStage($buildId, $projectID, $dbForProject);
|
||||
});
|
||||
|
||||
// return success
|
||||
return $response->json(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
logError($e, "buildEndpoint");
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Expires', '0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->json(['error' => $e->getMessage()]);
|
||||
}
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->send();
|
||||
});
|
||||
|
||||
App::setMode(App::MODE_TYPE_PRODUCTION); // Define Mode
|
||||
|
@ -1387,7 +1319,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
);
|
||||
}, ['error', 'utopia', 'request', 'response']);
|
||||
|
||||
App::setResource('projectID', function () use ($projectId) {
|
||||
App::setResource('projectId', function () use ($projectId) {
|
||||
return $projectId;
|
||||
});
|
||||
|
||||
|
|
|
@ -92,6 +92,9 @@ const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
|
|||
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
|
||||
const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute';
|
||||
const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex';
|
||||
// Build Worker Types
|
||||
const BUILD_TYPE_DEPLOYMENT = 'deployment';
|
||||
const BUILD_TYPE_RETRY = 'retry';
|
||||
// Deletion Types
|
||||
const DELETE_TYPE_DOCUMENT = 'document';
|
||||
const DELETE_TYPE_COLLECTIONS = 'collections';
|
||||
|
|
164
app/workers/builds.php
Normal file
164
app/workers/builds.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Resque\Worker;
|
||||
use Cron\CronExpression;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Config\Config;
|
||||
|
||||
require_once __DIR__.'/../init.php';
|
||||
|
||||
// Disable Auth since we already validate it in the API
|
||||
Authorization::disable();
|
||||
|
||||
Console::title('Builds V1 Worker');
|
||||
Console::success(APP_NAME.' build worker v1 has started');
|
||||
|
||||
// TODO: Executor should return appropriate response codes.
|
||||
class BuildsV1 extends Worker
|
||||
{
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return "builds";
|
||||
}
|
||||
|
||||
public function init(): void {}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$type = $this->args['type'] ?? '';
|
||||
$projectId = $this->args['projectId'] ?? '';
|
||||
|
||||
switch ($type) {
|
||||
case BUILD_TYPE_DEPLOYMENT:
|
||||
$functionId = $this->args['functionId'] ?? '';
|
||||
$deploymentId = $this->args['deploymentId'] ?? '';
|
||||
Console::info("[ INFO ] Creating build for deployment: $deploymentId");
|
||||
$this->buildDeployment($projectId, $functionId, $deploymentId);
|
||||
break;
|
||||
|
||||
case BUILD_TYPE_RETRY:
|
||||
$buildId = $this->args['buildId'] ?? '';
|
||||
$functionId = $this->args['functionId'] ?? '';
|
||||
$deploymentId = $this->args['deploymentId'] ?? '';
|
||||
Console::info("[ INFO ] Retrying build for id: $buildId");
|
||||
$this->createBuild($projectId, $functionId, $deploymentId, $buildId);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \Exception('Invalid build type');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function createBuild(string $projectId, string $functionId, string $deploymentId, string $buildId)
|
||||
{
|
||||
// TODO: What is a reasonable time to wait for a build to complete?
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/$functionId/deployments/$deploymentId/builds/$buildId");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, 900);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'x-appwrite-project: '.$projectId,
|
||||
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
|
||||
]);
|
||||
|
||||
$response = \curl_exec($ch);
|
||||
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
$error = \curl_error($ch);
|
||||
if (!empty($error)) {
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
\curl_close($ch);
|
||||
|
||||
if ($responseStatus >= 400) {
|
||||
throw new \Exception("Build failed with status code: $responseStatus");
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildDeployment(string $projectId, string $functionId, string $deploymentId)
|
||||
{
|
||||
$dbForProject = $this->getProjectDB($projectId);
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
}
|
||||
|
||||
// Get deployment document
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
if ($deployment->isEmpty()) {
|
||||
throw new Exception('Deployment not found', 404);
|
||||
}
|
||||
|
||||
$runtimes = Config::getParam('runtimes', []);
|
||||
$key = $function->getAttribute('runtime');
|
||||
$runtime = isset($runtimes[$key]) ? $runtimes[$key] : null;
|
||||
if (\is_null($runtime)) {
|
||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
$buildId = $deployment->getAttribute('buildId', '');
|
||||
|
||||
// If build ID is empty, create a new build
|
||||
if (empty($buildId)) {
|
||||
try {
|
||||
$buildId = $dbForProject->getId();
|
||||
// TODO : There is no way to associate a build with a deployment. So we need to add a deploymentId attribute to the build document
|
||||
$dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$read' => [],
|
||||
'$write' => [],
|
||||
'dateCreated' => time(),
|
||||
'status' => 'processing',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'outputPath' => '',
|
||||
'source' => $deployment->getAttribute('path'),
|
||||
'sourceType' => Storage::DEVICE_LOCAL,
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'time' => 0,
|
||||
'vars' => [
|
||||
'ENTRYPOINT_NAME' => $deployment->getAttribute('entrypoint'),
|
||||
'APPWRITE_FUNCTION_ID' => $function->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $projectId,
|
||||
]
|
||||
]));
|
||||
} catch (\Throwable $th) {
|
||||
$deployment->setAttribute('status', 'failed');
|
||||
$deployment->setAttribute('buildId', '');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
Console::error($th->getMessage());
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the Code
|
||||
try {
|
||||
$deployment->setAttribute('buildId', $buildId);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
$this->createBuild($projectId, $functionId, $deploymentId, $buildId);
|
||||
} catch (\Throwable $th) {
|
||||
$deployment->setAttribute('status', 'failed');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
Console::error($th->getMessage());
|
||||
throw $th;
|
||||
}
|
||||
|
||||
Console::success("[ SUCCESS ] Build id: $buildId started");
|
||||
}
|
||||
|
||||
public function shutdown(): void {}
|
||||
}
|
|
@ -224,13 +224,12 @@ class FunctionsV1 extends Worker
|
|||
public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $eventData = '', string $data = '', array $webhooks = [], string $userId = '', string $jwt = ''): void
|
||||
{
|
||||
$ch = \curl_init();
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/execute");
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/{$function->getId()}/executions");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'trigger' => $trigger,
|
||||
'projectId' => $projectId,
|
||||
'executionId' => $executionId,
|
||||
'functionId' => $function->getId(),
|
||||
'event' => $event,
|
||||
'eventData' => $eventData,
|
||||
'data' => $data,
|
||||
|
|
10
bin/worker-builds
Normal file
10
bin/worker-builds
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ]
|
||||
then
|
||||
REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
|
||||
else
|
||||
REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
|
||||
fi
|
||||
|
||||
INTERVAL=0.1 QUEUE='v1-builds' APP_INCLUDE='/usr/src/code/app/workers/builds.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php
|
|
@ -50,7 +50,7 @@
|
|||
"utopia-php/cache": "0.4.*",
|
||||
"utopia-php/cli": "0.11.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.13.*",
|
||||
"utopia-php/database": "0.14.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
|
|
33
composer.lock
generated
33
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "cba39f50398d5ae2b121db34c9e4c529",
|
||||
"content-hash": "1a5d84f96eb76e59f7ad0ff7bcd4a8d8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -2135,16 +2135,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.13.2",
|
||||
"version": "0.14.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "bf92279b707b3a10ee5ec5df5c065023b2221357"
|
||||
"reference": "2f2527bb080cf578fba327ea2ec637064561d403"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/bf92279b707b3a10ee5ec5df5c065023b2221357",
|
||||
"reference": "bf92279b707b3a10ee5ec5df5c065023b2221357",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/2f2527bb080cf578fba327ea2ec637064561d403",
|
||||
"reference": "2f2527bb080cf578fba327ea2ec637064561d403",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2192,9 +2192,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.13.2"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.14.0"
|
||||
},
|
||||
"time": "2022-01-04T10:51:22+00:00"
|
||||
"time": "2022-01-21T16:34:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -3126,23 +3126,23 @@
|
|||
},
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/pcre.git",
|
||||
"reference": "3d322d715c43a1ac36c7fe215fa59336265500f2"
|
||||
"reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/pcre/zipball/3d322d715c43a1ac36c7fe215fa59336265500f2",
|
||||
"reference": "3d322d715c43a1ac36c7fe215fa59336265500f2",
|
||||
"url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560",
|
||||
"reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1",
|
||||
"phpstan/phpstan": "^1.3",
|
||||
"phpstan/phpstan-strict-rules": "^1.1",
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5"
|
||||
},
|
||||
|
@ -3177,7 +3177,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/pcre/issues",
|
||||
"source": "https://github.com/composer/pcre/tree/1.0.0"
|
||||
"source": "https://github.com/composer/pcre/tree/1.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -3193,7 +3193,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-12-06T15:17:27+00:00"
|
||||
"time": "2022-01-21T20:24:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
|
@ -3697,9 +3697,6 @@
|
|||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.6",
|
||||
|
@ -6665,5 +6662,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
|
|
@ -285,6 +285,34 @@ services:
|
|||
- _APP_DB_PASS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-builds:
|
||||
entrypoint: worker-builds
|
||||
container_name: appwrite-worker-builds
|
||||
build:
|
||||
context: .
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_EXECUTOR_SECRET
|
||||
|
||||
appwrite-worker-certificates:
|
||||
entrypoint: worker-certificates
|
||||
|
@ -350,7 +378,11 @@ services:
|
|||
|
||||
appwrite-executor:
|
||||
container_name: appwrite-executor
|
||||
entrypoint: executor
|
||||
entrypoint:
|
||||
- php
|
||||
- -e
|
||||
- /usr/src/code/app/executor.php
|
||||
- -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php
|
||||
stop_signal: SIGINT
|
||||
build:
|
||||
context: .
|
||||
|
|
|
@ -45,7 +45,7 @@ abstract class Worker
|
|||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function init() {
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
throw new Exception("Please implement init method in worker");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +56,7 @@ abstract class Worker
|
|||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function run() {
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
throw new Exception("Please implement run method in worker");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,7 +67,7 @@ abstract class Worker
|
|||
* @throws \Exception|\Throwable
|
||||
*/
|
||||
public function shutdown() {
|
||||
throw new Exception("Please implement getName method in worker");
|
||||
throw new Exception("Please implement shutdown method in worker");
|
||||
}
|
||||
|
||||
const MAX_ATTEMPTS = 10;
|
||||
|
|
|
@ -242,7 +242,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'async' => 1,
|
||||
'async' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $execution['headers']['status-code']);
|
||||
|
|
Loading…
Reference in a new issue