From 029e1dc6f65293a4da5bfcbe742233c2f938e9f0 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 23 Jan 2022 19:21:23 +0200 Subject: [PATCH] Hiding port 8080 --- app/controllers/api/functions.php | 10 +- app/executor.php | 552 +++++++++++++++--------------- app/workers/functions.php | 4 +- docker-compose.yml | 6 +- 4 files changed, 285 insertions(+), 287 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index cb3e9c7a8..542ac872a 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -433,7 +433,7 @@ App::delete('/v1/functions/:functionId') // Request executor to delete tag containers $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor:8080/v1/cleanup/function"); + \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/cleanup/function"); \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'functionId' => $functionId @@ -590,7 +590,7 @@ App::post('/v1/functions/:functionId/tags') $function = $dbForProject->getDocument('functions', $functionId); $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor:8080/v1/tag"); + \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/tag"); \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'functionId' => $function->getId(), @@ -769,7 +769,7 @@ App::delete('/v1/functions/:functionId/tags/:tagId') // Request executor to delete tag containers $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor:8080/v1/cleanup/tag"); + \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/cleanup/tag"); \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'tagId' => $tagId @@ -927,7 +927,7 @@ App::post('/v1/functions/:functionId/executions') // Directly execute function. $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor:8080/v1/execute"); + \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/execute"); \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'trigger' => 'http', @@ -1162,7 +1162,7 @@ App::post('/v1/builds/:buildId') // Retry build $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor:8080/v1/build/{$buildId}"); + \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/build/{$buildId}"); \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); \curl_setopt($ch, CURLOPT_TIMEOUT, 900); diff --git a/app/executor.php b/app/executor.php index 470ceb951..fa138eee3 100644 --- a/app/executor.php +++ b/app/executor.php @@ -1,40 +1,40 @@ exists($container)) { // Create contianer if not ready + if (!$activeFunctions->exists($container)) { // Create container if not ready createRuntimeServer($functionId, $projectId, $tag->getId(), $database); } else if ($activeFunctions->get($container)['status'] === 'Down') { sleep(1); @@ -651,6 +652,257 @@ function execute(string $trigger, string $projectId, string $executionId, string ]; }; +function runBuildStage(string $buildId, string $projectID): Document +{ + global $runtimes; + global $orchestrationPool; + global $register; + + /** @var Orchestration $orchestration */ + $orchestration = $orchestrationPool->get(); + + $buildStdout = ''; + $buildStderr = ''; + + $db = $register->get('dbPool')->get(); + $redis = $register->get('redisPool')->get(); + $cache = new Cache(new RedisCache($redis)); + + $database = new Database(new MariaDB($db), $cache); + $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); + $database->setNamespace('_project_' . $projectID); + + // Check if build has already been run + $build = $database->getDocument('builds', $buildId); + + try { + // If we already have a built package ready there is no need to rebuild. + if ($build->getAttribute('status') === 'ready' && \file_exists($build->getAttribute('outputPath'))) { + return $build; + } + + // Update Tag Status + $build->setAttribute('status', 'building'); + + $database->updateDocument('builds', $build->getId(), $build); + + // Check if runtime is active + $runtime = $runtimes[$build->getAttribute('runtime', '')] ?? null; + + if (\is_null($runtime)) { + throw new Exception('Runtime "' . $build->getAttribute('runtime', '') . '" is not supported'); + } + + // Grab Tag Files + $tagPath = $build->getAttribute('source', ''); + $sourceType = $build->getAttribute('sourceType', ''); + + $device = Storage::getDevice('builds'); + + $tagPathTarget = '/tmp/project-' . $projectID . '/' . $build->getId() . '/code.tar.gz'; + $tagPathTargetDir = \pathinfo($tagPathTarget, PATHINFO_DIRNAME); + + $container = 'build-stage-' . $build->getId(); + + // Perform various checks + if (!\file_exists($tagPathTargetDir)) { + if (!\mkdir($tagPathTargetDir, 0777, true)) { + throw new Exception('Can\'t create directory ' . $tagPathTargetDir); + } + } + + if (!\file_exists($tagPathTarget)) { + if (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) { + if (!\copy($tagPath, $tagPathTarget)) { + throw new Exception('Can\'t create temporary code file ' . $tagPathTarget); + } + } else { + $buffer = $device->read($tagPath); + \file_put_contents($tagPathTarget, $buffer); + } + } + + if (!$device->exists($tagPath)) { + throw new Exception('Code is not readable: ' . $build->getAttribute('source', '')); + } + + $vars = $build->getAttribute('envVars', []); + + // Start tracking time + $buildStart = \microtime(true); + $buildTime = \time(); + + $orchestration + ->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 0)) + ->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', 256)) + ->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256)); + + foreach ($vars as &$value) { + $value = strval($value); + } + + if (!\file_exists('/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode')) { + if (!\mkdir('/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode', 0777, true)) { + throw new Exception('Can\'t create directory /tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode'); + } + }; + + // Launch build container + $id = $orchestration->run( + image: $runtime['base'], + name: $container, + vars: $vars, + workdir: '/usr/code', + labels: [ + 'appwrite-type' => 'function', + 'appwrite-created' => strval($buildTime), + 'appwrite-runtime' => $build->getAttribute('runtime', ''), + 'appwrite-project' => $projectID, + 'appwrite-build' => $build->getId(), + ], + command: [ + 'tail', + '-f', + '/dev/null' + ], + hostname: $container, + mountFolder: $tagPathTargetDir, + volumes: [ + '/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode' . ':/usr/builtCode:rw' + ] + ); + + if (empty($id)) { + throw new Exception('Failed to start build container'); + } + + // Extract user code into build container + $untarStdout = ''; + $untarStderr = ''; + + $untarSuccess = $orchestration->execute( + name: $container, + command: [ + 'sh', + '-c', + 'mkdir -p /usr/code && cp /tmp/code.tar.gz /usr/workspace/code.tar.gz && cd /usr/workspace/ && tar -zxf /usr/workspace/code.tar.gz -C /usr/code && rm /usr/workspace/code.tar.gz' + ], + stdout: $untarStdout, + stderr: $untarStderr, + timeout: 60 + ); + + if (!$untarSuccess) { + throw new Exception('Failed to extract tar: ' . $untarStderr); + } + + // Build Code / Install Dependencies + $buildSuccess = $orchestration->execute( + name: $container, + command: ['sh', '-c', 'cd /usr/local/src && ./build.sh'], + stdout: $buildStdout, + stderr: $buildStderr, + timeout: App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900) + ); + + if (!$buildSuccess) { + throw new Exception('Failed to build dependencies: ' . $buildStderr); + } + + // Repackage Code and Save. + $compressStdout = ''; + $compressStderr = ''; + + $builtCodePath = '/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode/code.tar.gz'; + + $compressSuccess = $orchestration->execute( + name: $container, + command: [ + 'tar', '-C', '/usr/code', '-czvf', '/usr/builtCode/code.tar.gz', './' + ], + stdout: $compressStdout, + stderr: $compressStderr, + timeout: 60 + ); + + if (!$compressSuccess) { + throw new Exception('Failed to compress built code: ' . $compressStderr); + } + + // Remove Container + $orchestration->remove($id, true); + + // Check if the build was successful by checking if file exists + if (!\file_exists($builtCodePath)) { + throw new Exception('Something went wrong during the build process.'); + } + + // Upload new code + $device = Storage::getDevice('builds'); + + $path = $device->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); + + if (!\file_exists(\dirname($path))) { // Checks if directory path to file exists + if (!@\mkdir(\dirname($path), 0777, true)) { + throw new Exception('Can\'t create directory: ' . \dirname($path)); + } + } + + if (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) { + if (!$device->move($builtCodePath, $path)) { + throw new Exception('Failed to upload built code upload to storage', 500); + } + } else { + if (!$device->upload($builtCodePath, $path)) { + throw new Exception('Failed to upload built code upload to storage', 500); + } + } + + if ($buildStdout == '') { + $buildStdout = 'Build Successful!'; + } + + $build + ->setAttribute('outputPath', $path) + ->setAttribute('status', 'ready') + ->setAttribute('stdout', \utf8_encode(\mb_substr($buildStdout, -4096))) + ->setAttribute('stderr', \utf8_encode(\mb_substr($buildStderr, -4096))) + ->setAttribute('buildTime', $buildTime); + + // Update build with built code attribute + $build = $database->updateDocument('builds', $buildId, $build); + + $buildEnd = \microtime(true); + + Console::info('Build Stage Ran in ' . ($buildEnd - $buildStart) . ' seconds'); + } catch (Exception $e) { + $build + ->setAttribute('status', 'failed') + ->setAttribute('stdout', \utf8_encode(\mb_substr($buildStdout, -4096))) + ->setAttribute('stderr', \utf8_encode(\mb_substr($e->getMessage(), -4096))); + + $build = $database->updateDocument('builds', $buildId, $build); + + // also remove the container if it exists + if (isset($id)) { + $orchestration->remove($id, true); + } + $orchestrationPool->put(null); + + $register->get('dbPool')->put($db); + $register->get('redisPool')->put($redis); + + throw new Exception('Build failed: ' . $e->getMessage()); + } finally { + $orchestrationPool->put($orchestration); + + $register->get('dbPool')->put($db); + $register->get('redisPool')->put($redis); + } + + return $build; +} + App::post('/v1/execute') // Define Route ->desc('Execute a function') ->param('trigger', '', new Text(1024)) @@ -682,7 +934,6 @@ App::post('/v1/execute') // Define Route } ); - // Cleanup Endpoints used internally by appwrite when a function or tag gets deleted to also clean up their containers App::post('/v1/cleanup/function') ->param('functionId', '', new UID()) @@ -961,260 +1212,9 @@ App::post('/v1/build/:buildId') // Start a Build } }); -function runBuildStage(string $buildId, string $projectID): Document -{ - global $runtimes; - global $orchestrationPool; - global $register; - - /** @var Orchestration $orchestration */ - $orchestration = $orchestrationPool->get(); - - $buildStdout = ''; - $buildStderr = ''; - - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - $cache = new Cache(new RedisCache($redis)); - - $database = new Database(new MariaDB($db), $cache); - $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); - $database->setNamespace('_project_' . $projectID); - - // Check if build has already been run - $build = $database->getDocument('builds', $buildId); - - try { - // If we already have a built package ready there is no need to rebuild. - if ($build->getAttribute('status') === 'ready' && \file_exists($build->getAttribute('outputPath'))) { - return $build; - } - - // Update Tag Status - $build->setAttribute('status', 'building'); - - $database->updateDocument('builds', $build->getId(), $build); - - // Check if runtime is active - $runtime = $runtimes[$build->getAttribute('runtime', '')] ?? null; - - if (\is_null($runtime)) { - throw new Exception('Runtime "' . $build->getAttribute('runtime', '') . '" is not supported'); - } - - // Grab Tag Files - $tagPath = $build->getAttribute('source', ''); - $sourceType = $build->getAttribute('sourceType', ''); - - $device = Storage::getDevice('builds'); - - $tagPathTarget = '/tmp/project-' . $projectID . '/' . $build->getId() . '/code.tar.gz'; - $tagPathTargetDir = \pathinfo($tagPathTarget, PATHINFO_DIRNAME); - - $container = 'build-stage-' . $build->getId(); - - // Perform various checks - if (!\file_exists($tagPathTargetDir)) { - if (!\mkdir($tagPathTargetDir, 0777, true)) { - throw new Exception('Can\'t create directory ' . $tagPathTargetDir); - } - } - - if (!\file_exists($tagPathTarget)) { - if (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) { - if (!\copy($tagPath, $tagPathTarget)) { - throw new Exception('Can\'t create temporary code file ' . $tagPathTarget); - } - } else { - $buffer = $device->read($tagPath); - \file_put_contents($tagPathTarget, $buffer); - } - } - - if (!$device->exists($tagPath)) { - throw new Exception('Code is not readable: ' . $build->getAttribute('source', '')); - } - - $vars = $build->getAttribute('envVars', []); - - // Start tracking time - $buildStart = \microtime(true); - $buildTime = \time(); - - $orchestration - ->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 0)) - ->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', 256)) - ->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256)); - - foreach ($vars as &$value) { - $value = strval($value); - } - - if (!\file_exists('/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode')) { - if (!\mkdir('/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode', 0777, true)) { - throw new Exception('Can\'t create directory /tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode'); - } - }; - - // Launch build container - $id = $orchestration->run( - image: $runtime['base'], - name: $container, - vars: $vars, - workdir: '/usr/code', - labels: [ - 'appwrite-type' => 'function', - 'appwrite-created' => strval($buildTime), - 'appwrite-runtime' => $build->getAttribute('runtime', ''), - 'appwrite-project' => $projectID, - 'appwrite-build' => $build->getId(), - ], - command: [ - 'tail', - '-f', - '/dev/null' - ], - hostname: $container, - mountFolder: $tagPathTargetDir, - volumes: [ - '/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode' . ':/usr/builtCode:rw' - ] - ); - - if (empty($id)) { - throw new Exception('Failed to start build container'); - } - - // Extract user code into build container - $untarStdout = ''; - $untarStderr = ''; - - $untarSuccess = $orchestration->execute( - name: $container, - command: [ - 'sh', - '-c', - 'mkdir -p /usr/code && cp /tmp/code.tar.gz /usr/workspace/code.tar.gz && cd /usr/workspace/ && tar -zxf /usr/workspace/code.tar.gz -C /usr/code && rm /usr/workspace/code.tar.gz' - ], - stdout: $untarStdout, - stderr: $untarStderr, - timeout: 60 - ); - - if (!$untarSuccess) { - throw new Exception('Failed to extract tar: ' . $untarStderr); - } - - // Build Code / Install Dependencies - $buildSuccess = $orchestration->execute( - name: $container, - command: ['sh', '-c', 'cd /usr/local/src && ./build.sh'], - stdout: $buildStdout, - stderr: $buildStderr, - timeout: App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900) - ); - - if (!$buildSuccess) { - throw new Exception('Failed to build dependencies: ' . $buildStderr); - } - - // Repackage Code and Save. - $compressStdout = ''; - $compressStderr = ''; - - $builtCodePath = '/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode/code.tar.gz'; - - $compressSuccess = $orchestration->execute( - name: $container, - command: [ - 'tar', '-C', '/usr/code', '-czvf', '/usr/builtCode/code.tar.gz', './' - ], - stdout: $compressStdout, - stderr: $compressStderr, - timeout: 60 - ); - - if (!$compressSuccess) { - throw new Exception('Failed to compress built code: ' . $compressStderr); - } - - // Remove Container - $orchestration->remove($id, true); - - // Check if the build was successful by checking if file exists - if (!\file_exists($builtCodePath)) { - throw new Exception('Something went wrong during the build process.'); - } - - // Upload new code - $device = Storage::getDevice('builds'); - - $path = $device->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); - - if (!\file_exists(\dirname($path))) { // Checks if directory path to file exists - if (!@\mkdir(\dirname($path), 0777, true)) { - throw new Exception('Can\'t create directory: ' . \dirname($path)); - } - } - - if (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) { - if (!$device->move($builtCodePath, $path)) { - throw new Exception('Failed to upload built code upload to storage', 500); - } - } else { - if (!$device->upload($builtCodePath, $path)) { - throw new Exception('Failed to upload built code upload to storage', 500); - } - } - - if ($buildStdout == '') { - $buildStdout = 'Build Successful!'; - } - - $build - ->setAttribute('outputPath', $path) - ->setAttribute('status', 'ready') - ->setAttribute('stdout', \utf8_encode(\mb_substr($buildStdout, -4096))) - ->setAttribute('stderr', \utf8_encode(\mb_substr($buildStderr, -4096))) - ->setAttribute('buildTime', $buildTime); - - // Update build with built code attribute - $build = $database->updateDocument('builds', $buildId, $build); - - $buildEnd = \microtime(true); - - Console::info('Build Stage Ran in ' . ($buildEnd - $buildStart) . ' seconds'); - } catch (Exception $e) { - $build - ->setAttribute('status', 'failed') - ->setAttribute('stdout', \utf8_encode(\mb_substr($buildStdout, -4096))) - ->setAttribute('stderr', \utf8_encode(\mb_substr($e->getMessage(), -4096))); - - $build = $database->updateDocument('builds', $buildId, $build); - - // also remove the container if it exists - if (isset($id)) { - $orchestration->remove($id, true); - } - $orchestrationPool->put(null); - - $register->get('dbPool')->put($db); - $register->get('redisPool')->put($redis); - - throw new Exception('Build failed: ' . $e->getMessage()); - } finally { - $orchestrationPool->put($orchestration); - - $register->get('dbPool')->put($db); - $register->get('redisPool')->put($redis); - } - - return $build; -} - App::setMode(App::MODE_TYPE_PRODUCTION); // Define Mode -$http = new Server("0.0.0.0", 8080); +$http = new Server("0.0.0.0", 80); function handleShutdown() { diff --git a/app/workers/functions.php b/app/workers/functions.php index b34ab45f4..a96a1fa7f 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -9,8 +9,8 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Orchestration\Orchestration; use Utopia\Orchestration\Adapter\DockerAPI; +use Utopia\Orchestration\Orchestration; require_once __DIR__.'/../init.php'; @@ -224,7 +224,7 @@ class FunctionsV1 extends Worker public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $eventData = '', string $data = '', array $webhooks = [], string $userId = '', string $jwt = ''): void { $ch = \curl_init(); - \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor:8080/v1/execute"); + \curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/execute"); \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'trigger' => $trigger, diff --git a/docker-compose.yml b/docker-compose.yml index dd43731b3..da6a18944 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -367,8 +367,6 @@ services: - /usr/src/code/app/executor.php - -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php stop_signal: SIGINT - ports: - - "8080:8080" build: context: . args: @@ -411,10 +409,10 @@ services: - _APP_USAGE_STATS - _APP_STATSD_HOST - _APP_STATSD_PORT - - DOCKERHUB_PULL_USERNAME - - DOCKERHUB_PULL_PASSWORD - _APP_LOGGING_PROVIDER - _APP_LOGGING_CONFIG + - DOCKERHUB_PULL_USERNAME + - DOCKERHUB_PULL_PASSWORD appwrite-worker-mails: entrypoint: worker-mails