1
0
Fork 0
mirror of synced 2024-06-29 11:40:45 +12:00

Merge pull request #7605 from appwrite/feat-cancel-build-deployment

Added cancel build endpoint
This commit is contained in:
Christy Jacob 2024-06-13 19:06:03 +05:30 committed by GitHub
commit 83d60612f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 254 additions and 76 deletions

View file

@ -541,6 +541,11 @@ return [
'description' => 'Build with the requested ID is already in progress. Please wait before you can retry.', 'description' => 'Build with the requested ID is already in progress. Please wait before you can retry.',
'code' => 400, 'code' => 400,
], ],
Exception::BUILD_ALREADY_COMPLETED => [
'name' => Exception::BUILD_ALREADY_COMPLETED,
'description' => 'Build with the requested ID is already completed and cannot be canceled.',
'code' => 400,
],
/** Deployments */ /** Deployments */
Exception::DEPLOYMENT_NOT_FOUND => [ Exception::DEPLOYMENT_NOT_FOUND => [

View file

@ -1444,7 +1444,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions') ->label('sdk.namespace', 'functions')
->label('sdk.method', 'createBuild') ->label('sdk.method', 'createBuild')
->label('sdk.description', '/docs/references/functions/create-build.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.') ->param('functionId', '', new UID(), 'Function ID.')
@ -1462,7 +1461,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
if ($function->isEmpty()) { if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND); throw new Exception(Exception::FUNCTION_NOT_FOUND);
} }
$deployment = $dbForProject->getDocument('deployments', $deploymentId); $deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) { if ($deployment->isEmpty()) {
@ -1493,6 +1491,86 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
$response->noContent(); $response->noContent();
}); });
App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->groups(['api', 'functions'])
->desc('Cancel deployment')
->label('scope', 'functions.write')
->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateDeploymentBuild')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUILD)
->param('functionId', '', new UID(), 'Function ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
$buildId = ID::unique();
$build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
'$permissions' => [],
'startTime' => DateTime::now(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentId' => $deployment->getId(),
'status' => 'canceled',
'path' => '',
'runtime' => $function->getAttribute('runtime'),
'source' => $deployment->getAttribute('path', ''),
'sourceType' => '',
'logs' => '',
'duration' => 0,
'size' => 0
]));
$deployment->setAttribute('buildId', $build->getId());
$deployment->setAttribute('buildInternalId', $build->getInternalId());
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
} else {
if (\in_array($build->getAttribute('status'), ['ready', 'failed'])) {
throw new Exception(Exception::BUILD_ALREADY_COMPLETED);
}
$startTime = new \DateTime($build->getAttribute('startTime'));
$endTime = new \DateTime('now');
$duration = $endTime->getTimestamp() - $startTime->getTimestamp();
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttributes([
'endTime' => DateTime::now(),
'duration' => $duration,
'status' => 'canceled'
]));
}
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$deleteBuild = $executor->deleteRuntime($project->getId(), $deploymentId . "-build");
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response->dynamic($build, Response::MODEL_BUILD);
});
App::post('/v1/functions/:functionId/executions') App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions']) ->groups(['api', 'functions'])
->desc('Create execution') ->desc('Create execution')

121
composer.lock generated
View file

@ -822,16 +822,16 @@
}, },
{ {
"name": "paragonie/constant_time_encoding", "name": "paragonie/constant_time_encoding",
"version": "v2.6.3", "version": "v2.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git", "url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "58c3f47f650c94ec05a151692652a868995d2938" "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105",
"reference": "58c3f47f650c94ec05a151692652a868995d2938", "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -885,7 +885,7 @@
"issues": "https://github.com/paragonie/constant_time_encoding/issues", "issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding" "source": "https://github.com/paragonie/constant_time_encoding"
}, },
"time": "2022-06-14T06:56:20+00:00" "time": "2024-05-08T12:18:48+00:00"
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
@ -1672,16 +1672,16 @@
}, },
{ {
"name": "utopia-php/dsn", "name": "utopia-php/dsn",
"version": "0.2.0", "version": "0.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/dsn.git", "url": "https://github.com/utopia-php/dsn.git",
"reference": "c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7" "reference": "42ee37a3d1785100b2f69091c9d4affadb6846eb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/dsn/zipball/c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7", "url": "https://api.github.com/repos/utopia-php/dsn/zipball/42ee37a3d1785100b2f69091c9d4affadb6846eb",
"reference": "c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7", "reference": "42ee37a3d1785100b2f69091c9d4affadb6846eb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1713,9 +1713,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/dsn/issues", "issues": "https://github.com/utopia-php/dsn/issues",
"source": "https://github.com/utopia-php/dsn/tree/0.2.0" "source": "https://github.com/utopia-php/dsn/tree/0.2.1"
}, },
"time": "2023-11-02T12:01:43+00:00" "time": "2024-05-07T02:01:25+00:00"
}, },
{ {
"name": "utopia-php/framework", "name": "utopia-php/framework",
@ -1966,22 +1966,21 @@
}, },
{ {
"name": "utopia-php/migration", "name": "utopia-php/migration",
"version": "0.4.1", "version": "0.4.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/migration.git", "url": "https://github.com/utopia-php/migration.git",
"reference": "ae3cfe93f6d313105d226aeb68806660c806a925" "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ae3cfe93f6d313105d226aeb68806660c806a925", "url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33",
"reference": "ae3cfe93f6d313105d226aeb68806660c806a925", "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"appwrite/appwrite": "10.1.0", "appwrite/appwrite": "10.1.0",
"php": "8.*", "php": "8.*"
"utopia-php/cli": "0.*"
}, },
"require-dev": { "require-dev": {
"laravel/pint": "1.*", "laravel/pint": "1.*",
@ -2008,9 +2007,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/migration/issues", "issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.4.1" "source": "https://github.com/utopia-php/migration/tree/0.4.4"
}, },
"time": "2024-05-01T13:19:18+00:00" "time": "2024-05-17T05:25:31+00:00"
}, },
{ {
"name": "utopia-php/mongo", "name": "utopia-php/mongo",
@ -2124,16 +2123,16 @@
}, },
{ {
"name": "utopia-php/platform", "name": "utopia-php/platform",
"version": "0.5.1", "version": "0.5.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/platform.git", "url": "https://github.com/utopia-php/platform.git",
"reference": "3eceef0b6593fe0f7d2efd36d40402a395a4c285" "reference": "b9feabc79b92dc2b05683a986ad43bce5c1583e3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/3eceef0b6593fe0f7d2efd36d40402a395a4c285", "url": "https://api.github.com/repos/utopia-php/platform/zipball/b9feabc79b92dc2b05683a986ad43bce5c1583e3",
"reference": "3eceef0b6593fe0f7d2efd36d40402a395a4c285", "reference": "b9feabc79b92dc2b05683a986ad43bce5c1583e3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2141,7 +2140,7 @@
"ext-redis": "*", "ext-redis": "*",
"php": ">=8.0", "php": ">=8.0",
"utopia-php/cli": "0.15.*", "utopia-php/cli": "0.15.*",
"utopia-php/framework": "0.*.*" "utopia-php/framework": "0.33.*"
}, },
"require-dev": { "require-dev": {
"laravel/pint": "1.2.*", "laravel/pint": "1.2.*",
@ -2167,9 +2166,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/platform/issues", "issues": "https://github.com/utopia-php/platform/issues",
"source": "https://github.com/utopia-php/platform/tree/0.5.1" "source": "https://github.com/utopia-php/platform/tree/0.5.2"
}, },
"time": "2023-12-26T16:14:41+00:00" "time": "2024-05-22T12:50:35+00:00"
}, },
{ {
"name": "utopia-php/pools", "name": "utopia-php/pools",
@ -2499,16 +2498,16 @@
}, },
{ {
"name": "utopia-php/vcs", "name": "utopia-php/vcs",
"version": "0.6.5", "version": "0.6.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/vcs.git", "url": "https://github.com/utopia-php/vcs.git",
"reference": "104e47ea8e38c156ec0e0bd415caa3dcd5046fe2" "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/104e47ea8e38c156ec0e0bd415caa3dcd5046fe2", "url": "https://api.github.com/repos/utopia-php/vcs/zipball/e538264cfee5e3efdfe1771efba04750cf20b2c4",
"reference": "104e47ea8e38c156ec0e0bd415caa3dcd5046fe2", "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2542,9 +2541,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/vcs/issues", "issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.6.5" "source": "https://github.com/utopia-php/vcs/tree/0.6.6"
}, },
"time": "2024-01-08T17:11:12+00:00" "time": "2024-05-17T09:36:30+00:00"
}, },
{ {
"name": "utopia-php/websocket", "name": "utopia-php/websocket",
@ -2731,16 +2730,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "appwrite/sdk-generator", "name": "appwrite/sdk-generator",
"version": "0.38.2", "version": "0.38.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/appwrite/sdk-generator.git", "url": "https://github.com/appwrite/sdk-generator.git",
"reference": "51284668529e2b10ed933412a42b603c76cded23" "reference": "d7016d6d72545e84709892faca972eb4bf5bd699"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/51284668529e2b10ed933412a42b603c76cded23", "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d7016d6d72545e84709892faca972eb4bf5bd699",
"reference": "51284668529e2b10ed933412a42b603c76cded23", "reference": "d7016d6d72545e84709892faca972eb4bf5bd699",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2776,9 +2775,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": { "support": {
"issues": "https://github.com/appwrite/sdk-generator/issues", "issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.38.2" "source": "https://github.com/appwrite/sdk-generator/tree/0.38.6"
}, },
"time": "2024-04-25T07:49:29+00:00" "time": "2024-05-20T18:00:16+00:00"
}, },
{ {
"name": "doctrine/deprecations", "name": "doctrine/deprecations",
@ -2899,16 +2898,16 @@
}, },
{ {
"name": "laravel/pint", "name": "laravel/pint",
"version": "v1.15.3", "version": "v1.16.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/pint.git", "url": "https://github.com/laravel/pint.git",
"reference": "3600b5d17aff52f6100ea4921849deacbbeb8656" "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/3600b5d17aff52f6100ea4921849deacbbeb8656", "url": "https://api.github.com/repos/laravel/pint/zipball/1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98",
"reference": "3600b5d17aff52f6100ea4921849deacbbeb8656", "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2919,11 +2918,11 @@
"php": "^8.1.0" "php": "^8.1.0"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^3.54.0", "friendsofphp/php-cs-fixer": "^3.57.1",
"illuminate/view": "^10.48.8", "illuminate/view": "^10.48.10",
"larastan/larastan": "^2.9.5", "larastan/larastan": "^2.9.6",
"laravel-zero/framework": "^10.3.0", "laravel-zero/framework": "^10.4.0",
"mockery/mockery": "^1.6.11", "mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^1.15.1", "nunomaduro/termwind": "^1.15.1",
"pestphp/pest": "^2.34.7" "pestphp/pest": "^2.34.7"
}, },
@ -2961,7 +2960,7 @@
"issues": "https://github.com/laravel/pint/issues", "issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint" "source": "https://github.com/laravel/pint"
}, },
"time": "2024-04-30T15:02:26+00:00" "time": "2024-05-21T18:08:25+00:00"
}, },
{ {
"name": "matthiasmullie/minify", "name": "matthiasmullie/minify",
@ -3377,16 +3376,16 @@
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",
"version": "5.4.0", "version": "5.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "298d2febfe79d03fe714eb871d5538da55205b1a" "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/298d2febfe79d03fe714eb871d5538da55205b1a", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
"reference": "298d2febfe79d03fe714eb871d5538da55205b1a", "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3435,9 +3434,9 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": { "support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.0" "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1"
}, },
"time": "2024-04-09T21:13:58+00:00" "time": "2024-05-21T05:55:05+00:00"
}, },
{ {
"name": "phpdocumentor/type-resolver", "name": "phpdocumentor/type-resolver",
@ -3568,16 +3567,16 @@
}, },
{ {
"name": "phpstan/phpdoc-parser", "name": "phpstan/phpdoc-parser",
"version": "1.28.0", "version": "1.29.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git", "url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc",
"reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3609,9 +3608,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types", "description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": { "support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues", "issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0"
}, },
"time": "2024-04-03T18:51:33+00:00" "time": "2024-05-06T12:04:23+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -5476,5 +5475,5 @@
"platform-overrides": { "platform-overrides": {
"php": "8.3" "php": "8.3"
}, },
"plugin-api-version": "2.6.0" "plugin-api-version": "2.3.0"
} }

View file

@ -1 +0,0 @@
Create a new build for an Appwrite Function deployment. This endpoint can be used to retry a failed build.

View file

@ -163,6 +163,7 @@ class Exception extends \Exception
public const BUILD_NOT_FOUND = 'build_not_found'; public const BUILD_NOT_FOUND = 'build_not_found';
public const BUILD_NOT_READY = 'build_not_ready'; public const BUILD_NOT_READY = 'build_not_ready';
public const BUILD_IN_PROGRESS = 'build_in_progress'; public const BUILD_IN_PROGRESS = 'build_in_progress';
public const BUILD_ALREADY_COMPLETED = 'build_already_completed';
/** Execution */ /** Execution */
public const EXECUTION_NOT_FOUND = 'execution_not_found'; public const EXECUTION_NOT_FOUND = 'execution_not_found';

View file

@ -8,6 +8,7 @@ use Appwrite\Event\Usage;
use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Vcs\Comment; use Appwrite\Vcs\Comment;
use Exception;
use Executor\Executor; use Executor\Executor;
use Swoole\Coroutine as Co; use Swoole\Coroutine as Co;
use Utopia\Cache\Cache; use Utopia\Cache\Cache;
@ -156,9 +157,9 @@ class Builds extends Action
$startTime = DateTime::now(); $startTime = DateTime::now();
$durationStart = \microtime(true); $durationStart = \microtime(true);
$buildId = $deployment->getAttribute('buildId', ''); $buildId = $deployment->getAttribute('buildId', '');
$build = $dbForProject->getDocument('builds', $buildId);
$isNewBuild = empty($buildId); $isNewBuild = empty($buildId);
if ($build->isEmpty()) {
if ($isNewBuild) {
$buildId = ID::unique(); $buildId = ID::unique();
$build = $dbForProject->createDocument('builds', new Document([ $build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId, '$id' => $buildId,
@ -180,6 +181,9 @@ class Builds extends Action
$deployment->setAttribute('buildId', $build->getId()); $deployment->setAttribute('buildId', $build->getId());
$deployment->setAttribute('buildInternalId', $build->getInternalId()); $deployment->setAttribute('buildInternalId', $build->getInternalId());
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
} elseif ($build->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
} else { } else {
$build = $dbForProject->getDocument('builds', $buildId); $build = $dbForProject->getDocument('builds', $buildId);
} }
@ -221,6 +225,12 @@ class Builds extends Action
$stdout = ''; $stdout = '';
$stderr = ''; $stderr = '';
Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr); Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr);
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr); $exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
if ($exit !== 0) { if ($exit !== 0) {
@ -397,6 +407,11 @@ class Builds extends Action
$response = null; $response = null;
$err = null; $err = null;
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
Co::join([ Co::join([
Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) { Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) {
try { try {
@ -463,6 +478,10 @@ class Builds extends Action
]); ]);
if ($err) { if ($err) {
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
throw $err; throw $err;
} }
@ -492,6 +511,11 @@ class Builds extends Action
$function = $dbForProject->updateDocument('functions', $function->getId(), $function); $function = $dbForProject->updateDocument('functions', $function->getId(), $function);
} }
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
/** Update function schedule */ /** Update function schedule */
// Inform scheduler if function is still active // Inform scheduler if function is still active
@ -502,6 +526,11 @@ class Builds extends Action
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
} catch (\Throwable $th) { } catch (\Throwable $th) {
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
$endTime = DateTime::now(); $endTime = DateTime::now();
$durationEnd = \microtime(true); $durationEnd = \microtime(true);
$build->setAttribute('endTime', $endTime); $build->setAttribute('endTime', $endTime);

View file

@ -434,6 +434,73 @@ class FunctionsCustomServerTest extends Scope
return array_merge($data, ['deploymentId' => $deploymentId]); return array_merge($data, ['deploymentId' => $deploymentId]);
} }
/**
* @depends testUpdate
*/
public function testCancelDeploymentBuild($data): void
{
// Create a new deployment to cancel
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
'activate' => true
]);
$deploymentId = $deployment['body']['$id'] ?? '';
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt']));
$this->assertEquals('index.php', $deployment['body']['entrypoint']);
// Poll until deployment is in progress
while (true) {
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
if (
$deployment['headers']['status-code'] >= 400
|| $deployment['body']['status'] === 'building'
) {
break;
}
\sleep(1);
}
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals('building', $deployment['body']['status']);
// Cancel the deployment build
$cancel = $this->client->call(Client::METHOD_PATCH, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId . '/build', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $cancel['headers']['status-code']);
$this->assertEquals('canceled', $cancel['body']['status']);
// Confirm the deployment is canceled
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals('canceled', $deployment['body']['status']);
}
/** /**
* @depends testUpdate * @depends testUpdate
*/ */
@ -523,9 +590,9 @@ class FunctionsCustomServerTest extends Scope
], $this->getHeaders())); ], $this->getHeaders()));
$this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['headers']['status-code'], 200);
$this->assertEquals($function['body']['total'], 2); $this->assertEquals($function['body']['total'], 3);
$this->assertIsArray($function['body']['deployments']); $this->assertIsArray($function['body']['deployments']);
$this->assertCount(2, $function['body']['deployments']); $this->assertCount(3, $function['body']['deployments']);
/** /**
* Test search queries * Test search queries
@ -538,9 +605,9 @@ class FunctionsCustomServerTest extends Scope
])); ]));
$this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['headers']['status-code'], 200);
$this->assertEquals(2, $function['body']['total']); $this->assertEquals(3, $function['body']['total']);
$this->assertIsArray($function['body']['deployments']); $this->assertIsArray($function['body']['deployments']);
$this->assertCount(2, $function['body']['deployments']); $this->assertCount(3, $function['body']['deployments']);
$this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']); $this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
@ -565,7 +632,7 @@ class FunctionsCustomServerTest extends Scope
]); ]);
$this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['headers']['status-code'], 200);
$this->assertCount(1, $function['body']['deployments']); $this->assertCount(2, $function['body']['deployments']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -577,7 +644,7 @@ class FunctionsCustomServerTest extends Scope
]); ]);
$this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['headers']['status-code'], 200);
$this->assertCount(2, $function['body']['deployments']); $this->assertCount(3, $function['body']['deployments']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
@ -599,9 +666,9 @@ class FunctionsCustomServerTest extends Scope
])); ]));
$this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['headers']['status-code'], 200);
$this->assertEquals(2, $function['body']['total']); $this->assertEquals(3, $function['body']['total']);
$this->assertIsArray($function['body']['deployments']); $this->assertIsArray($function['body']['deployments']);
$this->assertCount(2, $function['body']['deployments']); $this->assertCount(3, $function['body']['deployments']);
$this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']); $this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
@ -612,9 +679,9 @@ class FunctionsCustomServerTest extends Scope
])); ]));
$this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['headers']['status-code'], 200);
$this->assertEquals(2, $function['body']['total']); $this->assertEquals(3, $function['body']['total']);
$this->assertIsArray($function['body']['deployments']); $this->assertIsArray($function['body']['deployments']);
$this->assertCount(2, $function['body']['deployments']); $this->assertCount(3, $function['body']['deployments']);
$this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']); $this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']);
return $data; return $data;