diff --git a/app/config/collections.php b/app/config/collections.php index 9bf6cd454..828916526 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -2255,7 +2255,7 @@ $collections = [ 'filters' => [], ], [ - '$id' => 'exitCode', + '$id' => 'statusCode', 'type' => Database::VAR_INTEGER, 'format' => '', 'size' => 0, diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 9f442496f..005abd78c 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -858,7 +858,7 @@ App::post('/v1/functions/:functionId/executions') 'tagId' => $tag->getId(), 'trigger' => 'http', // http / schedule / event 'status' => 'waiting', // waiting / processing / completed / failed - 'exitCode' => 0, + 'statusCode' => 0, 'stdout' => '', 'stderr' => '', 'time' => 0.0, diff --git a/app/executor.php b/app/executor.php index c403bd83e..e962ef509 100644 --- a/app/executor.php +++ b/app/executor.php @@ -665,6 +665,10 @@ function createRuntimeServer(string $functionId, string $projectId, string $tagI return $database->getDocument('tags', $tagId); }); + if ($tag->getAttribute('buildId') === null) { + throw new Exception('Tag has no buildId'); + } + // Grab Build Document $build = Authorization::skip(function () use ($database, $tag) { return $database->getDocument('builds', $tag->getAttribute('buildId')); @@ -856,7 +860,7 @@ function execute(string $trigger, string $projectId, string $executionId, string 'tagId' => $tag->getId(), 'trigger' => $trigger, // http / schedule / event 'status' => 'processing', // waiting / processing / completed / failed - 'exitCode' => 0, + 'statusCode' => 0, 'stdout' => '', 'stderr' => '', 'time' => 0.0, @@ -873,7 +877,7 @@ function execute(string $trigger, string $projectId, string $executionId, string Console::error('Execution Failed. Reason: Code was still being built.'); $execution->setAttribute('status', 'failed') - ->setAttribute('exitCode', 1) + ->setAttribute('statusCode', 500) ->setAttribute('stderr', 'Tag is still being built.') ->setAttribute('time', 0); @@ -951,7 +955,7 @@ function execute(string $trigger, string $projectId, string $executionId, string } catch (Exception $e) { Console::error('Something went wrong building the code. ' . $e->getMessage()); $execution->setAttribute('status', 'failed') - ->setAttribute('exitCode', 1) + ->setAttribute('statusCode', 500) ->setAttribute('stderr', \utf8_encode(\mb_substr($e->getMessage(), -4000))) // log last 4000 chars output ->setAttribute('time', 0); @@ -972,7 +976,7 @@ function execute(string $trigger, string $projectId, string $executionId, string Console::error('Something went wrong building the runtime server. ' . $e->getMessage()); $execution->setAttribute('status', 'failed') - ->setAttribute('exitCode', 1) + ->setAttribute('statusCode', 500) ->setAttribute('stderr', \utf8_encode(\mb_substr($e->getMessage(), -4000))) // log last 4000 chars output ->setAttribute('time', 0); @@ -1012,8 +1016,7 @@ function execute(string $trigger, string $projectId, string $executionId, string $executionStart = \microtime(true); - $exitCode = 0; - $statusCode = 200; + $statusCode = 0; $errNo = -1; $attempts = 0; @@ -1067,12 +1070,12 @@ function execute(string $trigger, string $projectId, string $executionId, string if ($attempts >= 5) { $stderr = 'Failed to connect to executor runtime after 5 attempts.'; - $exitCode = 124; + $statusCode = 124; } // If timeout error if ($errNo == CURLE_OPERATION_TIMEDOUT || $errNo == 110) { - $exitCode = 124; + $statusCode = 124; } // 110 is the Swoole error code for timeout, see: https://www.swoole.co.uk/docs/swoole-error-code @@ -1081,29 +1084,43 @@ function execute(string $trigger, string $projectId, string $executionId, string throw new Exception('Curl error: ' . $error, 500); } + $executionData = []; + if (!empty($executorResponse)) { $executionData = json_decode($executorResponse, true); } if (isset($executionData['code'])) { - $exitCode = $executionData['code']; + $statusCode = $executionData['code']; } - if ($exitCode === 500) { - $stderr = $executionData['message']; - } else if ($exitCode === 0) { + if ($statusCode === 500) { + if (isset($executionData['message'])) { + $stderr = $executionData['message']; + } else { + $stderr = 'Internal Runtime error'; + } + } else if ($statusCode === 124) { + $stderr = 'Execution timed out.'; + } else if ($statusCode === 0) { + $stderr = 'Execution failed.'; + } else if ($statusCode >= 200 && $statusCode < 300) { $stdout = $executorResponse; + } else { + $stderr = 'Execution failed.'; } $executionEnd = \microtime(true); $executionTime = ($executionEnd - $executionStart); - $functionStatus = ($exitCode === 0) ? 'completed' : 'failed'; + $functionStatus = ($statusCode >= 200 && $statusCode < 300) ? 'completed' : 'failed'; + + var_dump($statusCode); Console::info('Function executed in ' . ($executionEnd - $executionStart) . ' seconds, status: ' . $functionStatus); $execution->setAttribute('tagId', $tag->getId()) ->setAttribute('status', $functionStatus) - ->setAttribute('exitCode', $exitCode) + ->setAttribute('statusCode', $statusCode) ->setAttribute('stdout', \utf8_encode(\mb_substr($stdout, -8000))) ->setAttribute('stderr', \utf8_encode(\mb_substr($stderr, -8000))) ->setAttribute('time', $executionTime); @@ -1341,7 +1358,7 @@ function handleShutdown() // Mark all processing executions as failed foreach ($executions as $execution) { $execution->setAttribute('status', 'failed') - ->setAttribute('exitCode', 1) + ->setAttribute('statusCode', 1) ->setAttribute('stderr', 'Appwrite was shutdown during execution'); Authorization::skip(function () use ($database, $execution) { diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml index 07e5a28c3..9e6fd4c44 100644 --- a/app/views/console/functions/function.phtml +++ b/app/views/console/functions/function.phtml @@ -371,7 +371,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true); - + diff --git a/src/Appwrite/Utopia/Response/Model/Execution.php b/src/Appwrite/Utopia/Response/Model/Execution.php index 34c939b6c..ae97db5ef 100644 --- a/src/Appwrite/Utopia/Response/Model/Execution.php +++ b/src/Appwrite/Utopia/Response/Model/Execution.php @@ -47,9 +47,9 @@ class Execution extends Model 'default' => '', 'example' => 'processing', ]) - ->addRule('exitCode', [ + ->addRule('statusCode', [ 'type' => self::TYPE_INTEGER, - 'description' => 'The script exit code.', + 'description' => 'The script status code.', 'default' => 0, 'example' => 0, ]) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 7568c159f..34ef60bc8 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -86,6 +86,9 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(201, $tag['headers']['status-code']); + // Wait for tag to be built. + sleep(5); + $function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$function['body']['$id'].'/tag', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -96,9 +99,6 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(200, $function['headers']['status-code']); - // Wait for tag to be built. - sleep(5); - $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/executions', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -169,6 +169,9 @@ class FunctionsCustomClientTest extends Scope $tagId = $tag['body']['$id'] ?? ''; + // Wait for tag to be built. + sleep(5); + $this->assertEquals(201, $tag['headers']['status-code']); $function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', [ @@ -181,9 +184,6 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(200, $function['headers']['status-code']); - // Wait for tag to be built. - sleep(5); - $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, @@ -319,6 +319,9 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(201, $tag['headers']['status-code']); + // Wait for tag to be built. + sleep(5); + $function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, @@ -329,9 +332,6 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(200, $function['headers']['status-code']); - // Wait for tag to be built. - sleep(5); - $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 154c04b4d..cf21bc05e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -292,6 +292,9 @@ class FunctionsCustomServerTest extends Scope $this->assertIsInt($tag['body']['dateCreated']); $this->assertEquals('index.php', $tag['body']['entrypoint']); // $this->assertGreaterThan(10000, $tag['body']['size']); + + // Wait for tag to build. + sleep(5); /** * Test for FAILURE @@ -320,9 +323,6 @@ class FunctionsCustomServerTest extends Scope $this->assertIsInt($response['body']['dateCreated']); $this->assertIsInt($response['body']['dateUpdated']); $this->assertEquals($data['tagId'], $response['body']['tag']); - - // Wait for tag to be built. - sleep(5); /** * Test for FAILURE @@ -445,7 +445,7 @@ class FunctionsCustomServerTest extends Scope $this->assertIsInt($execution['body']['dateCreated']); $this->assertEquals($data['functionId'], $execution['body']['functionId']); $this->assertEquals('waiting', $execution['body']['status']); - $this->assertEquals(0, $execution['body']['exitCode']); + $this->assertEquals(0, $execution['body']['statusCode']); $this->assertEquals('', $execution['body']['stdout']); $this->assertEquals('', $execution['body']['stderr']); $this->assertEquals(0, $execution['body']['time']); @@ -462,7 +462,7 @@ class FunctionsCustomServerTest extends Scope $this->assertIsInt($execution['body']['dateCreated']); $this->assertEquals($data['functionId'], $execution['body']['functionId']); $this->assertEquals('completed', $execution['body']['status']); - $this->assertEquals(0, $execution['body']['exitCode']); + $this->assertEquals(200, $execution['body']['statusCode']); $this->assertStringContainsString($execution['body']['functionId'], $execution['body']['stdout']); $this->assertStringContainsString($data['tagId'], $execution['body']['stdout']); $this->assertStringContainsString('Test1', $execution['body']['stdout']); @@ -685,6 +685,9 @@ class FunctionsCustomServerTest extends Scope $tagId = $tag['body']['$id'] ?? ''; $this->assertEquals(201, $tag['headers']['status-code']); + // Allow build step to run + sleep(5); + $tag = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -694,9 +697,6 @@ class FunctionsCustomServerTest extends Scope ]); $this->assertEquals(200, $tag['headers']['status-code']); - - // Allow build step to run - sleep(5); $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json', @@ -723,11 +723,11 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); $this->assertEquals($executions['body']['executions'][0]['status'], 'failed'); - $this->assertEquals($executions['body']['executions'][0]['exitCode'], 124); + $this->assertEquals($executions['body']['executions'][0]['statusCode'], 124); $this->assertGreaterThan(2, $executions['body']['executions'][0]['time']); $this->assertLessThan(3, $executions['body']['executions'][0]['time']); $this->assertEquals($executions['body']['executions'][0]['stdout'], ''); - $this->assertEquals($executions['body']['executions'][0]['stderr'], ''); + $this->assertEquals($executions['body']['executions'][0]['stderr'], 'Execution timed out.'); } /** @@ -768,6 +768,9 @@ class FunctionsCustomServerTest extends Scope $tagId = $tag['body']['$id'] ?? ''; $this->assertEquals(201, $tag['headers']['status-code']); + // Allow build step to run + sleep(5); + $tag = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/tag', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -776,9 +779,6 @@ class FunctionsCustomServerTest extends Scope ]); $this->assertEquals(200, $tag['headers']['status-code']); - - // Allow build step to run - sleep(5); $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json',