diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bc27bff..ecaf9ba 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,5 +29,4 @@ jobs: - name: Run Tests run: | - docker run --rm -v $PWD:/app --network openruntimes-runtimes -w /app phpswoole/swoole:5.1.2-php8.3-alpine sh -c \ - "composer install --profile --ignore-platform-reqs && composer test" \ No newline at end of file + docker run --rm -v $PWD:/app -v /var/run/docker.sock:/var/run/docker.sock --network openruntimes-runtimes -w /app phpswoole/swoole:5.1.2-php8.3-alpine sh -c "apk update && apk add docker-cli && composer install --profile --ignore-platform-reqs && composer test" \ No newline at end of file diff --git a/app/http.php b/app/http.php index b558f5b..1f2871e 100644 --- a/app/http.php +++ b/app/http.php @@ -411,11 +411,12 @@ function removeAllRuntimes(Table $activeRuntimes, Orchestration $orchestration): ->param('cpus', 1, new Integer(), 'Container CPU.', true) ->param('memory', 512, new Integer(), 'Comtainer RAM memory.', true) ->param('version', 'v4', new WhiteList(['v2', 'v4']), 'Runtime Open Runtime version.', true) + ->param('restartPolicy', DockerAPI::RESTART_NO, new WhiteList([DockerAPI::RESTART_NO, DockerAPI::RESTART_ALWAYS, DockerAPI::RESTART_ON_FAILURE, DockerAPI::RESTART_UNLESS_STOPPED], true), 'Define restart policy for the runtime once an exit code is returned. Default value is "no". Possible values are "no", "always", "on-failure", "unless-stopped".', true) ->inject('orchestration') ->inject('activeRuntimes') ->inject('response') ->inject('log') - ->action(function (string $runtimeId, string $image, string $entrypoint, string $source, string $destination, array $variables, string $runtimeEntrypoint, string $command, int $timeout, bool $remove, int $cpus, int $memory, string $version, Orchestration $orchestration, Table $activeRuntimes, Response $response, Log $log) { + ->action(function (string $runtimeId, string $image, string $entrypoint, string $source, string $destination, array $variables, string $runtimeEntrypoint, string $command, int $timeout, bool $remove, int $cpus, int $memory, string $version, string $restartPolicy, Orchestration $orchestration, Table $activeRuntimes, Response $response, Log $log) { $runtimeName = System::getHostname() . '-' . $runtimeId; $runtimeHostname = \uniqid(); @@ -536,7 +537,8 @@ function removeAllRuntimes(Table $activeRuntimes, Orchestration $orchestration): ], volumes: $volumes, network: \strval($openruntimes_network) ?: 'executor_runtimes', - workdir: $workdir + workdir: $workdir, + restart: $restartPolicy ); if (empty($containerId)) { @@ -748,12 +750,13 @@ function removeAllRuntimes(Table $activeRuntimes, Orchestration $orchestration): ->param('version', 'v4', new WhiteList(['v2', 'v4']), 'Runtime Open Runtime version.', true) ->param('runtimeEntrypoint', '', new Text(1024, 0), 'Commands to run when creating a container. Maximum of 100 commands are allowed, each 1024 characters long.', true) ->param('logging', true, new Boolean(true), 'Whether executions will be logged.', true) + ->param('restartPolicy', DockerAPI::RESTART_NO, new WhiteList([DockerAPI::RESTART_NO, DockerAPI::RESTART_ALWAYS, DockerAPI::RESTART_ON_FAILURE, DockerAPI::RESTART_UNLESS_STOPPED], true), 'Define restart policy once exit code is returned by command. Default value is "no". Possible values are "no", "always", "on-failure", "unless-stopped".', true) ->inject('activeRuntimes') ->inject('response') ->inject('request') ->inject('log') ->action( - function (string $runtimeId, ?string $payload, string $path, string $method, mixed $headers, int $timeout, string $image, string $source, string $entrypoint, mixed $variables, int $cpus, int $memory, string $version, string $runtimeEntrypoint, bool $logging, Table $activeRuntimes, Response $response, Request $request, Log $log) { + function (string $runtimeId, ?string $payload, string $path, string $method, mixed $headers, int $timeout, string $image, string $source, string $entrypoint, mixed $variables, int $cpus, int $memory, string $version, string $runtimeEntrypoint, bool $logging, string $restartPolicy, Table $activeRuntimes, Response $response, Request $request, Log $log) { if (empty($payload)) { $payload = ''; } @@ -821,7 +824,7 @@ function (string $runtimeId, ?string $payload, string $path, string $method, mix } // Prepare request to executor - $sendCreateRuntimeRequest = function () use ($runtimeId, $image, $source, $entrypoint, $variables, $cpus, $memory, $version, $runtimeEntrypoint) { + $sendCreateRuntimeRequest = function () use ($runtimeId, $image, $source, $entrypoint, $variables, $cpus, $memory, $version, $restartPolicy, $runtimeEntrypoint) { $statusCode = 0; $errNo = -1; $executorResponse = ''; @@ -837,6 +840,7 @@ function (string $runtimeId, ?string $payload, string $path, string $method, mix 'cpus' => $cpus, 'memory' => $memory, 'version' => $version, + 'restartPolicy' => $restartPolicy, 'runtimeEntrypoint' => $runtimeEntrypoint ]); diff --git a/composer.json b/composer.json index fdff90d..4b24a21 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", "utopia-php/system": "0.8.*", - "utopia-php/orchestration": "0.12.*", + "utopia-php/orchestration": "0.13.*", "appwrite/php-runtimes": "0.14.*" }, "require-dev": { diff --git a/composer.lock b/composer.lock index f9b3741..139e946 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f0dc014a994fe3fe015f6dda9b453dac", + "content-hash": "dddd09b7a6871b51edc0ebbaeee1bb1c", "packages": [ { "name": "appwrite/php-runtimes", @@ -262,16 +262,16 @@ }, { "name": "utopia-php/orchestration", - "version": "0.12.0", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "cae1ae4cb697c177446df9fcd059e4469cf983d0" + "reference": "30cd13abb8f79a2533fe78bc0da87d847bd55331" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/cae1ae4cb697c177446df9fcd059e4469cf983d0", - "reference": "cae1ae4cb697c177446df9fcd059e4469cf983d0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/30cd13abb8f79a2533fe78bc0da87d847bd55331", + "reference": "30cd13abb8f79a2533fe78bc0da87d847bd55331", "shasum": "" }, "require": { @@ -306,9 +306,9 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.12.0" + "source": "https://github.com/utopia-php/orchestration/tree/0.13.0" }, - "time": "2024-07-16T13:27:47+00:00" + "time": "2024-07-29T14:31:57+00:00" }, { "name": "utopia-php/preloader", diff --git a/tests/ExecutorTest.php b/tests/ExecutorTest.php index 47509d4..ef91f4c 100644 --- a/tests/ExecutorTest.php +++ b/tests/ExecutorTest.php @@ -365,6 +365,65 @@ public function testExecute(array $data): void $this->assertEquals(200, $response['headers']['status-code']); } + public function testRestartPolicy(): void + { + $output = ''; + Console::execute('cd /app/tests/resources/functions/php-exit && tar --exclude code.tar.gz -czf code.tar.gz .', '', $output); + + $command = 'php src/server.php'; + + /** Build runtime */ + + $params = [ + 'runtimeId' => 'test-build-restart-policy', + 'source' => '/storage/functions/php-exit/code.tar.gz', + 'destination' => '/storage/builds/test-restart-policy', + 'entrypoint' => 'index.php', + 'image' => 'openruntimes/php:v4-8.1', + 'command' => 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh ""' + ]; + + $response = $this->client->call(Client::METHOD_POST, '/runtimes', [], $params); + $this->assertEquals(201, $response['headers']['status-code']); + + $buildPath = $response['body']['path']; + + /** Execute function */ + + $response = $this->client->call(Client::METHOD_POST, '/runtimes/test-exec-restart-policy/executions', [], [ + 'source' => $buildPath, + 'entrypoint' => 'index.php', + 'image' => 'openruntimes/php:v4-8.1', + 'runtimeEntrypoint' => 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"', + 'restartPolicy' => 'always' + ]); + $this->assertEquals(500, $response['headers']['status-code']); + + \sleep(5); + + $response = $this->client->call(Client::METHOD_POST, '/runtimes/test-exec-restart-policy/executions', [], [ + 'source' => $buildPath, + 'entrypoint' => 'index.php', + 'image' => 'openruntimes/php:v4-8.1', + 'runtimeEntrypoint' => 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"' + ]); + $this->assertEquals(500, $response['headers']['status-code']); + + \sleep(5); + + /** Ensure restart policy */ + + $output = []; + \exec('docker logs executor-test-exec-restart-policy', $output); + $output = \implode("\n", $output); + $occurances = \substr_count($output, 'HTTP server successfully started!'); + $this->assertEquals(3, $occurances); + + /** Delete runtime */ + $response = $this->client->call(Client::METHOD_DELETE, '/runtimes/test-exec-restart-policy', [], []); + $this->assertEquals(200, $response['headers']['status-code']); + } + /** * * @return array @@ -692,7 +751,7 @@ public function testCustomRuntimes(string $folder, string $image, string $entryp 'destination' => '/storage/builds/test', 'entrypoint' => $entrypoint, 'image' => $image, - 'timeout' => 60, + 'timeout' => 120, 'command' => 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $buildCommand . '"', 'remove' => true ]; @@ -712,7 +771,7 @@ public function testCustomRuntimes(string $folder, string $image, string $entryp 'entrypoint' => $entrypoint, 'image' => $image, 'runtimeEntrypoint' => 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $startCommand . '"', - 'timeout' => 60, + 'timeout' => 120, 'variables' => [ 'TEST_VARIABLE' => 'Variable secret' ], diff --git a/tests/resources/functions/php-exit/index.php b/tests/resources/functions/php-exit/index.php new file mode 100644 index 0000000..1ece812 --- /dev/null +++ b/tests/resources/functions/php-exit/index.php @@ -0,0 +1,6 @@ +res->send('OK'); +};