mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge pull request #6142 from appwrite/fix-v2-functions
Fix: v2 functions
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* List of Appwrite Cloud Functions supported runtimes
|
||||
*/
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
||||
$runtimes = new Runtimes('v2');
|
||||
|
||||
$allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES'));
|
||||
|
||||
$runtimes = $runtimes->getAll(true, $allowList);
|
||||
|
||||
return $runtimes;
|
||||
@@ -1523,7 +1523,8 @@ App::post('/v1/functions/:functionId/executions')
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$runtimes = Config::getParam('runtimes', []);
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
|
||||
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
|
||||
|
||||
@@ -1684,7 +1685,9 @@ App::post('/v1/functions/:functionId/executions')
|
||||
/** Execute function */
|
||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$command = $runtime['startCommand'];
|
||||
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"';
|
||||
$executionResponse = $executor->createExecution(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
@@ -1694,11 +1697,11 @@ App::post('/v1/functions/:functionId/executions')
|
||||
image: $runtime['image'],
|
||||
source: $build->getAttribute('path', ''),
|
||||
entrypoint: $deployment->getAttribute('entrypoint', ''),
|
||||
version: $function->getAttribute('version'),
|
||||
version: $version,
|
||||
path: $path,
|
||||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'
|
||||
runtimeEntrypoint: $command
|
||||
);
|
||||
|
||||
$headersFiltered = [];
|
||||
|
||||
@@ -750,7 +750,7 @@ App::put('/v1/users/:userId/labels')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), 5), 'Array of user labels. Replaces the previous labels. Maximum of 5 labels are allowed, each up to 36 alphanumeric characters long.')
|
||||
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of user labels. Replaces the previous labels. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' labels are allowed, each up to 36 alphanumeric characters long.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
|
||||
@@ -8,12 +8,14 @@ use Utopia\Validator\Host;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Storage\Validator\File;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
App::get('/v1/mock/tests/foo')
|
||||
@@ -605,6 +607,32 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
]);
|
||||
});
|
||||
|
||||
App::patch('/v1/mock/functions-v2')
|
||||
->desc('Update Function Version to V2 (outdated code syntax)')
|
||||
->groups(['mock', 'api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('docs', false)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, Response $response, Database $dbForProject) {
|
||||
$isDevelopment = App::getEnv('_APP_ENV', 'development') === 'development';
|
||||
|
||||
if (!$isDevelopment) {
|
||||
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('version', 'v2'));
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::shutdown()
|
||||
->groups(['mock'])
|
||||
->inject('utopia')
|
||||
|
||||
@@ -237,6 +237,7 @@ Config::load('providers', __DIR__ . '/config/providers.php');
|
||||
Config::load('platforms', __DIR__ . '/config/platforms.php');
|
||||
Config::load('collections', __DIR__ . '/config/collections.php');
|
||||
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
|
||||
Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php');
|
||||
Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__ . '/config/services.php'); // List of services
|
||||
|
||||
@@ -597,7 +597,7 @@ services:
|
||||
hostname: appwrite-executor
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.3.5
|
||||
image: openruntimes/executor:0.4.0
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
|
||||
@@ -90,7 +90,8 @@ class BuildsV1 extends Worker
|
||||
throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500);
|
||||
}
|
||||
|
||||
$runtimes = Config::getParam('runtimes', []);
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$key = $function->getAttribute('runtime');
|
||||
$runtime = isset($runtimes[$key]) ? $runtimes[$key] : null;
|
||||
if (\is_null($runtime)) {
|
||||
@@ -358,17 +359,20 @@ class BuildsV1 extends Worker
|
||||
Co::join([
|
||||
Co\go(function () use (&$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$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 "' . $command . '"';
|
||||
|
||||
$response = $this->executor->createRuntime(
|
||||
deploymentId: $deployment->getId(),
|
||||
projectId: $project->getId(),
|
||||
source: $source,
|
||||
image: $runtime['image'],
|
||||
version: $function->getAttribute('version'),
|
||||
version: $version,
|
||||
remove: true,
|
||||
entrypoint: $deployment->getAttribute('entrypoint'),
|
||||
destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}",
|
||||
variables: $vars,
|
||||
command: 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'
|
||||
command: $command
|
||||
);
|
||||
} catch (Exception $error) {
|
||||
$err = $error;
|
||||
|
||||
@@ -74,7 +74,8 @@ Server::setResource('execute', function () {
|
||||
}
|
||||
|
||||
/** Check if runtime is supported */
|
||||
$runtimes = Config::getParam('runtimes', []);
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
|
||||
if (!\array_key_exists($function->getAttribute('runtime'), $runtimes)) {
|
||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||
@@ -171,8 +172,10 @@ Server::setResource('execute', function () {
|
||||
|
||||
/** Execute function */
|
||||
try {
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$command = $runtime['startCommand'];
|
||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
||||
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"';
|
||||
$executionResponse = $executor->createExecution(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deploymentId,
|
||||
@@ -182,11 +185,11 @@ Server::setResource('execute', function () {
|
||||
image: $runtime['image'],
|
||||
source: $build->getAttribute('path', ''),
|
||||
entrypoint: $deployment->getAttribute('entrypoint', ''),
|
||||
version: $function->getAttribute('version'),
|
||||
version: $version,
|
||||
path: $path,
|
||||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'
|
||||
runtimeEntrypoint: $command
|
||||
);
|
||||
|
||||
$status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed';
|
||||
@@ -381,9 +384,9 @@ $server->job()
|
||||
data: $data,
|
||||
user: $user,
|
||||
jwt: $jwt,
|
||||
path: $payload['path'],
|
||||
method: $payload['method'],
|
||||
headers: $payload['headers'],
|
||||
path: $payload['path'] ?? '',
|
||||
method: $payload['method'] ?? 'POST',
|
||||
headers: $payload['headers'] ?? [],
|
||||
statsd: $statsd,
|
||||
);
|
||||
break;
|
||||
@@ -401,9 +404,9 @@ $server->job()
|
||||
data: null,
|
||||
user: null,
|
||||
jwt: null,
|
||||
path: $payload['path'],
|
||||
method: $payload['method'],
|
||||
headers: $payload['headers'],
|
||||
path: $payload['path'] ?? '/',
|
||||
method: $payload['method'] ?? 'POST',
|
||||
headers: $payload['headers'] ?? [],
|
||||
statsd: $statsd,
|
||||
);
|
||||
break;
|
||||
|
||||
+2
-2
@@ -693,7 +693,7 @@ services:
|
||||
hostname: appwrite-executor
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.3.5
|
||||
image: openruntimes/executor:0.4.0
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
@@ -742,7 +742,7 @@ services:
|
||||
hostname: proxy
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/proxy:0.3.0
|
||||
image: openruntimes/proxy:0.3.1
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
|
||||
@@ -975,7 +975,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
*/
|
||||
public function testCreateCustomExecution(string $folder, string $name, string $entrypoint, string $runtimeName, string $runtimeVersion)
|
||||
{
|
||||
$timeout = 2;
|
||||
$timeout = 5;
|
||||
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
|
||||
$this->packageCode($folder);
|
||||
|
||||
@@ -1094,6 +1094,104 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testv2Function()
|
||||
{
|
||||
$timeout = 5;
|
||||
$code = realpath(__DIR__ . '/../../../resources/functions') . "/php-v2/code.tar.gz";
|
||||
$this->packageCode('php-v2');
|
||||
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test PHP V2',
|
||||
'runtime' => 'php-8.0',
|
||||
'entrypoint' => 'index.php',
|
||||
'events' => [],
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
|
||||
$headers = [
|
||||
'content-type' => 'application/json',
|
||||
'origin' => 'http://localhost',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
];
|
||||
|
||||
$variable = $this->client->call(Client::METHOD_PATCH, '/mock/functions-v2', $headers, [
|
||||
'functionId' => $functionId
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $variable['headers']['status-code']);
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
|
||||
// Poll until deployment is built
|
||||
while (true) {
|
||||
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
if (
|
||||
$deployment['headers']['status-code'] >= 400
|
||||
|| \in_array($deployment['body']['status'], ['ready', 'failed'])
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
\sleep(1);
|
||||
}
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'body' => 'foobar',
|
||||
'async' => false
|
||||
]);
|
||||
|
||||
$output = json_decode($execution['body']['responseBody'], true);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEquals('completed', $execution['body']['status']);
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
$this->assertEquals(true, $output['v2Woks']);
|
||||
|
||||
// Cleanup : Delete function
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], []);
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testGetRuntimes()
|
||||
{
|
||||
$runtimes = $this->client->call(Client::METHOD_GET, '/functions/runtimes', array_merge([
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return function ($req, $res) {
|
||||
$res->json([
|
||||
'v2Woks' => true
|
||||
]);
|
||||
};
|
||||
Reference in New Issue
Block a user