2022-01-24 11:25:46 +13:00
|
|
|
<?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';
|
|
|
|
|
2022-01-25 09:06:29 +13:00
|
|
|
// Disable Auth since we already validate it in the API
|
|
|
|
Authorization::disable();
|
|
|
|
|
2022-01-24 11:25:46 +13:00
|
|
|
Console::title('Builds V1 Worker');
|
|
|
|
Console::success(APP_NAME.' build worker v1 has started');
|
|
|
|
|
2022-01-25 09:06:29 +13:00
|
|
|
// TODO: Executor should return appropriate response codes.
|
2022-01-24 11:25:46 +13:00
|
|
|
class BuildsV1 extends Worker
|
2022-01-24 12:01:42 +13:00
|
|
|
{
|
2022-01-24 11:25:46 +13:00
|
|
|
|
2022-01-26 12:45:41 +13:00
|
|
|
public function getName(): string
|
|
|
|
{
|
2022-01-24 11:25:46 +13:00
|
|
|
return "builds";
|
|
|
|
}
|
|
|
|
|
2022-01-26 12:45:41 +13:00
|
|
|
public function init(): void {}
|
2022-01-24 11:25:46 +13:00
|
|
|
|
|
|
|
public function run(): void
|
|
|
|
{
|
|
|
|
$type = $this->args['type'] ?? '';
|
|
|
|
$projectId = $this->args['projectId'] ?? '';
|
|
|
|
|
|
|
|
switch ($type) {
|
2022-01-27 02:09:32 +13:00
|
|
|
case BUILD_TYPE_DEPLOYMENT:
|
2022-01-24 11:25:46 +13:00
|
|
|
$functionId = $this->args['functionId'] ?? '';
|
2022-01-27 02:09:32 +13:00
|
|
|
$deploymentId = $this->args['deploymentId'] ?? '';
|
|
|
|
Console::info("[ INFO ] Creating build for deployment: $deploymentId");
|
|
|
|
$this->buildDeployment($projectId, $functionId, $deploymentId);
|
2022-01-24 11:25:46 +13:00
|
|
|
break;
|
2022-01-24 12:01:42 +13:00
|
|
|
|
|
|
|
case BUILD_TYPE_RETRY:
|
|
|
|
$buildId = $this->args['buildId'] ?? '';
|
2022-01-27 11:29:49 +13:00
|
|
|
$functionId = $this->args['functionId'] ?? '';
|
|
|
|
$deploymentId = $this->args['deploymentId'] ?? '';
|
2022-01-25 09:06:29 +13:00
|
|
|
Console::info("[ INFO ] Retrying build for id: $buildId");
|
2022-01-27 11:29:49 +13:00
|
|
|
$this->createBuild($projectId, $functionId, $deploymentId, $buildId);
|
2022-01-24 12:01:42 +13:00
|
|
|
break;
|
|
|
|
|
2022-01-24 11:25:46 +13:00
|
|
|
default:
|
2022-01-24 12:01:42 +13:00
|
|
|
throw new \Exception('Invalid build type');
|
2022-01-24 11:25:46 +13:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-27 11:29:49 +13:00
|
|
|
protected function createBuild(string $projectId, string $functionId, string $deploymentId, string $buildId)
|
2022-01-24 11:25:46 +13:00
|
|
|
{
|
|
|
|
// TODO: What is a reasonable time to wait for a build to complete?
|
|
|
|
$ch = \curl_init();
|
2022-01-27 11:29:49 +13:00
|
|
|
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/$functionId/deployments/$deploymentId/builds/$buildId");
|
2022-01-24 11:25:46 +13:00
|
|
|
\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);
|
|
|
|
|
2022-01-25 09:06:29 +13:00
|
|
|
if ($responseStatus >= 400) {
|
2022-01-24 11:25:46 +13:00
|
|
|
throw new \Exception("Build failed with status code: $responseStatus");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-27 02:09:32 +13:00
|
|
|
protected function buildDeployment(string $projectId, string $functionId, string $deploymentId)
|
2022-01-24 11:25:46 +13:00
|
|
|
{
|
|
|
|
$dbForProject = $this->getProjectDB($projectId);
|
|
|
|
|
2022-01-25 09:06:29 +13:00
|
|
|
$function = $dbForProject->getDocument('functions', $functionId);
|
2022-01-24 11:25:46 +13:00
|
|
|
if ($function->isEmpty()) {
|
|
|
|
throw new Exception('Function not found', 404);
|
|
|
|
}
|
|
|
|
|
2022-01-27 02:09:32 +13:00
|
|
|
// Get deployment document
|
|
|
|
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
|
|
|
if ($deployment->isEmpty()) {
|
|
|
|
throw new Exception('Deployment not found', 404);
|
2022-01-24 11:25:46 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
$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');
|
|
|
|
}
|
|
|
|
|
2022-01-27 02:09:32 +13:00
|
|
|
$buildId = $deployment->getAttribute('buildId', '');
|
2022-01-24 11:25:46 +13:00
|
|
|
|
|
|
|
// If build ID is empty, create a new build
|
|
|
|
if (empty($buildId)) {
|
|
|
|
try {
|
|
|
|
$buildId = $dbForProject->getId();
|
2022-01-27 02:09:32 +13:00
|
|
|
// TODO : There is no way to associate a build with a deployment. So we need to add a deploymentId attribute to the build document
|
2022-01-24 11:25:46 +13:00
|
|
|
$dbForProject->createDocument('builds', new Document([
|
|
|
|
'$id' => $buildId,
|
2022-01-25 09:06:29 +13:00
|
|
|
'$read' => [],
|
|
|
|
'$write' => [],
|
2022-01-28 12:36:30 +13:00
|
|
|
'deploymentId' => $deploymentId,
|
2022-01-24 11:25:46 +13:00
|
|
|
'dateCreated' => time(),
|
|
|
|
'status' => 'processing',
|
|
|
|
'runtime' => $function->getAttribute('runtime'),
|
|
|
|
'outputPath' => '',
|
2022-01-27 02:09:32 +13:00
|
|
|
'source' => $deployment->getAttribute('path'),
|
2022-01-24 11:25:46 +13:00
|
|
|
'sourceType' => Storage::DEVICE_LOCAL,
|
|
|
|
'stdout' => '',
|
|
|
|
'stderr' => '',
|
2022-01-26 12:45:41 +13:00
|
|
|
'time' => 0,
|
|
|
|
'vars' => [
|
2022-01-27 02:09:32 +13:00
|
|
|
'ENTRYPOINT_NAME' => $deployment->getAttribute('entrypoint'),
|
2022-01-24 11:25:46 +13:00
|
|
|
'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) {
|
2022-01-27 02:09:32 +13:00
|
|
|
$deployment->setAttribute('status', 'failed');
|
|
|
|
$deployment->setAttribute('buildId', '');
|
|
|
|
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
2022-01-27 11:29:49 +13:00
|
|
|
Console::error($th->getMessage());
|
|
|
|
throw $th;
|
2022-01-24 11:25:46 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the Code
|
|
|
|
try {
|
2022-01-28 01:50:22 +13:00
|
|
|
$deployment->setAttribute('buildId', $buildId);
|
2022-01-27 02:09:32 +13:00
|
|
|
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
2022-01-27 11:29:49 +13:00
|
|
|
$this->createBuild($projectId, $functionId, $deploymentId, $buildId);
|
2022-01-24 11:25:46 +13:00
|
|
|
} catch (\Throwable $th) {
|
2022-01-27 02:09:32 +13:00
|
|
|
$deployment->setAttribute('status', 'failed');
|
|
|
|
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
2022-01-24 11:25:46 +13:00
|
|
|
Console::error($th->getMessage());
|
2022-01-27 11:29:49 +13:00
|
|
|
throw $th;
|
2022-01-24 11:25:46 +13:00
|
|
|
}
|
|
|
|
|
2022-01-27 11:29:49 +13:00
|
|
|
Console::success("[ SUCCESS ] Build id: $buildId started");
|
2022-01-24 11:25:46 +13:00
|
|
|
}
|
|
|
|
|
2022-01-26 12:45:41 +13:00
|
|
|
public function shutdown(): void {}
|
2022-01-24 11:25:46 +13:00
|
|
|
}
|