Merge remote-tracking branch 'origin/1.6.x' into feat-separate-function-sizes
This commit is contained in:
commit
695844faa1
36 changed files with 567 additions and 50 deletions
4
.env
4
.env
|
@ -69,8 +69,8 @@ _APP_STORAGE_PREVIEW_LIMIT=20000000
|
|||
_APP_FUNCTIONS_SIZE_LIMIT=30000000
|
||||
_APP_FUNCTIONS_TIMEOUT=900
|
||||
_APP_FUNCTIONS_BUILD_TIMEOUT=900
|
||||
_APP_FUNCTIONS_CPUS=1
|
||||
_APP_FUNCTIONS_MEMORY=1024
|
||||
_APP_FUNCTIONS_CPUS=8
|
||||
_APP_FUNCTIONS_MEMORY=8192
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=600
|
||||
_APP_FUNCTIONS_MAINTENANCE_INTERVAL=600
|
||||
_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes
|
||||
|
|
|
@ -3055,14 +3055,25 @@ $projectCollections = array_merge([
|
|||
'default' => null,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('specification'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => APP_FUNCTION_SPECIFICATION_DEFAULT,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('scopes'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
|
@ -4784,8 +4795,8 @@ $consoleCollections = array_merge([
|
|||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
|
|
51
app/config/runtimes/specifications.php
Normal file
51
app/config/runtimes/specifications.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Functions\Specification;
|
||||
|
||||
return [
|
||||
Specification::S_05VCPU_512MB => [
|
||||
'slug' => Specification::S_05VCPU_512MB,
|
||||
'memory' => 512,
|
||||
'cpus' => 0.5
|
||||
],
|
||||
Specification::S_1VCPU_512MB => [
|
||||
'slug' => Specification::S_1VCPU_512MB,
|
||||
'memory' => 512,
|
||||
'cpus' => 1
|
||||
],
|
||||
Specification::S_1VCPU_1GB => [
|
||||
'slug' => Specification::S_1VCPU_1GB,
|
||||
'memory' => 1024,
|
||||
'cpus' => 1
|
||||
],
|
||||
Specification::S_2VCPU_2GB => [
|
||||
'slug' => Specification::S_2VCPU_2GB,
|
||||
'memory' => 2048,
|
||||
'cpus' => 2
|
||||
],
|
||||
Specification::S_2VCPU_4GB => [
|
||||
'slug' => Specification::S_2VCPU_4GB,
|
||||
'memory' => 4096,
|
||||
'cpus' => 2
|
||||
],
|
||||
Specification::S_4VCPU_4GB => [
|
||||
'slug' => Specification::S_4VCPU_4GB,
|
||||
'memory' => 4096,
|
||||
'cpus' => 4
|
||||
],
|
||||
Specification::S_4VCPU_8GB => [
|
||||
'slug' => Specification::S_4VCPU_8GB,
|
||||
'memory' => 8192,
|
||||
'cpus' => 4
|
||||
],
|
||||
Specification::S_8VCPU_4GB => [
|
||||
'slug' => Specification::S_8VCPU_4GB,
|
||||
'memory' => 4096,
|
||||
'cpus' => 8
|
||||
],
|
||||
Specification::S_8VCPU_8GB => [
|
||||
'slug' => Specification::S_8VCPU_8GB,
|
||||
'memory' => 8192,
|
||||
'cpus' => 8
|
||||
]
|
||||
];
|
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -11,6 +11,7 @@ use Appwrite\Event\Validator\FunctionEvent;
|
|||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Functions\Validator\Headers;
|
||||
use Appwrite\Functions\Validator\RuntimeSpecification;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Platform\Tasks\ScheduleExecutions;
|
||||
use Appwrite\Task\Validator\Cron;
|
||||
|
@ -166,6 +167,12 @@ App::post('/v1/functions')
|
|||
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
|
||||
->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true)
|
||||
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.', true)
|
||||
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||
$plan,
|
||||
Config::getParam('runtime-specifications', []),
|
||||
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
||||
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
@ -175,7 +182,7 @@ App::post('/v1/functions')
|
|||
->inject('queueForBuilds')
|
||||
->inject('dbForConsole')
|
||||
->inject('gitHub')
|
||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
||||
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
||||
|
||||
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
||||
|
@ -236,6 +243,7 @@ App::post('/v1/functions')
|
|||
'providerBranch' => $providerBranch,
|
||||
'providerRootDirectory' => $providerRootDirectory,
|
||||
'providerSilentMode' => $providerSilentMode,
|
||||
'specification' => $specification
|
||||
]));
|
||||
|
||||
$schedule = Authorization::skip(
|
||||
|
@ -474,6 +482,42 @@ App::get('/v1/functions/runtimes')
|
|||
]), Response::MODEL_RUNTIME_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/functions/specifications')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List available function runtime specifications')
|
||||
->label('scope', 'functions.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listSpecifications')
|
||||
->label('sdk.description', '/docs/references/functions/list-specifications.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SPECIFICATION_LIST)
|
||||
->inject('response')
|
||||
->inject('plan')
|
||||
->action(function (Response $response, array $plan) {
|
||||
$allRuntimeSpecs = Config::getParam('runtime-specifications', []);
|
||||
|
||||
$runtimeSpecs = [];
|
||||
foreach ($allRuntimeSpecs as $spec) {
|
||||
$spec['enabled'] = true;
|
||||
|
||||
if (array_key_exists('runtimeSpecifications', $plan)) {
|
||||
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
|
||||
}
|
||||
|
||||
// Only add specs that are within the limits set by environment variables
|
||||
if ($spec['cpus'] <= System::getEnv('_APP_FUNCTIONS_CPUS', 1) && $spec['memory'] <= System::getEnv('_APP_FUNCTIONS_MEMORY', 512)) {
|
||||
$runtimeSpecs[] = $spec;
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'specifications' => $runtimeSpecs,
|
||||
'total' => count($runtimeSpecs)
|
||||
]), Response::MODEL_SPECIFICATION_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/functions/:functionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Get function')
|
||||
|
@ -732,6 +776,12 @@ App::put('/v1/functions/:functionId')
|
|||
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
|
||||
->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true)
|
||||
->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true)
|
||||
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||
$plan,
|
||||
Config::getParam('runtime-specifications', []),
|
||||
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
||||
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
@ -740,9 +790,8 @@ App::put('/v1/functions/:functionId')
|
|||
->inject('queueForBuilds')
|
||||
->inject('dbForConsole')
|
||||
->inject('gitHub')
|
||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
||||
// TODO: If only branch changes, re-deploy
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
|
@ -840,6 +889,21 @@ App::put('/v1/functions/:functionId')
|
|||
$live = false;
|
||||
}
|
||||
|
||||
$spec = Config::getParam('runtime-specifications')[$specification] ?? [];
|
||||
|
||||
// Enforce Cold Start if spec limits change.
|
||||
if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
|
||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
|
||||
} catch (\Throwable $th) {
|
||||
// Don't throw if the deployment doesn't exist
|
||||
if ($th->getCode() !== 404) {
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
|
||||
'execute' => $execute,
|
||||
'name' => $name,
|
||||
|
@ -861,6 +925,7 @@ App::put('/v1/functions/:functionId')
|
|||
'providerBranch' => $providerBranch,
|
||||
'providerRootDirectory' => $providerRootDirectory,
|
||||
'providerSilentMode' => $providerSilentMode,
|
||||
'specification' => $specification,
|
||||
'search' => implode(' ', [$functionId, $name, $runtime]),
|
||||
])));
|
||||
|
||||
|
@ -1639,7 +1704,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_MULTIPART)
|
||||
->label('sdk.response.model', Response::MODEL_EXECUTION)
|
||||
->label('sdk.request.type', Response::CONTENT_TYPE_MULTIPART)
|
||||
->label('sdk.request.type', Response::CONTENT_TYPE_JSON)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
|
||||
->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true)
|
||||
|
@ -1697,6 +1762,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT)];
|
||||
|
||||
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
|
||||
|
||||
|
@ -1908,6 +1974,8 @@ App::post('/v1/functions/:functionId/executions')
|
|||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
]);
|
||||
|
@ -1932,6 +2000,8 @@ App::post('/v1/functions/:functionId/executions')
|
|||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: $command,
|
||||
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
logging: $function->getAttribute('logging', true),
|
||||
requestTimeout: 30
|
||||
);
|
||||
|
@ -1970,8 +2040,8 @@ App::post('/v1/functions/:functionId/executions')
|
|||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
;
|
||||
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
|
|
|
@ -133,6 +133,38 @@ App::get('/v1/project/usage')
|
|||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
return [
|
||||
'resourceId' => $id,
|
||||
'name' => $name,
|
||||
'value' => $value['value'] ?? 0,
|
||||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
$buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
return [
|
||||
'resourceId' => $id,
|
||||
'name' => $name,
|
||||
'value' => $value['value'] ?? 0,
|
||||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
$bucketsBreakdown = array_map(function ($bucket) use ($dbForProject) {
|
||||
$id = $bucket->getId();
|
||||
$name = $bucket->getAttribute('name');
|
||||
|
@ -236,6 +268,8 @@ App::get('/v1/project/usage')
|
|||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
||||
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
|
||||
'bucketsBreakdown' => $bucketsBreakdown,
|
||||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
||||
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
|
||||
'deploymentsStorageBreakdown' => $deploymentsStorageBreakdown,
|
||||
]), Response::MODEL_USAGE_PROJECT);
|
||||
});
|
||||
|
|
|
@ -133,6 +133,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
|||
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT)];
|
||||
|
||||
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
|
||||
|
||||
|
@ -266,6 +267,8 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
|||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
]);
|
||||
|
@ -290,6 +293,8 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
|||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: $command,
|
||||
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
logging: $function->getAttribute('logging', true),
|
||||
requestTimeout: 30
|
||||
);
|
||||
|
@ -329,8 +334,8 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
|||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->setProject($project)
|
||||
->trigger()
|
||||
;
|
||||
|
|
|
@ -33,6 +33,7 @@ use Appwrite\Event\Messaging;
|
|||
use Appwrite\Event\Migration;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\Specification;
|
||||
use Appwrite\GraphQL\Promises\Adapter\Swoole;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
|
@ -147,6 +148,9 @@ const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
|||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
||||
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB;
|
||||
const APP_FUNCTION_CPUS_DEFAULT = 0.5;
|
||||
const APP_FUNCTION_MEMORY_DEFAULT = 512;
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
|
@ -307,6 +311,7 @@ Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
|
|||
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
|
||||
Config::load('runtime-specifications', __DIR__ . '/config/runtimes/specifications.php');
|
||||
Config::load('function-templates', __DIR__ . '/config/function-templates.php');
|
||||
|
||||
/**
|
||||
|
|
12
composer.lock
generated
12
composer.lock
generated
|
@ -2993,16 +2993,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "dev-fix-cookie-fallback",
|
||||
"version": "0.39.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "9cbad6d71172322e7f1121f39fe460ea5ffc3633"
|
||||
"reference": "a3998d8971c43ff2247542c128f98a94fa4833e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9cbad6d71172322e7f1121f39fe460ea5ffc3633",
|
||||
"reference": "9cbad6d71172322e7f1121f39fe460ea5ffc3633",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/a3998d8971c43ff2247542c128f98a94fa4833e7",
|
||||
"reference": "a3998d8971c43ff2247542c128f98a94fa4833e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3038,9 +3038,9 @@
|
|||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/fix-cookie-fallback"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.7"
|
||||
},
|
||||
"time": "2024-08-20T10:27:20+00:00"
|
||||
"time": "2024-08-19T09:33:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
|
|
|
@ -873,7 +873,7 @@ services:
|
|||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.6.5
|
||||
image: openruntimes/executor:0.6.6
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
|
1
docs/references/functions/list-specifications.md
Normal file
1
docs/references/functions/list-specifications.md
Normal file
|
@ -0,0 +1 @@
|
|||
List allowed function specifications for this instance.
|
16
src/Appwrite/Functions/Specification.php
Normal file
16
src/Appwrite/Functions/Specification.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Functions;
|
||||
|
||||
class Specification
|
||||
{
|
||||
public const S_05VCPU_512MB = 's-0.5vcpu-512mb';
|
||||
public const S_1VCPU_512MB = 's-1vcpu-512mb';
|
||||
public const S_1VCPU_1GB = 's-1vcpu-1gb';
|
||||
public const S_2VCPU_2GB = 's-2vcpu-2gb';
|
||||
public const S_2VCPU_4GB = 's-2vcpu-4gb';
|
||||
public const S_4VCPU_4GB = 's-4vcpu-4gb';
|
||||
public const S_4VCPU_8GB = 's-4vcpu-8gb';
|
||||
public const S_8VCPU_4GB = 's-8vcpu-4gb';
|
||||
public const S_8VCPU_8GB = 's-8vcpu-8gb';
|
||||
}
|
112
src/Appwrite/Functions/Validator/RuntimeSpecification.php
Normal file
112
src/Appwrite/Functions/Validator/RuntimeSpecification.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Functions\Validator;
|
||||
|
||||
use Utopia\Validator;
|
||||
|
||||
class RuntimeSpecification extends Validator
|
||||
{
|
||||
private array $plan;
|
||||
|
||||
private array $specifications;
|
||||
|
||||
private float $maxCpus;
|
||||
|
||||
private int $maxMemory;
|
||||
|
||||
public function __construct(array $plan, array $specifications, float $maxCpus, int $maxMemory)
|
||||
{
|
||||
$this->plan = $plan;
|
||||
$this->specifications = $specifications;
|
||||
$this->maxCpus = $maxCpus;
|
||||
$this->maxMemory = $maxMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Allowed Specifications.
|
||||
*
|
||||
* Get allowed specifications taking into account the limits set by the environment variables and the plan.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedSpecifications(): array
|
||||
{
|
||||
$allowedSpecifications = [];
|
||||
|
||||
foreach ($this->specifications as $size => $values) {
|
||||
if ($values['cpus'] <= $this->maxCpus && $values['memory'] <= $this->maxMemory) {
|
||||
if (!empty($this->plan) && array_key_exists('runtimeSpecifications', $this->plan)) {
|
||||
if (!\in_array($size, $this->plan['runtimeSpecifications'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$allowedSpecifications[] = $size;
|
||||
}
|
||||
}
|
||||
|
||||
return $allowedSpecifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* Returns validator description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Specification must be one of: ' . implode(', ', $this->getAllowedSpecifications());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if valid or false if not.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (empty($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!\in_array($value, $this->getAllowedSpecifications())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array.
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type.
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_STRING;
|
||||
}
|
||||
}
|
|
@ -148,6 +148,7 @@ class V21 extends Migration
|
|||
} catch (\Throwable $th) {
|
||||
Console::warning("'scheduleId' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(50000);
|
||||
|
@ -177,7 +178,7 @@ class V21 extends Migration
|
|||
$document->setAttribute('scopes', []);
|
||||
|
||||
// Add size attribute
|
||||
$document->setAttribute('specification', APP_FUNCTION_BASE_SPECIFICATION);
|
||||
$document->setAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT);
|
||||
}
|
||||
|
||||
return $document;
|
||||
|
@ -194,7 +195,7 @@ class V21 extends Migration
|
|||
$bucketId = 'bucket_' . $bucket['$internalId'];
|
||||
|
||||
try {
|
||||
$this->projectDB->updateAttribute($bucketId, 'metadata', size: 75000);
|
||||
$this->projectDB->updateAttribute($bucketId, 'metadata', size: 65534);
|
||||
$this->projectDB->purgeCachedCollection($bucketId);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'bucketId' from {$bucketId}: {$th->getMessage()}");
|
||||
|
|
|
@ -142,6 +142,7 @@ class Builds extends Action
|
|||
}
|
||||
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specifications', APP_FUNCTION_SPECIFICATION_DEFAULT)];
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$key = $function->getAttribute('runtime');
|
||||
$runtime = $runtimes[$key] ?? null;
|
||||
|
@ -470,6 +471,9 @@ class Builds extends Action
|
|||
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
|
||||
}
|
||||
|
||||
$cpus = $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT;
|
||||
$memory = max($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT, 1024); // We have a minimum of 1024MB here because some runtimes can't compile with less memory than this.
|
||||
|
||||
$jwtExpiry = (int)System::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
|
||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||
$apiKey = $jwtObj->encode([
|
||||
|
@ -491,6 +495,8 @@ class Builds extends Action
|
|||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => $cpus,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $memory,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
]);
|
||||
|
@ -506,7 +512,7 @@ class Builds extends Action
|
|||
}
|
||||
|
||||
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, $cpus, $memory, &$err) {
|
||||
try {
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . \trim(\escapeshellarg($command), "\'") . '"';
|
||||
|
@ -517,6 +523,8 @@ class Builds extends Action
|
|||
source: $source,
|
||||
image: $runtime['image'],
|
||||
version: $version,
|
||||
cpus: $cpus,
|
||||
memory: $memory,
|
||||
remove: true,
|
||||
entrypoint: $deployment->getAttribute('entrypoint'),
|
||||
destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}",
|
||||
|
@ -682,11 +690,11 @@ class Builds extends Action
|
|||
->addMetric(METRIC_BUILDS, 1) // per project
|
||||
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
|
||||
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(METRIC_BUILDS_MB_SECONDS, (int)(512 * $build->getAttribute('duration', 0)))
|
||||
->addMetric(METRIC_BUILDS_MB_SECONDS, (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $build->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS), (int)(512 * $build->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $build->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
}
|
||||
|
|
|
@ -315,6 +315,7 @@ class Functions extends Action
|
|||
$user ??= new Document();
|
||||
$functionId = $function->getId();
|
||||
$deploymentId = $function->getAttribute('deployment', '');
|
||||
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT)];
|
||||
|
||||
$log->addTag('deploymentId', $deploymentId);
|
||||
|
||||
|
@ -467,6 +468,8 @@ class Functions extends Action
|
|||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT),
|
||||
'APPWRITE_FUNCTION_MEMORY' => ($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT),
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
]);
|
||||
|
@ -491,6 +494,8 @@ class Functions extends Action
|
|||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: $command,
|
||||
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
logging: $function->getAttribute('logging', true),
|
||||
);
|
||||
|
||||
|
@ -529,8 +534,8 @@ class Functions extends Action
|
|||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000))
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->trigger()
|
||||
;
|
||||
}
|
||||
|
|
|
@ -434,7 +434,7 @@ class Swagger2 extends Format
|
|||
}
|
||||
}
|
||||
|
||||
if ($allowed) {
|
||||
if ($allowed && $validator->getType() === 'string') {
|
||||
$node['enum'] = $validator->getList();
|
||||
$node['x-enum-name'] = $this->getEnumName($route->getLabel('sdk.namespace', ''), $method, $name);
|
||||
$node['x-enum-keys'] = $this->getEnumKeys($route->getLabel('sdk.namespace', ''), $method, $name);
|
||||
|
|
|
@ -84,6 +84,7 @@ use Appwrite\Utopia\Response\Model\ProviderRepository;
|
|||
use Appwrite\Utopia\Response\Model\Rule;
|
||||
use Appwrite\Utopia\Response\Model\Runtime;
|
||||
use Appwrite\Utopia\Response\Model\Session;
|
||||
use Appwrite\Utopia\Response\Model\Specification;
|
||||
use Appwrite\Utopia\Response\Model\Subscriber;
|
||||
use Appwrite\Utopia\Response\Model\Target;
|
||||
use Appwrite\Utopia\Response\Model\Team;
|
||||
|
@ -256,6 +257,8 @@ class Response extends SwooleResponse
|
|||
public const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
|
||||
public const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
|
||||
public const MODEL_HEADERS = 'headers';
|
||||
public const MODEL_SPECIFICATION = 'specification';
|
||||
public const MODEL_SPECIFICATION_LIST = 'specificationList';
|
||||
public const MODEL_TEMPLATE_FUNCTION = 'templateFunction';
|
||||
public const MODEL_TEMPLATE_FUNCTION_LIST = 'templateFunctionList';
|
||||
public const MODEL_TEMPLATE_RUNTIME = 'templateRuntime';
|
||||
|
@ -379,6 +382,7 @@ class Response extends SwooleResponse
|
|||
->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET))
|
||||
->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION))
|
||||
->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT))
|
||||
->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION))
|
||||
->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT))
|
||||
// Entities
|
||||
->setModel(new Database())
|
||||
|
@ -461,6 +465,7 @@ class Response extends SwooleResponse
|
|||
->setModel(new UsageFunction())
|
||||
->setModel(new UsageProject())
|
||||
->setModel(new Headers())
|
||||
->setModel(new Specification())
|
||||
->setModel(new Rule())
|
||||
->setModel(new TemplateSMS())
|
||||
->setModel(new TemplateEmail())
|
||||
|
|
|
@ -16,6 +16,7 @@ class V18 extends Filter
|
|||
Response::MODEL_FUNCTION => $this->parseFunction($content),
|
||||
Response::MODEL_EXECUTION => $this->parseExecution($content),
|
||||
Response::MODEL_PROJECT => $this->parseProject($content),
|
||||
Response::MODEL_RUNTIME => $this->parseRuntime($content),
|
||||
default => $parsedResponse,
|
||||
};
|
||||
|
||||
|
@ -37,6 +38,7 @@ class V18 extends Filter
|
|||
protected function parseFunction(array $content)
|
||||
{
|
||||
unset($content['scopes']);
|
||||
unset($content['specification']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
@ -46,4 +48,10 @@ class V18 extends Filter
|
|||
unset($content['authSessionAlerts']);
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function parseRuntime(array $content)
|
||||
{
|
||||
unset($content['key']);
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,6 +152,12 @@ class Func extends Model
|
|||
'default' => false,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('specification', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Machine specification for builds and executions.',
|
||||
'default' => APP_FUNCTION_SPECIFICATION_DEFAULT,
|
||||
'example' => APP_FUNCTION_SPECIFICATION_DEFAULT,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
|
58
src/Appwrite/Utopia/Response/Model/Specification.php
Normal file
58
src/Appwrite/Utopia/Response/Model/Specification.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class Specification extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this->addRule('memory', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Memory size in MB.',
|
||||
'default' => 0,
|
||||
'example' => 512
|
||||
]);
|
||||
$this->addRule('cpus', [
|
||||
'type' => self::TYPE_FLOAT,
|
||||
'description' => 'Number of CPUs.',
|
||||
'default' => 0,
|
||||
'example' => 1
|
||||
]);
|
||||
$this->addRule('enabled', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Is size enabled.',
|
||||
'default' => false,
|
||||
'example' => true
|
||||
]);
|
||||
$this->addRule('slug', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Size slug.',
|
||||
'default' => '',
|
||||
'example' => APP_FUNCTION_SPECIFICATION_DEFAULT
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Specification';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_SPECIFICATION;
|
||||
}
|
||||
}
|
|
@ -119,7 +119,6 @@ class UsageFunction extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
|
||||
->addRule('executionsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of function executions compute time per period.',
|
||||
|
|
|
@ -132,7 +132,6 @@ class UsageFunctions extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
|
||||
->addRule('executionsTime', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of functions execution compute time per period.',
|
||||
|
|
|
@ -25,10 +25,6 @@ class Executor
|
|||
|
||||
protected array $headers;
|
||||
|
||||
protected int $cpus;
|
||||
|
||||
protected int $memory;
|
||||
|
||||
public function __construct(string $endpoint)
|
||||
{
|
||||
if (!filter_var($endpoint, FILTER_VALIDATE_URL)) {
|
||||
|
@ -36,8 +32,6 @@ class Executor
|
|||
}
|
||||
|
||||
$this->endpoint = $endpoint;
|
||||
$this->cpus = \intval(System::getEnv('_APP_FUNCTIONS_CPUS', '1'));
|
||||
$this->memory = \intval(System::getEnv('_APP_FUNCTIONS_MEMORY', '512'));
|
||||
$this->headers = [
|
||||
'content-type' => 'application/json',
|
||||
'authorization' => 'Bearer ' . System::getEnv('_APP_EXECUTOR_SECRET', ''),
|
||||
|
@ -66,6 +60,8 @@ class Executor
|
|||
string $source,
|
||||
string $image,
|
||||
string $version,
|
||||
float $cpus,
|
||||
int $memory,
|
||||
bool $remove = false,
|
||||
string $entrypoint = '',
|
||||
string $destination = '',
|
||||
|
@ -90,8 +86,8 @@ class Executor
|
|||
'variables' => $variables,
|
||||
'remove' => $remove,
|
||||
'command' => $command,
|
||||
'cpus' => $this->cpus,
|
||||
'memory' => $this->memory,
|
||||
'cpus' => $cpus,
|
||||
'memory' => $memory,
|
||||
'version' => $version,
|
||||
'timeout' => $timeout,
|
||||
];
|
||||
|
@ -185,6 +181,8 @@ class Executor
|
|||
string $path,
|
||||
string $method,
|
||||
array $headers,
|
||||
float $cpus,
|
||||
int $memory,
|
||||
string $runtimeEntrypoint = null,
|
||||
bool $logging,
|
||||
int $requestTimeout = null
|
||||
|
@ -211,8 +209,8 @@ class Executor
|
|||
'image' => $image,
|
||||
'source' => $source,
|
||||
'entrypoint' => $entrypoint,
|
||||
'cpus' => $this->cpus,
|
||||
'memory' => $this->memory,
|
||||
'cpus' => $cpus,
|
||||
'memory' => $memory,
|
||||
'version' => $version,
|
||||
'runtimeEntrypoint' => $runtimeEntrypoint,
|
||||
'logging' => $logging,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\General;
|
||||
|
||||
use Appwrite\Functions\Specification;
|
||||
use Appwrite\Tests\Retry;
|
||||
use CURLFile;
|
||||
use DateTime;
|
||||
|
@ -617,6 +618,7 @@ class UsageTest extends Scope
|
|||
],
|
||||
'schedule' => '0 0 1 1 *',
|
||||
'timeout' => 10,
|
||||
'specification' => Specification::S_8VCPU_8GB
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Functions;
|
||||
|
||||
use Appwrite\Functions\Specification;
|
||||
use Appwrite\Tests\Retry;
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
|
@ -1397,6 +1398,99 @@ class FunctionsCustomServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetExecution
|
||||
*/
|
||||
#[Retry(count: 2)]
|
||||
public function testUpdateSpecs($data): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response1 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Test1',
|
||||
'events' => [
|
||||
'users.*.update.name',
|
||||
'users.*.update.email',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'specification' => Specification::S_1VCPU_1GB,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response1['headers']['status-code']);
|
||||
$this->assertNotEmpty($response1['body']['$id']);
|
||||
$this->assertEquals(Specification::S_1VCPU_1GB, $response1['body']['specification']);
|
||||
|
||||
// Test Execution
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$output = json_decode($execution['body']['responseBody'], true);
|
||||
|
||||
$this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']);
|
||||
$this->assertEquals(1024, $output['APPWRITE_FUNCTION_MEMORY']);
|
||||
|
||||
$response2 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Test1',
|
||||
'events' => [
|
||||
'users.*.update.name',
|
||||
'users.*.update.email',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'specification' => Specification::S_1VCPU_512MB,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response2['headers']['status-code']);
|
||||
$this->assertNotEmpty($response2['body']['$id']);
|
||||
$this->assertEquals(Specification::S_1VCPU_512MB, $response2['body']['specification']);
|
||||
|
||||
// Test Execution
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$output = json_decode($execution['body']['responseBody'], true);
|
||||
|
||||
$this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']);
|
||||
$this->assertEquals(512, $output['APPWRITE_FUNCTION_MEMORY']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response3 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Test1',
|
||||
'events' => [
|
||||
'users.*.update.name',
|
||||
'users.*.update.email',
|
||||
],
|
||||
'timeout' => 15,
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'specification' => 's-2vcpu-512mb', // Invalid specification
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response3['headers']['status-code']);
|
||||
$this->assertStringStartsWith('Invalid `specification` param: Specification must be one of:', $response3['body']['message']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetExecution
|
||||
*/
|
||||
|
|
|
@ -9,6 +9,8 @@ return function ($context) {
|
|||
'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
|
||||
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
|
||||
'APPWRITE_FUNCTION_CPUS' => \getenv('APPWRITE_FUNCTION_CPUS') ?: '',
|
||||
'APPWRITE_FUNCTION_MEMORY' => \getenv('APPWRITE_FUNCTION_MEMORY') ?: '',
|
||||
'UNICODE_TEST' => "êä"
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,8 @@ return function ($context) {
|
|||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '',
|
||||
'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '',
|
||||
'UNICODE_TEST' => "êä",
|
||||
'GLOBAL_VARIABLE' => \getenv('GLOBAL_VARIABLE') ?: ''
|
||||
'GLOBAL_VARIABLE' => \getenv('GLOBAL_VARIABLE') ?: '',
|
||||
'APPWRITE_FUNCTION_CPUS' => \getenv('APPWRITE_FUNCTION_CPUS') ?: '',
|
||||
'APPWRITE_FUNCTION_MEMORY' => \getenv('APPWRITE_FUNCTION_MEMORY') ?: '',
|
||||
], \intval($statusCode));
|
||||
};
|
||||
|
|
|
@ -147,4 +147,29 @@ class V18Test extends TestCase
|
|||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public function runtimeProvider(): array
|
||||
{
|
||||
return [
|
||||
'remove key' => [
|
||||
[
|
||||
'key' => 'example_key',
|
||||
],
|
||||
[
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider runtimeProvider
|
||||
*/
|
||||
public function testRuntime(array $content, array $expected): void
|
||||
{
|
||||
$model = Response::MODEL_RUNTIME;
|
||||
|
||||
$result = $this->filter->parse($content, $model);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue