From 2bb6a718740454faeb73e1e6ae0752fe7cdfc0b6 Mon Sep 17 00:00:00 2001 From: snekROmonoro <54446189+snekROmonoro@users.noreply.github.com> Date: Mon, 6 May 2024 15:42:01 +0200 Subject: [PATCH 01/38] "Inprogress" -> "In progress" --- resources/views/livewire/upgrade.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/upgrade.blade.php b/resources/views/livewire/upgrade.blade.php index 7447189ac..1e3ad014b 100644 --- a/resources/views/livewire/upgrade.blade.php +++ b/resources/views/livewire/upgrade.blade.php @@ -10,7 +10,7 @@ - Inprogress + In progress @else Date: Tue, 7 May 2024 09:43:41 +0200 Subject: [PATCH 02/38] Update version to 4.0.0-beta.277 --- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/sentry.php b/config/sentry.php index f36eb7818..e65072dcc 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.276', + 'release' => '4.0.0-beta.277', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index ae9fc8db5..fea91e1f0 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 7 May 2024 09:43:51 +0200 Subject: [PATCH 03/38] chore: remove docker compose versions --- docker-compose.dev.yml | 2 -- docker-compose.prod.yml | 1 - docker-compose.windows.yml | 1 - docker-compose.yml | 1 - 4 files changed, 5 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6d76a9abd..91e90b989 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: coolify: build: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index f68b2c41c..f3dda9748 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,4 +1,3 @@ -version: '3.8' services: coolify: image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-latest}" diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml index e35ece624..af5ecc0f7 100644 --- a/docker-compose.windows.yml +++ b/docker-compose.windows.yml @@ -1,4 +1,3 @@ -version: '3.8' services: coolify-testing-host: init: true diff --git a/docker-compose.yml b/docker-compose.yml index 6adfaf98a..6f1cf8e08 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.8' services: coolify: container_name: coolify From d0e9d58a43e2d15bad8a6ecb134b0b470c012e9e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 7 May 2024 10:22:02 +0200 Subject: [PATCH 04/38] chore: Add Listmonk service template and logo --- app/Models/Service.php | 40 ++++++++++++++++++----- public/svgs/listmonk.svg | 2 ++ templates/compose/listmonk.yaml | 56 ++++++++++++++++++++++++++++++++ templates/service-templates.json | 14 ++++++++ 4 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 public/svgs/listmonk.svg create mode 100644 templates/compose/listmonk.yaml diff --git a/app/Models/Service.php b/app/Models/Service.php index 5de1a9745..cd8e578d6 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -171,7 +171,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Tolgee', $data); + $fields->put('Tolgee', $data->toArray()); break; case str($image)?->contains('logto'): $data = collect([]); @@ -195,7 +195,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Logto', $data); + $fields->put('Logto', $data->toArray()); break; case str($image)?->contains('unleash-server'): $data = collect([]); @@ -218,7 +218,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Unleash', $data); + $fields->put('Unleash', $data->toArray()); break; case str($image)?->contains('grafana'): $data = collect([]); @@ -241,7 +241,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Grafana', $data); + $fields->put('Grafana', $data->toArray()); break; case str($image)?->contains('directus'): $data = collect([]); @@ -267,7 +267,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Directus', $data); + $fields->put('Directus', $data->toArray()); break; case str($image)?->contains('kong'): $data = collect([]); @@ -370,7 +370,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Weblate', $data); + $fields->put('Weblate', $data->toArray()); break; case str($image)?->contains('meilisearch'): $data = collect([]); @@ -384,7 +384,7 @@ class Service extends BaseModel ], ]); } - $fields->put('Meilisearch', $data); + $fields->put('Meilisearch', $data->toArray()); break; case str($image)?->contains('ghost'): $data = collect([]); @@ -444,7 +444,31 @@ class Service extends BaseModel ]); } - $fields->put('Ghost', $data); + $fields->put('Ghost', $data->toArray()); + break; + default: + $data = collect([]); + $admin_user = $this->environment_variables()->where('key', 'SERVICE_USER_ADMIN')->first(); + $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first(); + $data = $data->merge([ + 'User' => [ + 'key' => 'SERVICE_USER_ADMIN', + 'value' => data_get($admin_user, 'value', 'admin'), + 'readonly' => true, + 'rules' => 'required', + ], + ]); + if ($admin_password) { + $data = $data->merge([ + 'Password' => [ + 'key' => 'SERVICE_PASSWORD_ADMIN', + 'value' => data_get($admin_password, 'value'), + 'rules' => 'required', + 'isPassword' => true, + ], + ]); + } + $fields->put('Admin', $data->toArray()); break; } } diff --git a/public/svgs/listmonk.svg b/public/svgs/listmonk.svg new file mode 100644 index 000000000..a4e5efd5f --- /dev/null +++ b/public/svgs/listmonk.svg @@ -0,0 +1,2 @@ + + diff --git a/templates/compose/listmonk.yaml b/templates/compose/listmonk.yaml new file mode 100644 index 000000000..faa70fce5 --- /dev/null +++ b/templates/compose/listmonk.yaml @@ -0,0 +1,56 @@ +# documentation: https://listmonk.app/ +# slogan: Self-hosted newsletter and mailing list manager +# tags: newsletter, mailing list, self-hosted, open source +# logo: svgs/listmonk.svg +# port: 9000 + +services: + listmonk: + image: listmonk/listmonk:latest + environment: + - SERVICE_FQDN_LISTMONK_9000 + - LISTMONK_app__address=0.0.0.0:9000 + - LISTMONK_db__host=postgres + - LISTMONK_db__name=listmonk + - LISTMONK_db__user=$SERVICE_USER_POSTGRES + - LISTMONK_db__password=$SERVICE_PASSWORD_POSTGRES + - LISTMONK_db__port=5432 + - LISTMONK_app__admin_username=admin + - LISTMONK_app__admin_password=$SERVICE_PASSWORD_ADMIN + - TZ=Etc/UTC + volumes: + - "listmonk-data:/listmonk/uploads" + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:9000"] + interval: 5s + timeout: 20s + retries: 10 + listmonk-initial-database-setup: + image: listmonk/listmonk:latest + command: "./listmonk --install --yes --idempotent" + restart: "no" + depends_on: + postgres: + condition: service_healthy + environment: + - LISTMONK_db__host=postgres + - LISTMONK_db__name=listmonk + - LISTMONK_db__user=$SERVICE_USER_POSTGRES + - LISTMONK_db__password=$SERVICE_PASSWORD_POSTGRES + - LISTMONK_db__port=5432 + postgres: + image: "postgres:latest" + environment: + - POSTGRES_DB=listmonk + - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES + - POSTGRES_USER=$SERVICE_USER_POSTGRES + volumes: + - "pg-data:/var/lib/postgresql/data" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 20s + retries: 10 diff --git a/templates/service-templates.json b/templates/service-templates.json index d7fd23e4c..8c916e474 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -505,6 +505,20 @@ "minversion": "0.0.0", "port": "7512" }, + "listmonk": { + "documentation": "https:\/\/listmonk.app\/", + "slogan": "Self-hosted newsletter and mailing list manager", + "compose": "c2VydmljZXM6CiAgbGlzdG1vbms6CiAgICBpbWFnZTogJ2xpc3Rtb25rL2xpc3Rtb25rOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9MSVNUTU9OS185MDAwCiAgICAgIC0gJ0xJU1RNT05LX2FwcF9fYWRkcmVzcz0wLjAuMC4wOjkwMDAnCiAgICAgIC0gTElTVE1PTktfZGJfX2hvc3Q9cG9zdGdyZXMKICAgICAgLSBMSVNUTU9OS19kYl9fbmFtZT1saXN0bW9uawogICAgICAtIExJU1RNT05LX2RiX191c2VyPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBMSVNUTU9OS19kYl9fcGFzc3dvcmQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBMSVNUTU9OS19kYl9fcG9ydD01NDMyCiAgICAgIC0gTElTVE1PTktfYXBwX19hZG1pbl91c2VybmFtZT1hZG1pbgogICAgICAtIExJU1RNT05LX2FwcF9fYWRtaW5fcGFzc3dvcmQ9JFNFUlZJQ0VfUEFTU1dPUkRfQURNSU4KICAgICAgLSBUWj1FdGMvVVRDCiAgICB2b2x1bWVzOgogICAgICAtICdsaXN0bW9uay1kYXRhOi9saXN0bW9uay91cGxvYWRzJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSB3Z2V0CiAgICAgICAgLSAnLXEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgbGlzdG1vbmstaW5pdGlhbC1kYXRhYmFzZS1zZXR1cDoKICAgIGltYWdlOiAnbGlzdG1vbmsvbGlzdG1vbms6bGF0ZXN0JwogICAgY29tbWFuZDogJy4vbGlzdG1vbmsgLS1pbnN0YWxsIC0teWVzIC0taWRlbXBvdGVudCcKICAgIHJlc3RhcnQ6ICdubycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBMSVNUTU9OS19kYl9faG9zdD1wb3N0Z3JlcwogICAgICAtIExJU1RNT05LX2RiX19uYW1lPWxpc3Rtb25rCiAgICAgIC0gTElTVE1PTktfZGJfX3VzZXI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIExJU1RNT05LX2RiX19wYXNzd29yZD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIExJU1RNT05LX2RiX19wb3J0PTU0MzIKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfREI9bGlzdG1vbmsKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAncGctZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", + "tags": [ + "newsletter", + "mailing list", + "self-hosted", + "open source" + ], + "logo": "svgs\/listmonk.svg", + "minversion": "0.0.0", + "port": "9000" + }, "logto": { "documentation": "https:\/\/docs.logto.io\/docs\/tutorials\/get-started\/#logto-oss-self-hosted", "slogan": "Logto offers a comprehensive identity solution covering both the front and backend, complete with pre-built infrastructure and enterprise-grade solutions.", From 2b422a542a51adfb7a691c74ae4e9efc33370902 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 7 May 2024 12:35:24 +0200 Subject: [PATCH 05/38] fix: empty db conf feat: add listen_addresses to postgresql if its missing in the custom conf --- app/Actions/Database/StartKeydb.php | 4 ++-- app/Actions/Database/StartMariadb.php | 4 ++-- app/Actions/Database/StartMongodb.php | 4 ++-- app/Actions/Database/StartMysql.php | 4 ++-- app/Actions/Database/StartPostgresql.php | 12 +++++++----- app/Actions/Database/StartRedis.php | 4 ++-- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index 489c74053..52768b13e 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -96,7 +96,7 @@ class StartKeydb if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->keydb_conf)) { + if (!is_null($this->database->keydb_conf) || !empty($this->database->keydb_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir . '/keydb.conf', @@ -162,7 +162,7 @@ class StartKeydb } private function add_custom_keydb() { - if (is_null($this->database->keydb_conf)) { + if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) { return; } $filename = 'keydb.conf'; diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index e02b28b2e..b6d6c5bb3 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -90,7 +90,7 @@ class StartMariadb if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->mariadb_conf)) { + if (!is_null($this->database->mariadb_conf) || !empty($this->database->mariadb_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir . '/custom-config.cnf', @@ -165,7 +165,7 @@ class StartMariadb } private function add_custom_mysql() { - if (is_null($this->database->mariadb_conf)) { + if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) { return; } $filename = 'custom-config.cnf'; diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 7bb6cbcd0..b624c2f50 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -97,7 +97,7 @@ class StartMongodb if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->mongo_conf)) { + if (!is_null($this->database->mongo_conf) || !empty($this->database->mongo_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir . '/mongod.conf', @@ -178,7 +178,7 @@ class StartMongodb } private function add_custom_mongo_conf() { - if (is_null($this->database->mongo_conf)) { + if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) { return; } $filename = 'mongod.conf'; diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index b3f695d72..43b7292fb 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -90,7 +90,7 @@ class StartMysql if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->mysql_conf)) { + if (!is_null($this->database->mysql_conf) || !empty($this->database->mysql_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir . '/custom-config.cnf', @@ -165,7 +165,7 @@ class StartMysql } private function add_custom_mysql() { - if (is_null($this->database->mysql_conf)) { + if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) { return; } $filename = 'custom-config.cnf'; diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index f19a8b036..37eb157f8 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -78,7 +78,6 @@ class StartPostgresql data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); } if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { - ray('Log Drain Enabled'); $docker_compose['services'][$container_name]['logging'] = [ 'driver' => 'fluentd', 'options' => [ @@ -107,7 +106,7 @@ class StartPostgresql ]; } } - if (!is_null($this->database->postgres_conf)) { + if (!is_null($this->database->postgres_conf) && !empty($this->database->postgres_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir . '/custom-postgres.conf', @@ -165,8 +164,6 @@ class StartPostgresql private function generate_environment_variables() { $environment_variables = collect(); - ray('Generate Environment Variables')->green(); - ray($this->database->runtime_environment_variables)->green(); foreach ($this->database->runtime_environment_variables as $env) { $environment_variables->push("$env->key=$env->real_value"); } @@ -203,11 +200,16 @@ class StartPostgresql } private function add_custom_conf() { - if (is_null($this->database->postgres_conf)) { + if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) { return; } $filename = 'custom-postgres.conf'; $content = $this->database->postgres_conf; + if (!str($content)->contains('listen_addresses')) { + $content .= "\nlisten_addresses = '*'"; + $this->database->postgres_conf = $content; + $this->database->save(); + } $content_base64 = base64_encode($content); $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; } diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index 01e9a9bef..35b9d5c8e 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -100,7 +100,7 @@ class StartRedis if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->redis_conf)) { + if (!is_null($this->database->redis_conf) || !empty($this->database->redis_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir . '/redis.conf', @@ -166,7 +166,7 @@ class StartRedis } private function add_custom_redis() { - if (is_null($this->database->redis_conf)) { + if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) { return; } $filename = 'redis.conf'; From f6f959a8973b3d6e11ea477758e92e848adb3d33 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 7 May 2024 15:41:50 +0200 Subject: [PATCH 06/38] feat: experimental sentinel --- app/Actions/Docker/GetContainersStatus.php | 655 ++++++++++++++++++ app/Jobs/ApplicationDeploymentJob.php | 4 +- app/Jobs/ContainerStatusJob.php | 632 +++++++++-------- app/Livewire/Project/Application/Heading.php | 4 +- app/Livewire/Project/Database/Heading.php | 4 +- .../Project/Service/Configuration.php | 4 +- app/Livewire/Project/Shared/Destination.php | 4 +- app/Livewire/Server/Proxy/Status.php | 4 +- app/Notifications/Server/Revived.php | 4 +- .../2024_05_07_124019_add_server_metrics.php | 28 + docker-compose.dev.yml | 6 + docker-compose.prod.yml | 9 + docker-compose.yml | 7 +- 13 files changed, 1038 insertions(+), 327 deletions(-) create mode 100644 app/Actions/Docker/GetContainersStatus.php create mode 100644 database/migrations/2024_05_07_124019_add_server_metrics.php diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php new file mode 100644 index 000000000..952dbfa1b --- /dev/null +++ b/app/Actions/Docker/GetContainersStatus.php @@ -0,0 +1,655 @@ +server = Server::find(0); + } + } + public function handle() + { + if (!$this->server->isFunctional()) { + return 'Server is not ready.'; + }; + $this->applications = $this->server->applications(); + $skip_these_applications = collect([]); + foreach ($this->applications as $application) { + if ($application->additional_servers->count() > 0) { + $skip_these_applications->push($application); + ComplexStatusCheck::run($application); + $this->applications = $this->applications->filter(function ($value, $key) use ($application) { + return $value->id !== $application->id; + }); + } + } + $this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) { + return !$skip_these_applications->pluck('id')->contains($value->id); + }); + if ($this->server->isSwarm()) { + $this->old_way(); + } else { + if (!$this->server->is_metrics_enabled) { + $this->old_way(); + return; + } + $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false); + if ($sentinel_found) { + raY('Sentinel'); + $this->sentinel(); + } else { + raY('Old way'); + $this->old_way(); + } + } + } + + private function sentinel() + { + try { + $containers = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/containers"'], $this->server, false); + if (is_null($containers)) { + return; + } + $containers = data_get(json_decode($containers, true), 'containers', []); + $containers = collect($containers); + + $databases = $this->server->databases(); + $services = $this->server->services()->get(); + $previews = $this->server->previews(); + $foundApplications = []; + $foundApplicationPreviews = []; + $foundDatabases = []; + $foundServices = []; + + foreach ($containers as $container) { + $labels = Arr::undot(data_get($container, 'labels')); + $containerStatus = data_get($container, 'state'); + $containerHealth = data_get($container, 'health_status', 'unhealthy'); + $containerStatus = "$containerStatus ($containerHealth)"; + $applicationId = data_get($labels, 'coolify.applicationId'); + if ($applicationId) { + $pullRequestId = data_get($labels, 'coolify.pullRequestId'); + if ($pullRequestId) { + if (str($applicationId)->contains('-')) { + $applicationId = str($applicationId)->before('-'); + } + $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first(); + if ($preview) { + $foundApplicationPreviews[] = $preview->id; + $statusFromDb = $preview->status; + if ($statusFromDb !== $containerStatus) { + $preview->update(['status' => $containerStatus]); + } + } else { + //Notify user that this container should not be there. + } + } else { + $application = $this->applications->where('id', $applicationId)->first(); + if ($application) { + $foundApplications[] = $application->id; + $statusFromDb = $application->status; + ray($statusFromDb, $containerStatus); + if ($statusFromDb !== $containerStatus) { + $application->update(['status' => $containerStatus]); + } + } else { + //Notify user that this container should not be there. + } + } + } else { + $uuid = data_get($labels, 'com.docker.compose.service'); + $type = data_get($labels, 'coolify.type'); + + if ($uuid) { + if ($type === 'service') { + $database_id = data_get($labels, 'coolify.service.subId'); + if ($database_id) { + $service_db = ServiceDatabase::where('id', $database_id)->first(); + if ($service_db) { + $uuid = $service_db->service->uuid; + $isPublic = data_get($service_db, 'is_public'); + if ($isPublic) { + $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { + if ($this->server->isSwarm()) { + return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; + } else { + return data_get($value, 'Name') === "/$uuid-proxy"; + } + })->first(); + if (!$foundTcpProxy) { + StartDatabaseProxy::run($service_db); + // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); + } + } + } + } + } else { + $database = $databases->where('uuid', $uuid)->first(); + if ($database) { + $isPublic = data_get($database, 'is_public'); + $foundDatabases[] = $database->id; + $statusFromDb = $database->status; + if ($statusFromDb !== $containerStatus) { + $database->update(['status' => $containerStatus]); + } + if ($isPublic) { + $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { + if ($this->server->isSwarm()) { + return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; + } else { + return data_get($value, 'Name') === "/$uuid-proxy"; + } + })->first(); + if (!$foundTcpProxy) { + StartDatabaseProxy::run($database); + $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); + } + } + } else { + // Notify user that this container should not be there. + } + } + } + if (data_get($container, 'Name') === '/coolify-db') { + $foundDatabases[] = 0; + } + } + $serviceLabelId = data_get($labels, 'coolify.serviceId'); + if ($serviceLabelId) { + $subType = data_get($labels, 'coolify.service.subType'); + $subId = data_get($labels, 'coolify.service.subId'); + $service = $services->where('id', $serviceLabelId)->first(); + if (!$service) { + continue; + } + if ($subType === 'application') { + $service = $service->applications()->where('id', $subId)->first(); + } else { + $service = $service->databases()->where('id', $subId)->first(); + } + if ($service) { + $foundServices[] = "$service->id-$service->name"; + $statusFromDb = $service->status; + if ($statusFromDb !== $containerStatus) { + // ray('Updating status: ' . $containerStatus); + $service->update(['status' => $containerStatus]); + } + } + } + } + $exitedServices = collect([]); + foreach ($services as $service) { + $apps = $service->applications()->get(); + $dbs = $service->databases()->get(); + foreach ($apps as $app) { + if (in_array("$app->id-$app->name", $foundServices)) { + continue; + } else { + $exitedServices->push($app); + } + } + foreach ($dbs as $db) { + if (in_array("$db->id-$db->name", $foundServices)) { + continue; + } else { + $exitedServices->push($db); + } + } + } + $exitedServices = $exitedServices->unique('id'); + foreach ($exitedServices as $exitedService) { + if (str($exitedService->status)->startsWith('exited')) { + continue; + } + $name = data_get($exitedService, 'name'); + $fqdn = data_get($exitedService, 'fqdn'); + $containerName = $name ? "$name, available at $fqdn" : $fqdn; + $projectUuid = data_get($service, 'environment.project.uuid'); + $serviceUuid = data_get($service, 'uuid'); + $environmentName = data_get($service, 'environment.name'); + + if ($projectUuid && $serviceUuid && $environmentName) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; + } else { + $url = null; + } + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + $exitedService->update(['status' => 'exited']); + } + + $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications); + foreach ($notRunningApplications as $applicationId) { + $application = $this->applications->where('id', $applicationId)->first(); + if (str($application->status)->startsWith('exited')) { + continue; + } + $application->update(['status' => 'exited']); + + $name = data_get($application, 'name'); + $fqdn = data_get($application, 'fqdn'); + + $containerName = $name ? "$name ($fqdn)" : $fqdn; + + $projectUuid = data_get($application, 'environment.project.uuid'); + $applicationUuid = data_get($application, 'uuid'); + $environment = data_get($application, 'environment.name'); + + if ($projectUuid && $applicationUuid && $environment) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; + } else { + $url = null; + } + + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + } + $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews); + foreach ($notRunningApplicationPreviews as $previewId) { + $preview = $previews->where('id', $previewId)->first(); + if (str($preview->status)->startsWith('exited')) { + continue; + } + $preview->update(['status' => 'exited']); + + $name = data_get($preview, 'name'); + $fqdn = data_get($preview, 'fqdn'); + + $containerName = $name ? "$name ($fqdn)" : $fqdn; + + $projectUuid = data_get($preview, 'application.environment.project.uuid'); + $environmentName = data_get($preview, 'application.environment.name'); + $applicationUuid = data_get($preview, 'application.uuid'); + + if ($projectUuid && $applicationUuid && $environmentName) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; + } else { + $url = null; + } + + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + } + $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases); + foreach ($notRunningDatabases as $database) { + $database = $databases->where('id', $database)->first(); + if (str($database->status)->startsWith('exited')) { + continue; + } + $database->update(['status' => 'exited']); + + $name = data_get($database, 'name'); + $fqdn = data_get($database, 'fqdn'); + + $containerName = $name; + + $projectUuid = data_get($database, 'environment.project.uuid'); + $environmentName = data_get($database, 'environment.name'); + $databaseUuid = data_get($database, 'uuid'); + + if ($projectUuid && $databaseUuid && $environmentName) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; + } else { + $url = null; + } + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + } + + // Check if proxy is running + $this->server->proxyType(); + $foundProxyContainer = $containers->filter(function ($value, $key) { + if ($this->server->isSwarm()) { + return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik'; + } else { + return data_get($value, 'Name') === '/coolify-proxy'; + } + })->first(); + if (!$foundProxyContainer) { + try { + $shouldStart = CheckProxy::run($this->server); + if ($shouldStart) { + StartProxy::run($this->server, false); + $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server)); + } + } catch (\Throwable $e) { + ray($e); + } + } else { + $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); + $this->server->save(); + $connectProxyToDockerNetworks = connectProxyToNetworks($this->server); + instant_remote_process($connectProxyToDockerNetworks, $this->server, false); + } + } catch (\Exception $e) { + send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage()); + ray($e->getMessage()); + return handleError($e); + } + } + private function old_way() + { + if ($this->server->isSwarm()) { + $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false); + $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false); + } else { + // Precheck for containers + $containers = instant_remote_process(["docker container ls -q"], $this->server, false); + if (!$containers) { + return; + } + $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false); + $containerReplicates = null; + } + if (is_null($containers)) { + return; + } + + $containers = format_docker_command_output_to_json($containers); + if ($containerReplicates) { + $containerReplicates = format_docker_command_output_to_json($containerReplicates); + foreach ($containerReplicates as $containerReplica) { + $name = data_get($containerReplica, 'Name'); + $containers = $containers->map(function ($container) use ($name, $containerReplica) { + if (data_get($container, 'Spec.Name') === $name) { + $replicas = data_get($containerReplica, 'Replicas'); + $running = str($replicas)->explode('/')[0]; + $total = str($replicas)->explode('/')[1]; + if ($running === $total) { + data_set($container, 'State.Status', 'running'); + data_set($container, 'State.Health.Status', 'healthy'); + } else { + data_set($container, 'State.Status', 'starting'); + data_set($container, 'State.Health.Status', 'unhealthy'); + } + } + return $container; + }); + } + } + $databases = $this->server->databases(); + $services = $this->server->services()->get(); + $previews = $this->server->previews(); + $foundApplications = []; + $foundApplicationPreviews = []; + $foundDatabases = []; + $foundServices = []; + + foreach ($containers as $container) { + if ($this->server->isSwarm()) { + $labels = data_get($container, 'Spec.Labels'); + $uuid = data_get($labels, 'coolify.name'); + } else { + $labels = data_get($container, 'Config.Labels'); + } + $containerStatus = data_get($container, 'State.Status'); + $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy'); + $containerStatus = "$containerStatus ($containerHealth)"; + $labels = Arr::undot(format_docker_labels_to_json($labels)); + $applicationId = data_get($labels, 'coolify.applicationId'); + if ($applicationId) { + $pullRequestId = data_get($labels, 'coolify.pullRequestId'); + if ($pullRequestId) { + if (str($applicationId)->contains('-')) { + $applicationId = str($applicationId)->before('-'); + } + $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first(); + if ($preview) { + $foundApplicationPreviews[] = $preview->id; + $statusFromDb = $preview->status; + if ($statusFromDb !== $containerStatus) { + $preview->update(['status' => $containerStatus]); + } + } else { + //Notify user that this container should not be there. + } + } else { + $application = $this->applications->where('id', $applicationId)->first(); + if ($application) { + $foundApplications[] = $application->id; + $statusFromDb = $application->status; + if ($statusFromDb !== $containerStatus) { + $application->update(['status' => $containerStatus]); + } + } else { + //Notify user that this container should not be there. + } + } + } else { + $uuid = data_get($labels, 'com.docker.compose.service'); + $type = data_get($labels, 'coolify.type'); + + if ($uuid) { + if ($type === 'service') { + $database_id = data_get($labels, 'coolify.service.subId'); + if ($database_id) { + $service_db = ServiceDatabase::where('id', $database_id)->first(); + if ($service_db) { + $uuid = $service_db->service->uuid; + $isPublic = data_get($service_db, 'is_public'); + if ($isPublic) { + $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { + if ($this->server->isSwarm()) { + return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; + } else { + return data_get($value, 'Name') === "/$uuid-proxy"; + } + })->first(); + if (!$foundTcpProxy) { + StartDatabaseProxy::run($service_db); + // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); + } + } + } + } + } else { + $database = $databases->where('uuid', $uuid)->first(); + if ($database) { + $isPublic = data_get($database, 'is_public'); + $foundDatabases[] = $database->id; + $statusFromDb = $database->status; + if ($statusFromDb !== $containerStatus) { + $database->update(['status' => $containerStatus]); + } + if ($isPublic) { + $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { + if ($this->server->isSwarm()) { + return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; + } else { + return data_get($value, 'Name') === "/$uuid-proxy"; + } + })->first(); + if (!$foundTcpProxy) { + StartDatabaseProxy::run($database); + $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); + } + } + } else { + // Notify user that this container should not be there. + } + } + } + if (data_get($container, 'Name') === '/coolify-db') { + $foundDatabases[] = 0; + } + } + $serviceLabelId = data_get($labels, 'coolify.serviceId'); + if ($serviceLabelId) { + $subType = data_get($labels, 'coolify.service.subType'); + $subId = data_get($labels, 'coolify.service.subId'); + $service = $services->where('id', $serviceLabelId)->first(); + if (!$service) { + continue; + } + if ($subType === 'application') { + $service = $service->applications()->where('id', $subId)->first(); + } else { + $service = $service->databases()->where('id', $subId)->first(); + } + if ($service) { + $foundServices[] = "$service->id-$service->name"; + $statusFromDb = $service->status; + if ($statusFromDb !== $containerStatus) { + // ray('Updating status: ' . $containerStatus); + $service->update(['status' => $containerStatus]); + } + } + } + } + $exitedServices = collect([]); + foreach ($services as $service) { + $apps = $service->applications()->get(); + $dbs = $service->databases()->get(); + foreach ($apps as $app) { + if (in_array("$app->id-$app->name", $foundServices)) { + continue; + } else { + $exitedServices->push($app); + } + } + foreach ($dbs as $db) { + if (in_array("$db->id-$db->name", $foundServices)) { + continue; + } else { + $exitedServices->push($db); + } + } + } + $exitedServices = $exitedServices->unique('id'); + foreach ($exitedServices as $exitedService) { + if (str($exitedService->status)->startsWith('exited')) { + continue; + } + $name = data_get($exitedService, 'name'); + $fqdn = data_get($exitedService, 'fqdn'); + $containerName = $name ? "$name, available at $fqdn" : $fqdn; + $projectUuid = data_get($service, 'environment.project.uuid'); + $serviceUuid = data_get($service, 'uuid'); + $environmentName = data_get($service, 'environment.name'); + + if ($projectUuid && $serviceUuid && $environmentName) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; + } else { + $url = null; + } + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + $exitedService->update(['status' => 'exited']); + } + + $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications); + foreach ($notRunningApplications as $applicationId) { + $application = $this->applications->where('id', $applicationId)->first(); + if (str($application->status)->startsWith('exited')) { + continue; + } + $application->update(['status' => 'exited']); + + $name = data_get($application, 'name'); + $fqdn = data_get($application, 'fqdn'); + + $containerName = $name ? "$name ($fqdn)" : $fqdn; + + $projectUuid = data_get($application, 'environment.project.uuid'); + $applicationUuid = data_get($application, 'uuid'); + $environment = data_get($application, 'environment.name'); + + if ($projectUuid && $applicationUuid && $environment) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; + } else { + $url = null; + } + + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + } + $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews); + foreach ($notRunningApplicationPreviews as $previewId) { + $preview = $previews->where('id', $previewId)->first(); + if (str($preview->status)->startsWith('exited')) { + continue; + } + $preview->update(['status' => 'exited']); + + $name = data_get($preview, 'name'); + $fqdn = data_get($preview, 'fqdn'); + + $containerName = $name ? "$name ($fqdn)" : $fqdn; + + $projectUuid = data_get($preview, 'application.environment.project.uuid'); + $environmentName = data_get($preview, 'application.environment.name'); + $applicationUuid = data_get($preview, 'application.uuid'); + + if ($projectUuid && $applicationUuid && $environmentName) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; + } else { + $url = null; + } + + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + } + $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases); + foreach ($notRunningDatabases as $database) { + $database = $databases->where('id', $database)->first(); + if (str($database->status)->startsWith('exited')) { + continue; + } + $database->update(['status' => 'exited']); + + $name = data_get($database, 'name'); + $fqdn = data_get($database, 'fqdn'); + + $containerName = $name; + + $projectUuid = data_get($database, 'environment.project.uuid'); + $environmentName = data_get($database, 'environment.name'); + $databaseUuid = data_get($database, 'uuid'); + + if ($projectUuid && $databaseUuid && $environmentName) { + $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; + } else { + $url = null; + } + $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + } + + // Check if proxy is running + $this->server->proxyType(); + $foundProxyContainer = $containers->filter(function ($value, $key) { + if ($this->server->isSwarm()) { + return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik'; + } else { + return data_get($value, 'Name') === '/coolify-proxy'; + } + })->first(); + if (!$foundProxyContainer) { + try { + $shouldStart = CheckProxy::run($this->server); + if ($shouldStart) { + StartProxy::run($this->server, false); + $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server)); + } + } catch (\Throwable $e) { + ray($e); + } + } else { + $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); + $this->server->save(); + $connectProxyToDockerNetworks = connectProxyToNetworks($this->server); + instant_remote_process($connectProxyToDockerNetworks, $this->server, false); + } + } +} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index e3221e091..54b96ee68 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Actions\Docker\GetContainersStatus; use App\Enums\ApplicationDeploymentStatus; use App\Enums\ProcessStatus; use App\Events\ApplicationStatusChanged; @@ -302,7 +303,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted { if ($this->server->isProxyShouldRun()) { - dispatch(new ContainerStatusJob($this->server)); + GetContainersStatus::dispatch($this->server); + // dispatch(new ContainerStatusJob($this->server)); } $this->next(ApplicationDeploymentStatus::FINISHED->value); if ($this->pull_request_id !== 0) { diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index a9cec009b..96caf5dba 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -2,15 +2,8 @@ namespace App\Jobs; -use App\Actions\Database\StartDatabaseProxy; -use App\Actions\Proxy\CheckProxy; -use App\Actions\Proxy\StartProxy; -use App\Actions\Shared\ComplexStatusCheck; -use App\Models\ApplicationPreview; +use App\Actions\Docker\GetContainersStatus; use App\Models\Server; -use App\Models\ServiceDatabase; -use App\Notifications\Container\ContainerRestarted; -use App\Notifications\Container\ContainerStopped; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; @@ -18,7 +11,6 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Arr; class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted { @@ -44,335 +36,337 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted public function handle() { - if (!$this->server->isFunctional()) { - return 'Server is not ready.'; - }; - $applications = $this->server->applications(); - $skip_these_applications = collect([]); - foreach ($applications as $application) { - if ($application->additional_servers->count() > 0) { - $skip_these_applications->push($application); - ComplexStatusCheck::run($application); - $applications = $applications->filter(function ($value, $key) use ($application) { - return $value->id !== $application->id; - }); - } - } - $applications = $applications->filter(function ($value, $key) use ($skip_these_applications) { - return !$skip_these_applications->pluck('id')->contains($value->id); - }); - try { - if ($this->server->isSwarm()) { - $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false); - $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false); - } else { - // Precheck for containers - $containers = instant_remote_process(["docker container ls -q"], $this->server, false); - if (!$containers) { - return; - } - $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false); - $containerReplicates = null; - } - if (is_null($containers)) { - return; - } + GetContainersStatus::run(); + return; + // if (!$this->server->isFunctional()) { + // return 'Server is not ready.'; + // }; + // $applications = $this->server->applications(); + // $skip_these_applications = collect([]); + // foreach ($applications as $application) { + // if ($application->additional_servers->count() > 0) { + // $skip_these_applications->push($application); + // ComplexStatusCheck::run($application); + // $applications = $applications->filter(function ($value, $key) use ($application) { + // return $value->id !== $application->id; + // }); + // } + // } + // $applications = $applications->filter(function ($value, $key) use ($skip_these_applications) { + // return !$skip_these_applications->pluck('id')->contains($value->id); + // }); + // try { + // if ($this->server->isSwarm()) { + // $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false); + // $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false); + // } else { + // // Precheck for containers + // $containers = instant_remote_process(["docker container ls -q"], $this->server, false); + // if (!$containers) { + // return; + // } + // $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false); + // $containerReplicates = null; + // } + // if (is_null($containers)) { + // return; + // } - $containers = format_docker_command_output_to_json($containers); - if ($containerReplicates) { - $containerReplicates = format_docker_command_output_to_json($containerReplicates); - foreach ($containerReplicates as $containerReplica) { - $name = data_get($containerReplica, 'Name'); - $containers = $containers->map(function ($container) use ($name, $containerReplica) { - if (data_get($container, 'Spec.Name') === $name) { - $replicas = data_get($containerReplica, 'Replicas'); - $running = str($replicas)->explode('/')[0]; - $total = str($replicas)->explode('/')[1]; - if ($running === $total) { - data_set($container, 'State.Status', 'running'); - data_set($container, 'State.Health.Status', 'healthy'); - } else { - data_set($container, 'State.Status', 'starting'); - data_set($container, 'State.Health.Status', 'unhealthy'); - } - } - return $container; - }); - } - } - $databases = $this->server->databases(); - $services = $this->server->services()->get(); - $previews = $this->server->previews(); - $foundApplications = []; - $foundApplicationPreviews = []; - $foundDatabases = []; - $foundServices = []; + // $containers = format_docker_command_output_to_json($containers); + // if ($containerReplicates) { + // $containerReplicates = format_docker_command_output_to_json($containerReplicates); + // foreach ($containerReplicates as $containerReplica) { + // $name = data_get($containerReplica, 'Name'); + // $containers = $containers->map(function ($container) use ($name, $containerReplica) { + // if (data_get($container, 'Spec.Name') === $name) { + // $replicas = data_get($containerReplica, 'Replicas'); + // $running = str($replicas)->explode('/')[0]; + // $total = str($replicas)->explode('/')[1]; + // if ($running === $total) { + // data_set($container, 'State.Status', 'running'); + // data_set($container, 'State.Health.Status', 'healthy'); + // } else { + // data_set($container, 'State.Status', 'starting'); + // data_set($container, 'State.Health.Status', 'unhealthy'); + // } + // } + // return $container; + // }); + // } + // } + // $databases = $this->server->databases(); + // $services = $this->server->services()->get(); + // $previews = $this->server->previews(); + // $foundApplications = []; + // $foundApplicationPreviews = []; + // $foundDatabases = []; + // $foundServices = []; - foreach ($containers as $container) { - if ($this->server->isSwarm()) { - $labels = data_get($container, 'Spec.Labels'); - $uuid = data_get($labels, 'coolify.name'); - } else { - $labels = data_get($container, 'Config.Labels'); - } - $containerStatus = data_get($container, 'State.Status'); - $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy'); - $containerStatus = "$containerStatus ($containerHealth)"; - $labels = Arr::undot(format_docker_labels_to_json($labels)); - $applicationId = data_get($labels, 'coolify.applicationId'); - if ($applicationId) { - $pullRequestId = data_get($labels, 'coolify.pullRequestId'); - if ($pullRequestId) { - if (str($applicationId)->contains('-')) { - $applicationId = str($applicationId)->before('-'); - } - $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first(); - if ($preview) { - $foundApplicationPreviews[] = $preview->id; - $statusFromDb = $preview->status; - if ($statusFromDb !== $containerStatus) { - $preview->update(['status' => $containerStatus]); - } - } else { - //Notify user that this container should not be there. - } - } else { - $application = $applications->where('id', $applicationId)->first(); - if ($application) { - $foundApplications[] = $application->id; - $statusFromDb = $application->status; - if ($statusFromDb !== $containerStatus) { - $application->update(['status' => $containerStatus]); - } - } else { - //Notify user that this container should not be there. - } - } - } else { - $uuid = data_get($labels, 'com.docker.compose.service'); - $type = data_get($labels, 'coolify.type'); + // foreach ($containers as $container) { + // if ($this->server->isSwarm()) { + // $labels = data_get($container, 'Spec.Labels'); + // $uuid = data_get($labels, 'coolify.name'); + // } else { + // $labels = data_get($container, 'Config.Labels'); + // } + // $containerStatus = data_get($container, 'State.Status'); + // $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy'); + // $containerStatus = "$containerStatus ($containerHealth)"; + // $labels = Arr::undot(format_docker_labels_to_json($labels)); + // $applicationId = data_get($labels, 'coolify.applicationId'); + // if ($applicationId) { + // $pullRequestId = data_get($labels, 'coolify.pullRequestId'); + // if ($pullRequestId) { + // if (str($applicationId)->contains('-')) { + // $applicationId = str($applicationId)->before('-'); + // } + // $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first(); + // if ($preview) { + // $foundApplicationPreviews[] = $preview->id; + // $statusFromDb = $preview->status; + // if ($statusFromDb !== $containerStatus) { + // $preview->update(['status' => $containerStatus]); + // } + // } else { + // //Notify user that this container should not be there. + // } + // } else { + // $application = $applications->where('id', $applicationId)->first(); + // if ($application) { + // $foundApplications[] = $application->id; + // $statusFromDb = $application->status; + // if ($statusFromDb !== $containerStatus) { + // $application->update(['status' => $containerStatus]); + // } + // } else { + // //Notify user that this container should not be there. + // } + // } + // } else { + // $uuid = data_get($labels, 'com.docker.compose.service'); + // $type = data_get($labels, 'coolify.type'); - if ($uuid) { - if ($type === 'service') { - $database_id = data_get($labels, 'coolify.service.subId'); - if ($database_id) { - $service_db = ServiceDatabase::where('id', $database_id)->first(); - if ($service_db) { - $uuid = $service_db->service->uuid; - $isPublic = data_get($service_db, 'is_public'); - if ($isPublic) { - $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { - if ($this->server->isSwarm()) { - return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; - } else { - return data_get($value, 'Name') === "/$uuid-proxy"; - } - })->first(); - if (!$foundTcpProxy) { - StartDatabaseProxy::run($service_db); - // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); - } - } - } - } - } else { - $database = $databases->where('uuid', $uuid)->first(); - if ($database) { - $isPublic = data_get($database, 'is_public'); - $foundDatabases[] = $database->id; - $statusFromDb = $database->status; - if ($statusFromDb !== $containerStatus) { - $database->update(['status' => $containerStatus]); - } - if ($isPublic) { - $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { - if ($this->server->isSwarm()) { - return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; - } else { - return data_get($value, 'Name') === "/$uuid-proxy"; - } - })->first(); - if (!$foundTcpProxy) { - StartDatabaseProxy::run($database); - $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); - } - } - } else { - // Notify user that this container should not be there. - } - } - } - if (data_get($container, 'Name') === '/coolify-db') { - $foundDatabases[] = 0; - } - } - $serviceLabelId = data_get($labels, 'coolify.serviceId'); - if ($serviceLabelId) { - $subType = data_get($labels, 'coolify.service.subType'); - $subId = data_get($labels, 'coolify.service.subId'); - $service = $services->where('id', $serviceLabelId)->first(); - if (!$service) { - continue; - } - if ($subType === 'application') { - $service = $service->applications()->where('id', $subId)->first(); - } else { - $service = $service->databases()->where('id', $subId)->first(); - } - if ($service) { - $foundServices[] = "$service->id-$service->name"; - $statusFromDb = $service->status; - if ($statusFromDb !== $containerStatus) { - // ray('Updating status: ' . $containerStatus); - $service->update(['status' => $containerStatus]); - } - } - } - } - $exitedServices = collect([]); - foreach ($services as $service) { - $apps = $service->applications()->get(); - $dbs = $service->databases()->get(); - foreach ($apps as $app) { - if (in_array("$app->id-$app->name", $foundServices)) { - continue; - } else { - $exitedServices->push($app); - } - } - foreach ($dbs as $db) { - if (in_array("$db->id-$db->name", $foundServices)) { - continue; - } else { - $exitedServices->push($db); - } - } - } - $exitedServices = $exitedServices->unique('id'); - foreach ($exitedServices as $exitedService) { - if (str($exitedService->status)->startsWith('exited')) { - continue; - } - $name = data_get($exitedService, 'name'); - $fqdn = data_get($exitedService, 'fqdn'); - $containerName = $name ? "$name, available at $fqdn" : $fqdn; - $projectUuid = data_get($service, 'environment.project.uuid'); - $serviceUuid = data_get($service, 'uuid'); - $environmentName = data_get($service, 'environment.name'); + // if ($uuid) { + // if ($type === 'service') { + // $database_id = data_get($labels, 'coolify.service.subId'); + // if ($database_id) { + // $service_db = ServiceDatabase::where('id', $database_id)->first(); + // if ($service_db) { + // $uuid = $service_db->service->uuid; + // $isPublic = data_get($service_db, 'is_public'); + // if ($isPublic) { + // $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { + // if ($this->server->isSwarm()) { + // return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; + // } else { + // return data_get($value, 'Name') === "/$uuid-proxy"; + // } + // })->first(); + // if (!$foundTcpProxy) { + // StartDatabaseProxy::run($service_db); + // // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); + // } + // } + // } + // } + // } else { + // $database = $databases->where('uuid', $uuid)->first(); + // if ($database) { + // $isPublic = data_get($database, 'is_public'); + // $foundDatabases[] = $database->id; + // $statusFromDb = $database->status; + // if ($statusFromDb !== $containerStatus) { + // $database->update(['status' => $containerStatus]); + // } + // if ($isPublic) { + // $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) { + // if ($this->server->isSwarm()) { + // return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; + // } else { + // return data_get($value, 'Name') === "/$uuid-proxy"; + // } + // })->first(); + // if (!$foundTcpProxy) { + // StartDatabaseProxy::run($database); + // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); + // } + // } + // } else { + // // Notify user that this container should not be there. + // } + // } + // } + // if (data_get($container, 'Name') === '/coolify-db') { + // $foundDatabases[] = 0; + // } + // } + // $serviceLabelId = data_get($labels, 'coolify.serviceId'); + // if ($serviceLabelId) { + // $subType = data_get($labels, 'coolify.service.subType'); + // $subId = data_get($labels, 'coolify.service.subId'); + // $service = $services->where('id', $serviceLabelId)->first(); + // if (!$service) { + // continue; + // } + // if ($subType === 'application') { + // $service = $service->applications()->where('id', $subId)->first(); + // } else { + // $service = $service->databases()->where('id', $subId)->first(); + // } + // if ($service) { + // $foundServices[] = "$service->id-$service->name"; + // $statusFromDb = $service->status; + // if ($statusFromDb !== $containerStatus) { + // // ray('Updating status: ' . $containerStatus); + // $service->update(['status' => $containerStatus]); + // } + // } + // } + // } + // $exitedServices = collect([]); + // foreach ($services as $service) { + // $apps = $service->applications()->get(); + // $dbs = $service->databases()->get(); + // foreach ($apps as $app) { + // if (in_array("$app->id-$app->name", $foundServices)) { + // continue; + // } else { + // $exitedServices->push($app); + // } + // } + // foreach ($dbs as $db) { + // if (in_array("$db->id-$db->name", $foundServices)) { + // continue; + // } else { + // $exitedServices->push($db); + // } + // } + // } + // $exitedServices = $exitedServices->unique('id'); + // foreach ($exitedServices as $exitedService) { + // if (str($exitedService->status)->startsWith('exited')) { + // continue; + // } + // $name = data_get($exitedService, 'name'); + // $fqdn = data_get($exitedService, 'fqdn'); + // $containerName = $name ? "$name, available at $fqdn" : $fqdn; + // $projectUuid = data_get($service, 'environment.project.uuid'); + // $serviceUuid = data_get($service, 'uuid'); + // $environmentName = data_get($service, 'environment.name'); - if ($projectUuid && $serviceUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; - } else { - $url = null; - } - $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); - $exitedService->update(['status' => 'exited']); - } + // if ($projectUuid && $serviceUuid && $environmentName) { + // $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; + // } else { + // $url = null; + // } + // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + // $exitedService->update(['status' => 'exited']); + // } - $notRunningApplications = $applications->pluck('id')->diff($foundApplications); - foreach ($notRunningApplications as $applicationId) { - $application = $applications->where('id', $applicationId)->first(); - if (str($application->status)->startsWith('exited')) { - continue; - } - $application->update(['status' => 'exited']); + // $notRunningApplications = $applications->pluck('id')->diff($foundApplications); + // foreach ($notRunningApplications as $applicationId) { + // $application = $applications->where('id', $applicationId)->first(); + // if (str($application->status)->startsWith('exited')) { + // continue; + // } + // $application->update(['status' => 'exited']); - $name = data_get($application, 'name'); - $fqdn = data_get($application, 'fqdn'); + // $name = data_get($application, 'name'); + // $fqdn = data_get($application, 'fqdn'); - $containerName = $name ? "$name ($fqdn)" : $fqdn; + // $containerName = $name ? "$name ($fqdn)" : $fqdn; - $projectUuid = data_get($application, 'environment.project.uuid'); - $applicationUuid = data_get($application, 'uuid'); - $environment = data_get($application, 'environment.name'); + // $projectUuid = data_get($application, 'environment.project.uuid'); + // $applicationUuid = data_get($application, 'uuid'); + // $environment = data_get($application, 'environment.name'); - if ($projectUuid && $applicationUuid && $environment) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; - } else { - $url = null; - } + // if ($projectUuid && $applicationUuid && $environment) { + // $url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; + // } else { + // $url = null; + // } - $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); - } - $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews); - foreach ($notRunningApplicationPreviews as $previewId) { - $preview = $previews->where('id', $previewId)->first(); - if (str($preview->status)->startsWith('exited')) { - continue; - } - $preview->update(['status' => 'exited']); + // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + // } + // $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews); + // foreach ($notRunningApplicationPreviews as $previewId) { + // $preview = $previews->where('id', $previewId)->first(); + // if (str($preview->status)->startsWith('exited')) { + // continue; + // } + // $preview->update(['status' => 'exited']); - $name = data_get($preview, 'name'); - $fqdn = data_get($preview, 'fqdn'); + // $name = data_get($preview, 'name'); + // $fqdn = data_get($preview, 'fqdn'); - $containerName = $name ? "$name ($fqdn)" : $fqdn; + // $containerName = $name ? "$name ($fqdn)" : $fqdn; - $projectUuid = data_get($preview, 'application.environment.project.uuid'); - $environmentName = data_get($preview, 'application.environment.name'); - $applicationUuid = data_get($preview, 'application.uuid'); + // $projectUuid = data_get($preview, 'application.environment.project.uuid'); + // $environmentName = data_get($preview, 'application.environment.name'); + // $applicationUuid = data_get($preview, 'application.uuid'); - if ($projectUuid && $applicationUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; - } else { - $url = null; - } + // if ($projectUuid && $applicationUuid && $environmentName) { + // $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; + // } else { + // $url = null; + // } - $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); - } - $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases); - foreach ($notRunningDatabases as $database) { - $database = $databases->where('id', $database)->first(); - if (str($database->status)->startsWith('exited')) { - continue; - } - $database->update(['status' => 'exited']); + // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + // } + // $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases); + // foreach ($notRunningDatabases as $database) { + // $database = $databases->where('id', $database)->first(); + // if (str($database->status)->startsWith('exited')) { + // continue; + // } + // $database->update(['status' => 'exited']); - $name = data_get($database, 'name'); - $fqdn = data_get($database, 'fqdn'); + // $name = data_get($database, 'name'); + // $fqdn = data_get($database, 'fqdn'); - $containerName = $name; + // $containerName = $name; - $projectUuid = data_get($database, 'environment.project.uuid'); - $environmentName = data_get($database, 'environment.name'); - $databaseUuid = data_get($database, 'uuid'); + // $projectUuid = data_get($database, 'environment.project.uuid'); + // $environmentName = data_get($database, 'environment.name'); + // $databaseUuid = data_get($database, 'uuid'); - if ($projectUuid && $databaseUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; - } else { - $url = null; - } - $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); - } + // if ($projectUuid && $databaseUuid && $environmentName) { + // $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; + // } else { + // $url = null; + // } + // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); + // } - // Check if proxy is running - $this->server->proxyType(); - $foundProxyContainer = $containers->filter(function ($value, $key) { - if ($this->server->isSwarm()) { - return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik'; - } else { - return data_get($value, 'Name') === '/coolify-proxy'; - } - })->first(); - if (!$foundProxyContainer) { - try { - $shouldStart = CheckProxy::run($this->server); - if ($shouldStart) { - StartProxy::run($this->server, false); - $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server)); - } - } catch (\Throwable $e) { - ray($e); - } - } else { - $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); - $this->server->save(); - $connectProxyToDockerNetworks = connectProxyToNetworks($this->server); - instant_remote_process($connectProxyToDockerNetworks, $this->server, false); - } - } catch (\Throwable $e) { - send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage()); - ray($e->getMessage()); - return handleError($e); - } + // // Check if proxy is running + // $this->server->proxyType(); + // $foundProxyContainer = $containers->filter(function ($value, $key) { + // if ($this->server->isSwarm()) { + // return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik'; + // } else { + // return data_get($value, 'Name') === '/coolify-proxy'; + // } + // })->first(); + // if (!$foundProxyContainer) { + // try { + // $shouldStart = CheckProxy::run($this->server); + // if ($shouldStart) { + // StartProxy::run($this->server, false); + // $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server)); + // } + // } catch (\Throwable $e) { + // ray($e); + // } + // } else { + // $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); + // $this->server->save(); + // $connectProxyToDockerNetworks = connectProxyToNetworks($this->server); + // instant_remote_process($connectProxyToDockerNetworks, $this->server, false); + // } + // } catch (\Throwable $e) { + // send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage()); + // ray($e->getMessage()); + // return handleError($e); + // } } } diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 9adee7003..06b9a3c3c 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -3,6 +3,7 @@ namespace App\Livewire\Project\Application; use App\Actions\Application\StopApplication; +use App\Actions\Docker\GetContainersStatus; use App\Events\ApplicationStatusChanged; use App\Jobs\ContainerStatusJob; use App\Jobs\ServerStatusJob; @@ -32,7 +33,8 @@ class Heading extends Component public function check_status($showNotification = false) { if ($this->application->destination->server->isFunctional()) { - dispatch(new ContainerStatusJob($this->application->destination->server)); + GetContainersStatus::dispatch($this->application->destination->server); + // dispatch(new ContainerStatusJob($this->application->destination->server)); } else { dispatch(new ServerStatusJob($this->application->destination->server)); } diff --git a/app/Livewire/Project/Database/Heading.php b/app/Livewire/Project/Database/Heading.php index 960ff2689..b454a683c 100644 --- a/app/Livewire/Project/Database/Heading.php +++ b/app/Livewire/Project/Database/Heading.php @@ -11,6 +11,7 @@ use App\Actions\Database\StartMysql; use App\Actions\Database\StartPostgresql; use App\Actions\Database\StartRedis; use App\Actions\Database\StopDatabase; +use App\Actions\Docker\GetContainersStatus; use App\Jobs\ContainerStatusJob; use Livewire\Component; @@ -44,7 +45,8 @@ class Heading extends Component public function check_status($showNotification = false) { - dispatch_sync(new ContainerStatusJob($this->database->destination->server)); + GetContainersStatus::run($this->application->destination->server); + // dispatch_sync(new ContainerStatusJob($this->database->destination->server)); $this->database->refresh(); if ($showNotification) $this->dispatch('success', 'Database status updated.'); } diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index 2cbda4e02..0b26af22f 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -2,6 +2,7 @@ namespace App\Livewire\Project\Service; +use App\Actions\Docker\GetContainersStatus; use App\Jobs\ContainerStatusJob; use App\Models\Service; use Livewire\Component; @@ -64,7 +65,8 @@ class Configuration extends Component public function check_status() { try { - dispatch_sync(new ContainerStatusJob($this->service->server)); + GetContainersStatus::run($this->service->server); + // dispatch_sync(new ContainerStatusJob($this->service->server)); $this->dispatch('refresh')->self(); $this->dispatch('updateStatus'); } catch (\Exception $e) { diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index fa19e8c42..2ccae47fd 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -3,6 +3,7 @@ namespace App\Livewire\Project\Shared; use App\Actions\Application\StopApplicationOneServer; +use App\Actions\Docker\GetContainersStatus; use App\Events\ApplicationStatusChanged; use App\Jobs\ContainerStatusJob; use App\Models\Server; @@ -90,7 +91,8 @@ class Destination extends Component } public function refreshServers() { - ContainerStatusJob::dispatchSync($this->resource->destination->server); + GetContainersStatus::run($this->resource->destination->server); + // ContainerStatusJob::dispatchSync($this->resource->destination->server); $this->loadData(); $this->dispatch('refresh'); ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id')); diff --git a/app/Livewire/Server/Proxy/Status.php b/app/Livewire/Server/Proxy/Status.php index bd0ffe431..fbc16fde4 100644 --- a/app/Livewire/Server/Proxy/Status.php +++ b/app/Livewire/Server/Proxy/Status.php @@ -2,6 +2,7 @@ namespace App\Livewire\Server\Proxy; +use App\Actions\Docker\GetContainersStatus; use App\Actions\Proxy\CheckProxy; use App\Jobs\ContainerStatusJob; use App\Models\Server; @@ -49,7 +50,8 @@ class Status extends Component public function getProxyStatus() { try { - dispatch_sync(new ContainerStatusJob($this->server)); + GetContainersStatus::run($this->server); + // dispatch_sync(new ContainerStatusJob($this->server)); $this->dispatch('proxyStatusUpdated'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Notifications/Server/Revived.php b/app/Notifications/Server/Revived.php index c670ded9a..36775976b 100644 --- a/app/Notifications/Server/Revived.php +++ b/app/Notifications/Server/Revived.php @@ -2,6 +2,7 @@ namespace App\Notifications\Server; +use App\Actions\Docker\GetContainersStatus; use App\Jobs\ContainerStatusJob; use App\Models\Server; use Illuminate\Bus\Queueable; @@ -22,7 +23,8 @@ class Revived extends Notification implements ShouldQueue if ($this->server->unreachable_notification_sent === false) { return; } - dispatch(new ContainerStatusJob($server)); + GetContainersStatus::dispatch($server); + // dispatch(new ContainerStatusJob($server)); } public function via(object $notifiable): array diff --git a/database/migrations/2024_05_07_124019_add_server_metrics.php b/database/migrations/2024_05_07_124019_add_server_metrics.php new file mode 100644 index 000000000..40c74850b --- /dev/null +++ b/database/migrations/2024_05_07_124019_add_server_metrics.php @@ -0,0 +1,28 @@ +boolean('is_metrics_enabled')->default(true); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('is_metrics_enabled'); + }); + } +}; diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 91e90b989..2594822ce 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -19,6 +19,12 @@ services: PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" volumes: - .:/var/www/html/:cached + sentinel: + ports: + - "127.0.0.1:8888:8888" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - .:/var/www/html/:cached postgres: pull_policy: always ports: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index f3dda9748..57dedea92 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -103,6 +103,15 @@ services: condition: service_healthy redis: condition: service_healthy + sentinel: + image: "ghcr.io/coollabsio/sentinel:${LATEST_IMAGE:-latest}" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /data/coolify/metrics:/var/www/html/storage/app/metrics + ports: + - "127.0.0.1:8888:8888" + healthcheck: + test: curl --fail http://127.0.0.1:8888/api/health || exit 1 postgres: volumes: - coolify-db:/var/lib/postgresql/data diff --git a/docker-compose.yml b/docker-compose.yml index 6f1cf8e08..f292cc880 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,12 @@ services: depends_on: - postgres - redis - + sentinel: + image: "ghcr.io/coollabsio/sentinel:latest" + container_name: coolify-sentinel + restart: always + networks: + - coolify postgres: image: postgres:15-alpine container_name: coolify-db From 3eb4aed867706da687bbbe00bb27e602c4d59bf1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 09:23:32 +0200 Subject: [PATCH 07/38] chore: Refactor GetContainersStatus.php for improved readability and maintainability --- app/Actions/Docker/GetContainersStatus.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index 952dbfa1b..d606c7532 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -52,11 +52,13 @@ class GetContainersStatus return; } $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false); - if ($sentinel_found) { - raY('Sentinel'); + $sentinel_found = json_decode($sentinel_found, true); + $status = data_get($sentinel_found, '0.State.Status', 'exited'); + if ($status === 'running') { + ray('Sentinel'); $this->sentinel(); } else { - raY('Old way'); + ray('Old way'); $this->old_way(); } } @@ -107,7 +109,6 @@ class GetContainersStatus if ($application) { $foundApplications[] = $application->id; $statusFromDb = $application->status; - ray($statusFromDb, $containerStatus); if ($statusFromDb !== $containerStatus) { $application->update(['status' => $containerStatus]); } From fb80318553ddc2f00cf0a45c058437c2166f24cd Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 09:23:36 +0200 Subject: [PATCH 08/38] Refactor docker-compose files to remove version numbers --- app/Actions/Database/StartClickhouse.php | 1 - app/Actions/Database/StartDatabaseProxy.php | 1 - app/Actions/Database/StartDragonfly.php | 1 - app/Actions/Database/StartKeydb.php | 1 - app/Actions/Database/StartMariadb.php | 1 - app/Actions/Database/StartMongodb.php | 1 - app/Actions/Database/StartMysql.php | 1 - app/Actions/Database/StartPostgresql.php | 1 - app/Actions/Database/StartRedis.php | 1 - app/Jobs/ApplicationDeploymentJob.php | 1 - app/Livewire/Project/Database/Heading.php | 2 +- bootstrap/helpers/shared.php | 4 ---- templates/compose/appwrite.yaml | 1 - templates/compose/authentik.yaml | 2 -- templates/compose/glitchtip.yaml | 1 - templates/compose/penpot.yaml | 1 - templates/compose/plausible.yaml | 1 - templates/service-templates.json | 8 ++++---- 18 files changed, 5 insertions(+), 25 deletions(-) diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php index d043da410..5f567802f 100644 --- a/app/Actions/Database/StartClickhouse.php +++ b/app/Actions/Database/StartClickhouse.php @@ -33,7 +33,6 @@ class StartClickhouse $environment_variables = $this->generate_environment_variables(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartDatabaseProxy.php b/app/Actions/Database/StartDatabaseProxy.php index a1e47710c..547884b7a 100644 --- a/app/Actions/Database/StartDatabaseProxy.php +++ b/app/Actions/Database/StartDatabaseProxy.php @@ -107,7 +107,6 @@ class StartDatabaseProxy COPY nginx.conf /etc/nginx/nginx.conf EOF; $docker_compose = [ - 'version' => '3.8', 'services' => [ $proxyContainerName => [ 'build' => [ diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php index bb71d8c48..92daf195d 100644 --- a/app/Actions/Database/StartDragonfly.php +++ b/app/Actions/Database/StartDragonfly.php @@ -36,7 +36,6 @@ class StartDragonfly $environment_variables = $this->generate_environment_variables(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index 52768b13e..8c833efd5 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -37,7 +37,6 @@ class StartKeydb $this->add_custom_keydb(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index b6d6c5bb3..c79df0dc5 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -32,7 +32,6 @@ class StartMariadb $environment_variables = $this->generate_environment_variables(); $this->add_custom_mysql(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index b624c2f50..99403c2c1 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -35,7 +35,6 @@ class StartMongodb $this->add_custom_mongo_conf(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index 43b7292fb..6fdc8cdad 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -32,7 +32,6 @@ class StartMysql $environment_variables = $this->generate_environment_variables(); $this->add_custom_mysql(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index 37eb157f8..8db874ea6 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -35,7 +35,6 @@ class StartPostgresql $this->add_custom_conf(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index 35b9d5c8e..5b6ab2999 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -37,7 +37,6 @@ class StartRedis $this->add_custom_redis(); $docker_compose = [ - 'version' => '3.8', 'services' => [ $container_name => [ 'image' => $this->database->image, diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 54b96ee68..0645b1fab 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -1289,7 +1289,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->application->parseHealthcheckFromDockerfile($dockerfile); } $docker_compose = [ - 'version' => '3.8', 'services' => [ $this->container_name => [ 'image' => $this->production_image_name, diff --git a/app/Livewire/Project/Database/Heading.php b/app/Livewire/Project/Database/Heading.php index b454a683c..d6a0fe087 100644 --- a/app/Livewire/Project/Database/Heading.php +++ b/app/Livewire/Project/Database/Heading.php @@ -45,7 +45,7 @@ class Heading extends Component public function check_status($showNotification = false) { - GetContainersStatus::run($this->application->destination->server); + GetContainersStatus::run($this->database->destination->server); // dispatch_sync(new ContainerStatusJob($this->database->destination->server)); $this->database->refresh(); if ($showNotification) $this->dispatch('success', 'Database status updated.'); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index d5cacb06c..4df95f2c8 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -637,7 +637,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $allServices = getServiceTemplates(); $topLevelVolumes = collect(data_get($yaml, 'volumes', [])); $topLevelNetworks = collect(data_get($yaml, 'networks', [])); - $dockerComposeVersion = data_get($yaml, 'version') ?? '3.8'; $services = data_get($yaml, 'services'); $generatedServiceFQDNS = collect([]); @@ -1192,7 +1191,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal return $service; }); $finalServices = [ - 'version' => $dockerComposeVersion, 'services' => $services->toArray(), 'volumes' => $topLevelVolumes->toArray(), 'networks' => $topLevelNetworks->toArray(), @@ -1230,7 +1228,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $topLevelVolumes = collect([]); } $topLevelNetworks = collect(data_get($yaml, 'networks', [])); - $dockerComposeVersion = data_get($yaml, 'version') ?? '3.8'; $services = data_get($yaml, 'services'); $generatedServiceFQDNS = collect([]); @@ -1661,7 +1658,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal }); } $finalServices = [ - 'version' => $dockerComposeVersion, 'services' => $services->toArray(), 'volumes' => $topLevelVolumes->toArray(), 'networks' => $topLevelNetworks->toArray(), diff --git a/templates/compose/appwrite.yaml b/templates/compose/appwrite.yaml index 3e33e3a77..e2d64faa1 100644 --- a/templates/compose/appwrite.yaml +++ b/templates/compose/appwrite.yaml @@ -11,7 +11,6 @@ x-logging: &x-logging options: max-file: '5' max-size: '10m' -version: '3' services: appwrite: diff --git a/templates/compose/authentik.yaml b/templates/compose/authentik.yaml index d3d73489a..51360e349 100644 --- a/templates/compose/authentik.yaml +++ b/templates/compose/authentik.yaml @@ -4,8 +4,6 @@ # logo: svgs/authentik.png # port: 9000 -version: "3.4" - services: postgresql: image: docker.io/library/postgres:12-alpine diff --git a/templates/compose/glitchtip.yaml b/templates/compose/glitchtip.yaml index b53d9fe69..c73744d1b 100644 --- a/templates/compose/glitchtip.yaml +++ b/templates/compose/glitchtip.yaml @@ -4,7 +4,6 @@ # logo: svgs/glitchtip.png # port: 8080 -version: "3.8" services: postgres: image: postgres:16-alpine diff --git a/templates/compose/penpot.yaml b/templates/compose/penpot.yaml index 32d7466be..cd90985e4 100644 --- a/templates/compose/penpot.yaml +++ b/templates/compose/penpot.yaml @@ -2,7 +2,6 @@ # slogan: Penpot is the first Open Source design and prototyping platform for product teams. # tags: penpot,design,prototyping,figma,open,source -version: "3.5" services: frontend: image: "penpotapp/frontend:latest" diff --git a/templates/compose/plausible.yaml b/templates/compose/plausible.yaml index 5428e08b8..261826341 100644 --- a/templates/compose/plausible.yaml +++ b/templates/compose/plausible.yaml @@ -4,7 +4,6 @@ # tags: analytics, privacy, google, alternative # port: 8000 -version: "3.3" services: plausible: image: plausible/analytics:v2.0 diff --git a/templates/service-templates.json b/templates/service-templates.json index 8c916e474..5b3fd2b93 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -29,7 +29,7 @@ "appwrite": { "documentation": "https:\/\/appwrite.io", "slogan": "A backend-as-a-service platform that simplifies the web & mobile app development.", - "compose": "eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnZlcnNpb246ICczJwpzZXJ2aWNlczoKICBhcHB3cml0ZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS11cGxvYWRzOi9zdG9yYWdlL3VwbG9hZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNhY2hlOi9zdG9yYWdlL2NhY2hlOnJ3JwogICAgICAtICdhcHB3cml0ZS1jb25maWc6L3N0b3JhZ2UvY29uZmlnOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgICAtICdhcHB3cml0ZS1mdW5jdGlvbnM6L3N0b3JhZ2UvZnVuY3Rpb25zOnJ3JwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BUFBXUklURT0vCiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfTE9DQUxFCiAgICAgIC0gX0FQUF9DT05TT0xFX1dISVRFTElTVF9ST09UCiAgICAgIC0gX0FQUF9DT05TT0xFX1dISVRFTElTVF9FTUFJTFMKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0lQUwogICAgICAtIF9BUFBfQ09OU09MRV9IT1NUTkFNRVMKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1NZU1RFTV9SRVNQT05TRV9GT1JNQVQKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFMKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU49JFNFUlZJQ0VfRlFETl9BUFBXUklURQogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVD0kU0VSVklDRV9GUUROX0FQUFdSSVRFCiAgICAgIC0gX0FQUF9ET01BSU5fRlVOQ1RJT05TPSRTRVJWSUNFX0ZRRE5fQVBQV1JJVEUKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfU1RPUkFHRV9MSU1JVAogICAgICAtIF9BUFBfU1RPUkFHRV9QUkVWSUVXX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVUwogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVAogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0NQVVMKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19NRU1PUlkKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX0RFTEFZCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fRVhFQ1VUSU9OCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEUKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BQlVTRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FVRElUCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fVVNBR0VfSE9VUkxZCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fU0NIRURVTEVTCiAgICAgIC0gX0FQUF9TTVNfUFJPVklERVIKICAgICAgLSBfQVBQX1NNU19GUk9NCiAgICAgIC0gX0FQUF9HUkFQSFFMX01BWF9CQVRDSF9TSVpFCiAgICAgIC0gX0FQUF9HUkFQSFFMX01BWF9DT01QTEVYSVRZCiAgICAgIC0gX0FQUF9HUkFQSFFMX01BWF9ERVBUSAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9BUFBfTkFNRQogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9QUklWQVRFX0tFWQogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9BUFBfSUQKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfV0VCSE9PS19TRUNSRVQKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQ0xJRU5UX1NFQ1JFVAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9DTElFTlRfSUQKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQKICAgICAgLSBfQVBQX0FTU0lTVEFOVF9PUEVOQUlfQVBJX0tFWQogIGFwcHdyaXRlLXJlYWx0aW1lOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiByZWFsdGltZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BUFBXUklURT0vdjEvcmVhbHRpbWUKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUFRJT05TX0FCVVNFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItYXVkaXRzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItYXVkaXRzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWF1ZGl0cwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtd29ya2VyLXdlYmhvb2tzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItd2ViaG9va3MKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItd2ViaG9va3MKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtd29ya2VyLWRlbGV0ZXM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1kZWxldGVzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWRlbGV0ZXMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS11cGxvYWRzOi9zdG9yYWdlL3VwbG9hZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNhY2hlOi9zdG9yYWdlL2NhY2hlOnJ3JwogICAgICAtICdhcHB3cml0ZS1mdW5jdGlvbnM6L3N0b3JhZ2UvZnVuY3Rpb25zOnJ3JwogICAgICAtICdhcHB3cml0ZS1idWlsZHM6L3N0b3JhZ2UvYnVpbGRzOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIF9BUFBfRVhFQ1VUT1JfSE9TVAogIGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1kYXRhYmFzZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZGF0YWJhc2VzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItYnVpbGRzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItYnVpbGRzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWJ1aWxkcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1BSSVZBVEVfS0VZCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9JRAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfT1BUSU9OU19GT1JDRV9IVFRQUwogICAgICAtIF9BUFBfRE9NQUlOCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgYXBwd3JpdGUtd29ya2VyLWNlcnRpZmljYXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWNlcnRpZmljYXRlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1jZXJ0aWZpY2F0ZXMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS1jb25maWc6L3N0b3JhZ2UvY29uZmlnOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItZnVuY3Rpb25zCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWZ1bmN0aW9ucwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBvcGVucnVudGltZXMtZXhlY3V0b3IKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0NQVVMKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19NRU1PUlkKICAgICAgLSBfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIF9BUFBfRVhFQ1VUT1JfSE9TVAogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0RPQ0tFUl9IVUJfVVNFUk5BTUUKICAgICAgLSBfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgYXBwd3JpdGUtd29ya2VyLW1haWxzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWFpbHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWFpbHMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfTkFNRQogICAgICAtIF9BUFBfU1lTVEVNX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX1NNVFBfSE9TVAogICAgICAtIF9BUFBfU01UUF9QT1JUCiAgICAgIC0gX0FQUF9TTVRQX1NFQ1VSRQogICAgICAtIF9BUFBfU01UUF9VU0VSTkFNRQogICAgICAtIF9BUFBfU01UUF9QQVNTV09SRAogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItbWVzc2FnaW5nOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWVzc2FnaW5nCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLW1lc3NhZ2luZwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1NNU19GUk9NCiAgICAgIC0gX0FQUF9TTVNfUFJPVklERVIKICBhcHB3cml0ZS13b3JrZXItbWlncmF0aW9uczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLW1pZ3JhdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWlncmF0aW9ucwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfRE9NQUlOCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfSUQKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX1NFQ1JFVAogIGFwcHdyaXRlLW1haW50ZW5hbmNlOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiBtYWludGVuYW5jZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1haW50ZW5hbmNlCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlMKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX0lOVEVSVkFMCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fRVhFQ1VUSU9OCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEUKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BQlVTRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FVRElUCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fVVNBR0VfSE9VUkxZCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fU0NIRURVTEVTCiAgYXBwd3JpdGUtd29ya2VyLXVzYWdlOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUuMScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci11c2FnZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci11c2FnZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1VTQUdFX0FHR1JFR0FUSU9OX0lOVEVSVkFMCiAgYXBwd3JpdGUtd29ya2VyLXVzYWdlLWR1bXA6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNS4xJwogICAgZW50cnlwb2ludDogd29ya2VyLXVzYWdlLWR1bXAKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItdXNhZ2UtZHVtcAogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfVVNBR0VfQUdHUkVHQVRJT05fSU5URVJWQUwKICBhcHB3cml0ZS1zY2hlZHVsZXItZnVuY3Rpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiBzY2hlZHVsZS1mdW5jdGlvbnMKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZXItZnVuY3Rpb25zCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICBhcHB3cml0ZS1zY2hlZHVsZXItbWVzc2FnZXM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHNjaGVkdWxlLW1lc3NhZ2VzCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtc2NoZWR1bGVyLW1lc3NhZ2VzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICBhcHB3cml0ZS1hc3Npc3RhbnQ6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2Fzc2lzdGFudDowLjQuMCcKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1hc3Npc3RhbnQKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZCiAgb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yOgogICAgY29udGFpbmVyX25hbWU6IG9wZW5ydW50aW1lcy1leGVjdXRvcgogICAgaG9zdG5hbWU6IGFwcHdyaXRlLWV4ZWN1dG9yCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBzdG9wX3NpZ25hbDogU0lHSU5UCiAgICBpbWFnZTogJ29wZW5ydW50aW1lcy9leGVjdXRvcjowLjQuOScKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrJwogICAgICAtICdhcHB3cml0ZS1idWlsZHM6L3N0b3JhZ2UvYnVpbGRzOnJ3JwogICAgICAtICdhcHB3cml0ZS1mdW5jdGlvbnM6L3N0b3JhZ2UvZnVuY3Rpb25zOnJ3JwogICAgICAtICcvdG1wOi90bXA6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBPUFJfRVhFQ1VUT1JfSU5BQ1RJVkVfVFJFU0hPTEQ9JF9BUFBfRlVOQ1RJT05TX0lOQUNUSVZFX1RIUkVTSE9MRAogICAgICAtIE9QUl9FWEVDVVRPUl9NQUlOVEVOQU5DRV9JTlRFUlZBTD0kX0FQUF9GVU5DVElPTlNfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBPUFJfRVhFQ1VUT1JfTkVUV09SSz0kX0FQUF9GVU5DVElPTlNfUlVOVElNRVNfTkVUV09SSwogICAgICAtIE9QUl9FWEVDVVRPUl9ET0NLRVJfSFVCX1VTRVJOQU1FPSRfQVBQX0RPQ0tFUl9IVUJfVVNFUk5BTUUKICAgICAgLSBPUFJfRVhFQ1VUT1JfRE9DS0VSX0hVQl9QQVNTV09SRD0kX0FQUF9ET0NLRVJfSFVCX1BBU1NXT1JECiAgICAgIC0gT1BSX0VYRUNVVE9SX0VOVj0kX0FQUF9FTlYKICAgICAgLSBPUFJfRVhFQ1VUT1JfUlVOVElNRVM9JF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NFQ1JFVD0kX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfTE9HR0lOR19QUk9WSURFUj0kX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gT1BSX0VYRUNVVE9SX0xPR0dJTkdfQ09ORklHPSRfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfREVWSUNFPSRfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9TM19TRUNSRVQ9JF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9TM19SRUdJT049JF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9TM19CVUNLRVQ9JF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQ9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfU0VDUkVUPSRfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9SRUdJT049JF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfUkVHSU9OPSRfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgYXBwd3JpdGUtbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMC4xMScKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1tYXJpYWRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS1tYXJpYWRiOi92YXIvbGliL215c3FsOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtfQVBQX0RCX1JPT1RfUEFTU30nCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7X0FQUF9EQl9TQ0hFTUF9JwogICAgICAtICdNWVNRTF9VU0VSPSR7X0FQUF9EQl9VU0VSfScKICAgICAgLSAnTVlTUUxfUEFTU1dPUkQ9JHtfQVBQX0RCX1BBU1N9JwogICAgY29tbWFuZDogJ215c3FsZCAtLWlubm9kYi1mbHVzaC1tZXRob2Q9ZnN5bmMnCiAgYXBwd3JpdGUtcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjcuMi40LWFscGluZScKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1yZWRpcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29tbWFuZDogInJlZGlzLXNlcnZlciAtLW1heG1lbW9yeSAgICAgICAgICAgIDUxMm1iIC0tbWF4bWVtb3J5LXBvbGljeSAgICAgYWxsa2V5cy1scnUgLS1tYXhtZW1vcnktc2FtcGxlcyAgICA1XG4iCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS1yZWRpczovZGF0YTpydycKdm9sdW1lczoKICBhcHB3cml0ZS1tYXJpYWRiOiBudWxsCiAgYXBwd3JpdGUtcmVkaXM6IG51bGwKICBhcHB3cml0ZS1jYWNoZTogbnVsbAogIGFwcHdyaXRlLXVwbG9hZHM6IG51bGwKICBhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6IG51bGwKICBhcHB3cml0ZS1mdW5jdGlvbnM6IG51bGwKICBhcHB3cml0ZS1idWlsZHM6IG51bGwKICBhcHB3cml0ZS1jb25maWc6IG51bGwK", + "compose": "eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnNlcnZpY2VzOgogIGFwcHdyaXRlOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FQUFdSSVRFPS8KICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9MT0NBTEUKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1QKICAgICAgLSBfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUwogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfSVBTCiAgICAgIC0gX0FQUF9DT05TT0xFX0hPU1ROQU1FUwogICAgICAtIF9BUFBfU1lTVEVNX0VNQUlMX05BTUUKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9BRERSRVNTCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1JFU1BPTlNFX0ZPUk1BVAogICAgICAtIF9BUFBfT1BUSU9OU19BQlVTRQogICAgICAtIF9BUFBfT1BUSU9OU19GT1JDRV9IVFRQUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTj0kU0VSVklDRV9GUUROX0FQUFdSSVRFCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUPSRTRVJWSUNFX0ZRRE5fQVBQV1JJVEUKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlM9JFNFUlZJQ0VfRlFETl9BUFBXUklURQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9TTVRQX0hPU1QKICAgICAgLSBfQVBQX1NNVFBfUE9SVAogICAgICAtIF9BUFBfU01UUF9TRUNVUkUKICAgICAgLSBfQVBQX1NNVFBfVVNFUk5BTUUKICAgICAgLSBfQVBQX1NNVFBfUEFTU1dPUkQKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1BSRVZJRVdfTElNSVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQU5USVZJUlVTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVU19IT1NUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVU19QT1JUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfU0laRV9MSU1JVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfREVMQVkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICAgICAgLSBfQVBQX1NNU19QUk9WSURFUgogICAgICAtIF9BUFBfU01TX0ZST00KICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkUKICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0NPTVBMRVhJVFkKICAgICAgLSBfQVBQX0dSQVBIUUxfTUFYX0RFUFRICiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1BSSVZBVEVfS0VZCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0FQUF9JRAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9XRUJIT09LX1NFQ1JFVAogICAgICAtIF9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfSUQKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX1NFQ1JFVAogICAgICAtIF9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZCiAgYXBwd3JpdGUtcmVhbHRpbWU6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHJlYWx0aW1lCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0FQUFdSSVRFPS92MS9yZWFsdGltZQogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1hdWRpdHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1hdWRpdHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYXVkaXRzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItd2ViaG9va3M6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci13ZWJob29rcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci13ZWJob29rcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZGVsZXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWRlbGV0ZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZGVsZXRlcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9TM19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQlVDS0VUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgYXBwd3JpdGUtd29ya2VyLWRhdGFiYXNlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogd29ya2VyLWRhdGFiYXNlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgICAgLSBhcHB3cml0ZS1tYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1idWlsZHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1idWlsZHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYnVpbGRzCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9PUFRJT05TX0ZPUkNFX0hUVFBTCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItY2VydGlmaWNhdGVzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWNlcnRpZmljYXRlcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfRE9NQUlOCiAgICAgIC0gX0FQUF9ET01BSU5fVEFSR0VUCiAgICAgIC0gX0FQUF9ET01BSU5fRlVOQ1RJT05TCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgICAtIG9wZW5ydW50aW1lcy1leGVjdXRvcgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19CVUlMRF9USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQ1BVUwogICAgICAtIF9BUFBfRlVOQ1RJT05TX01FTU9SWQogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfRE9DS0VSX0hVQl9VU0VSTkFNRQogICAgICAtIF9BUFBfRE9DS0VSX0hVQl9QQVNTV09SRAogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICBhcHB3cml0ZS13b3JrZXItbWFpbHM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1tYWlscwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1tYWlscwogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmc6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1tZXNzYWdpbmcKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWVzc2FnaW5nCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfU01TX0ZST00KICAgICAgLSBfQVBQX1NNU19QUk9WSURFUgogIGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjUnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWlncmF0aW9ucwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX1NZU1RFTV9TRUNVUklUWV9FTUFJTF9BRERSRVNTCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfU0VDUkVUCiAgYXBwd3JpdGUtbWFpbnRlbmFuY2U6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IG1haW50ZW5hbmNlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtbWFpbnRlbmFuY2UKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYXBwd3JpdGUtcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICBhcHB3cml0ZS13b3JrZXItdXNhZ2U6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNS4xJwogICAgZW50cnlwb2ludDogd29ya2VyLXVzYWdlCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLXVzYWdlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfVVNBR0VfQUdHUkVHQVRJT05fSU5URVJWQUwKICBhcHB3cml0ZS13b3JrZXItdXNhZ2UtZHVtcDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41LjEnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItdXNhZ2UtZHVtcAogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci11c2FnZS1kdW1wCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLXJlZGlzCiAgICAgIC0gYXBwd3JpdGUtbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgICAgIC0gX0FQUF9VU0FHRV9BR0dSRUdBVElPTl9JTlRFUlZBTAogIGFwcHdyaXRlLXNjaGVkdWxlci1mdW5jdGlvbnM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNScKICAgIGVudHJ5cG9pbnQ6IHNjaGVkdWxlLWZ1bmN0aW9ucwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXNjaGVkdWxlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLXNjaGVkdWxlci1tZXNzYWdlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS41JwogICAgZW50cnlwb2ludDogc2NoZWR1bGUtbWVzc2FnZXMKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZXItbWVzc2FnZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICAtIGFwcHdyaXRlLW1hcmlhZGIKICAgICAgLSBhcHB3cml0ZS1yZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLWFzc2lzdGFudDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXNzaXN0YW50OjAuNC4wJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWFzc2lzdGFudAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9BU1NJU1RBTlRfT1BFTkFJX0FQSV9LRVkKICBvcGVucnVudGltZXMtZXhlY3V0b3I6CiAgICBjb250YWluZXJfbmFtZTogb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yCiAgICBob3N0bmFtZTogYXBwd3JpdGUtZXhlY3V0b3IKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHN0b3Bfc2lnbmFsOiBTSUdJTlQKICAgIGltYWdlOiAnb3BlbnJ1bnRpbWVzL2V4ZWN1dG9yOjAuNC45JwogICAgdm9sdW1lczoKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2snCiAgICAgIC0gJ2FwcHdyaXRlLWJ1aWxkczovc3RvcmFnZS9idWlsZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICAgIC0gJy90bXA6L3RtcDpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIE9QUl9FWEVDVVRPUl9JTkFDVElWRV9UUkVTSE9MRD0kX0FQUF9GVU5DVElPTlNfSU5BQ1RJVkVfVEhSRVNIT0xECiAgICAgIC0gT1BSX0VYRUNVVE9SX01BSU5URU5BTkNFX0lOVEVSVkFMPSRfQVBQX0ZVTkNUSU9OU19NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIE9QUl9FWEVDVVRPUl9ORVRXT1JLPSRfQVBQX0ZVTkNUSU9OU19SVU5USU1FU19ORVRXT1JLCiAgICAgIC0gT1BSX0VYRUNVVE9SX0RPQ0tFUl9IVUJfVVNFUk5BTUU9JF9BUFBfRE9DS0VSX0hVQl9VU0VSTkFNRQogICAgICAtIE9QUl9FWEVDVVRPUl9ET0NLRVJfSFVCX1BBU1NXT1JEPSRfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQKICAgICAgLSBPUFJfRVhFQ1VUT1JfRU5WPSRfQVBQX0VOVgogICAgICAtIE9QUl9FWEVDVVRPUl9SVU5USU1FUz0kX0FQUF9GVU5DVElPTlNfUlVOVElNRVMKICAgICAgLSBPUFJfRVhFQ1VUT1JfU0VDUkVUPSRfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9MT0dHSU5HX1BST1ZJREVSPSRfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBPUFJfRVhFQ1VUT1JfTE9HR0lOR19DT05GSUc9JF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ERVZJQ0U9JF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9TM19BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX1JFR0lPTj0kX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT049JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfQlVDS0VUPSRfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfU0VDUkVUPSRfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9SRUdJT049JF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS1tYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjEwLjExJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1hcmlhZGIKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLW1hcmlhZGI6L3Zhci9saWIvbXlzcWw6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke19BUFBfREJfUk9PVF9QQVNTfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtfQVBQX0RCX1NDSEVNQX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtfQVBQX0RCX1VTRVJ9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke19BUFBfREJfUEFTU30nCiAgICBjb21tYW5kOiAnbXlzcWxkIC0taW5ub2RiLWZsdXNoLW1ldGhvZD1mc3luYycKICBhcHB3cml0ZS1yZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny4yLjQtYWxwaW5lJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlZGlzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb21tYW5kOiAicmVkaXMtc2VydmVyIC0tbWF4bWVtb3J5ICAgICAgICAgICAgNTEybWIgLS1tYXhtZW1vcnktcG9saWN5ICAgICBhbGxrZXlzLWxydSAtLW1heG1lbW9yeS1zYW1wbGVzICAgIDVcbiIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXJlZGlzOi9kYXRhOnJ3Jwp2b2x1bWVzOgogIGFwcHdyaXRlLW1hcmlhZGI6IG51bGwKICBhcHB3cml0ZS1yZWRpczogbnVsbAogIGFwcHdyaXRlLWNhY2hlOiBudWxsCiAgYXBwd3JpdGUtdXBsb2FkczogbnVsbAogIGFwcHdyaXRlLWNlcnRpZmljYXRlczogbnVsbAogIGFwcHdyaXRlLWZ1bmN0aW9uczogbnVsbAogIGFwcHdyaXRlLWJ1aWxkczogbnVsbAogIGFwcHdyaXRlLWNvbmZpZzogbnVsbAo=", "tags": [ "backend-as-a-service", "platform" @@ -41,7 +41,7 @@ "authentik": { "documentation": "https:\/\/docs.goauthentik.io\/docs\/installation\/docker-compose", "slogan": "An open-source Identity Provider, focused on flexibility and versatility.", - "compose": "dmVyc2lvbjogJzMuNCcKc2VydmljZXM6CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2xpYnJhcnkvcG9zdGdyZXM6MTItYWxwaW5lJwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtZCBhdXRoZW50aWsgLVUgJCR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgICB2b2x1bWVzOgogICAgICAtICdhdXRoZW50aWstZGI6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSBQT1NUR1JFU19EQj1hdXRoZW50aWsKICByZWRpczoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2xpYnJhcnkvcmVkaXM6YWxwaW5lJwogICAgY29tbWFuZDogJy0tc2F2ZSA2MCAxIC0tbG9nbGV2ZWwgd2FybmluZycKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3JlZGlzLWNsaSBwaW5nIHwgZ3JlcCBQT05HJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgICB2b2x1bWVzOgogICAgICAtICdyZWRpczovZGF0YScKICBhdXRoZW50aWstc2VydmVyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dvYXV0aGVudGlrL3NlcnZlcjoke0FVVEhFTlRJS19UQUc6LTIwMjQuMi4yfScKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBjb21tYW5kOiBzZXJ2ZXIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BVVRIRU5USUtTRVJWRVJfOTAwMAogICAgICAtIEFVVEhFTlRJS19SRURJU19fSE9TVD1yZWRpcwogICAgICAtIEFVVEhFTlRJS19QT1NUR1JFU1FMX19IT1NUPXBvc3RncmVzcWwKICAgICAgLSAnQVVUSEVOVElLX1BPU1RHUkVTUUxfX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gQVVUSEVOVElLX1BPU1RHUkVTUUxfX05BTUU9YXV0aGVudGlrCiAgICAgIC0gJ0FVVEhFTlRJS19QT1NUR1JFU1FMX19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ0FVVEhFTlRJS19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9BVVRIRU5USUtTRVJWRVJ9JwogICAgICAtICdBVVRIRU5USUtfRVJST1JfUkVQT1JUSU5HX19FTkFCTEVEPSR7QVVUSEVOVElLX0VSUk9SX1JFUE9SVElOR19fRU5BQkxFRDotdHJ1ZX0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fSE9TVD0ke0FVVEhFTlRJS19FTUFJTF9fSE9TVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fUE9SVD0ke0FVVEhFTlRJS19FTUFJTF9fUE9SVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFUk5BTUU9JHtBVVRIRU5USUtfRU1BSUxfX1VTRVJOQU1FfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19QQVNTV09SRD0ke0FVVEhFTlRJS19FTUFJTF9fUEFTU1dPUkR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFM9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFN9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0w9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0x9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVQ9JHtBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX0ZST009JHtBVVRIRU5USUtfRU1BSUxfX0ZST019JwogICAgdm9sdW1lczoKICAgICAgLSAnLi9tZWRpYTovbWVkaWEnCiAgICAgIC0gJy4vY3VzdG9tLXRlbXBsYXRlczovdGVtcGxhdGVzJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgICAgIC0gcmVkaXMKICBhdXRoZW50aWstd29ya2VyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dvYXV0aGVudGlrL3NlcnZlcjoke0FVVEhFTlRJS19UQUc6LTIwMjQuMi4yfScKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBjb21tYW5kOiB3b3JrZXIKICAgIGVudmlyb25tZW50OgogICAgICAtIEFVVEhFTlRJS19SRURJU19fSE9TVD1yZWRpcwogICAgICAtIEFVVEhFTlRJS19QT1NUR1JFU1FMX19IT1NUPXBvc3RncmVzcWwKICAgICAgLSAnQVVUSEVOVElLX1BPU1RHUkVTUUxfX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gQVVUSEVOVElLX1BPU1RHUkVTUUxfX05BTUU9YXV0aGVudGlrCiAgICAgIC0gJ0FVVEhFTlRJS19QT1NUR1JFU1FMX19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ0FVVEhFTlRJS19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9BVVRIRU5USUtTRVJWRVJ9JwogICAgICAtICdBVVRIRU5USUtfRVJST1JfUkVQT1JUSU5HX19FTkFCTEVEPSR7QVVUSEVOVElLX0VSUk9SX1JFUE9SVElOR19fRU5BQkxFRH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fSE9TVD0ke0FVVEhFTlRJS19FTUFJTF9fSE9TVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fUE9SVD0ke0FVVEhFTlRJS19FTUFJTF9fUE9SVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFUk5BTUU9JHtBVVRIRU5USUtfRU1BSUxfX1VTRVJOQU1FfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19QQVNTV09SRD0ke0FVVEhFTlRJS19FTUFJTF9fUEFTU1dPUkR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFM9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFN9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0w9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0x9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVQ9JHtBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX0ZST009JHtBVVRIRU5USUtfRU1BSUxfX0ZST019JwogICAgdXNlcjogcm9vdAogICAgdm9sdW1lczoKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2snCiAgICAgIC0gJy4vbWVkaWE6L21lZGlhJwogICAgICAtICcuL2NlcnRzOi9jZXJ0cycKICAgICAgLSAnLi9jdXN0b20tdGVtcGxhdGVzOi90ZW1wbGF0ZXMnCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzcWwKICAgICAgLSByZWRpcwo=", + "compose": "c2VydmljZXM6CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2xpYnJhcnkvcG9zdGdyZXM6MTItYWxwaW5lJwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtZCBhdXRoZW50aWsgLVUgJCR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgICB2b2x1bWVzOgogICAgICAtICdhdXRoZW50aWstZGI6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSBQT1NUR1JFU19EQj1hdXRoZW50aWsKICByZWRpczoKICAgIGltYWdlOiAnZG9ja2VyLmlvL2xpYnJhcnkvcmVkaXM6YWxwaW5lJwogICAgY29tbWFuZDogJy0tc2F2ZSA2MCAxIC0tbG9nbGV2ZWwgd2FybmluZycKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3JlZGlzLWNsaSBwaW5nIHwgZ3JlcCBQT05HJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgICB2b2x1bWVzOgogICAgICAtICdyZWRpczovZGF0YScKICBhdXRoZW50aWstc2VydmVyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dvYXV0aGVudGlrL3NlcnZlcjoke0FVVEhFTlRJS19UQUc6LTIwMjQuMi4yfScKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBjb21tYW5kOiBzZXJ2ZXIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BVVRIRU5USUtTRVJWRVJfOTAwMAogICAgICAtIEFVVEhFTlRJS19SRURJU19fSE9TVD1yZWRpcwogICAgICAtIEFVVEhFTlRJS19QT1NUR1JFU1FMX19IT1NUPXBvc3RncmVzcWwKICAgICAgLSAnQVVUSEVOVElLX1BPU1RHUkVTUUxfX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gQVVUSEVOVElLX1BPU1RHUkVTUUxfX05BTUU9YXV0aGVudGlrCiAgICAgIC0gJ0FVVEhFTlRJS19QT1NUR1JFU1FMX19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ0FVVEhFTlRJS19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9BVVRIRU5USUtTRVJWRVJ9JwogICAgICAtICdBVVRIRU5USUtfRVJST1JfUkVQT1JUSU5HX19FTkFCTEVEPSR7QVVUSEVOVElLX0VSUk9SX1JFUE9SVElOR19fRU5BQkxFRDotdHJ1ZX0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fSE9TVD0ke0FVVEhFTlRJS19FTUFJTF9fSE9TVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fUE9SVD0ke0FVVEhFTlRJS19FTUFJTF9fUE9SVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFUk5BTUU9JHtBVVRIRU5USUtfRU1BSUxfX1VTRVJOQU1FfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19QQVNTV09SRD0ke0FVVEhFTlRJS19FTUFJTF9fUEFTU1dPUkR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFM9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFN9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0w9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0x9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVQ9JHtBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX0ZST009JHtBVVRIRU5USUtfRU1BSUxfX0ZST019JwogICAgdm9sdW1lczoKICAgICAgLSAnLi9tZWRpYTovbWVkaWEnCiAgICAgIC0gJy4vY3VzdG9tLXRlbXBsYXRlczovdGVtcGxhdGVzJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgICAgIC0gcmVkaXMKICBhdXRoZW50aWstd29ya2VyOgogICAgaW1hZ2U6ICdnaGNyLmlvL2dvYXV0aGVudGlrL3NlcnZlcjoke0FVVEhFTlRJS19UQUc6LTIwMjQuMi4yfScKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBjb21tYW5kOiB3b3JrZXIKICAgIGVudmlyb25tZW50OgogICAgICAtIEFVVEhFTlRJS19SRURJU19fSE9TVD1yZWRpcwogICAgICAtIEFVVEhFTlRJS19QT1NUR1JFU1FMX19IT1NUPXBvc3RncmVzcWwKICAgICAgLSAnQVVUSEVOVElLX1BPU1RHUkVTUUxfX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gQVVUSEVOVElLX1BPU1RHUkVTUUxfX05BTUU9YXV0aGVudGlrCiAgICAgIC0gJ0FVVEhFTlRJS19QT1NUR1JFU1FMX19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ0FVVEhFTlRJS19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9BVVRIRU5USUtTRVJWRVJ9JwogICAgICAtICdBVVRIRU5USUtfRVJST1JfUkVQT1JUSU5HX19FTkFCTEVEPSR7QVVUSEVOVElLX0VSUk9SX1JFUE9SVElOR19fRU5BQkxFRH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fSE9TVD0ke0FVVEhFTlRJS19FTUFJTF9fSE9TVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fUE9SVD0ke0FVVEhFTlRJS19FTUFJTF9fUE9SVH0nCiAgICAgIC0gJ0FVVEhFTlRJS19FTUFJTF9fVVNFUk5BTUU9JHtBVVRIRU5USUtfRU1BSUxfX1VTRVJOQU1FfScKICAgICAgLSAnQVVUSEVOVElLX0VNQUlMX19QQVNTV09SRD0ke0FVVEhFTlRJS19FTUFJTF9fUEFTU1dPUkR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFM9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9UTFN9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0w9JHtBVVRIRU5USUtfRU1BSUxfX1VTRV9TU0x9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVQ9JHtBVVRIRU5USUtfRU1BSUxfX1RJTUVPVVR9JwogICAgICAtICdBVVRIRU5USUtfRU1BSUxfX0ZST009JHtBVVRIRU5USUtfRU1BSUxfX0ZST019JwogICAgdXNlcjogcm9vdAogICAgdm9sdW1lczoKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2snCiAgICAgIC0gJy4vbWVkaWE6L21lZGlhJwogICAgICAtICcuL2NlcnRzOi9jZXJ0cycKICAgICAgLSAnLi9jdXN0b20tdGVtcGxhdGVzOi90ZW1wbGF0ZXMnCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzcWwKICAgICAgLSByZWRpcwo=", "tags": [ "identity", "login", @@ -403,7 +403,7 @@ "glitchtip": { "documentation": "https:\/\/glitchtip.com", "slogan": "GlitchTip is a self-hosted, open-source error tracking system.", - "compose": "dmVyc2lvbjogJzMuOCcKc2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICB3ZWI6CiAgICBpbWFnZTogZ2xpdGNodGlwL2dsaXRjaHRpcAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR0xJVENIVElQXzgwODAKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnR0xJVENIVElQX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HTElUQ0hUSVB9JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgd29ya2VyOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGNvbW1hbmQ6IC4vYmluL3J1bi1jZWxlcnktd2l0aC1iZWF0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgbWlncmF0ZToKICAgIGltYWdlOiBnbGl0Y2h0aXAvZ2xpdGNodGlwCiAgICByZXN0YXJ0OiAnbm8nCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGNvbW1hbmQ6ICcuL21hbmFnZS5weSBtaWdyYXRlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1nbGl0Y2h0aXB9JwogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnRU1BSUxfVVJMPSR7RU1BSUxfVVJMOi1jb25zb2xlbWFpbDovL30nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9Jwo=", + "compose": "c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICB3ZWI6CiAgICBpbWFnZTogZ2xpdGNodGlwL2dsaXRjaHRpcAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR0xJVENIVElQXzgwODAKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnR0xJVENIVElQX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HTElUQ0hUSVB9JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgd29ya2VyOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGNvbW1hbmQ6IC4vYmluL3J1bi1jZWxlcnktd2l0aC1iZWF0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBlY2hvCiAgICAgICAgLSBvawogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgbWlncmF0ZToKICAgIGltYWdlOiBnbGl0Y2h0aXAvZ2xpdGNodGlwCiAgICByZXN0YXJ0OiAnbm8nCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGNvbW1hbmQ6ICcuL21hbmFnZS5weSBtaWdyYXRlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1nbGl0Y2h0aXB9JwogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnRU1BSUxfVVJMPSR7RU1BSUxfVVJMOi1jb25zb2xlbWFpbDovL30nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9Jwo=", "tags": [ "error", "tracking", @@ -745,7 +745,7 @@ "penpot": { "documentation": "https:\/\/help.penpot.app\/technical-guide\/getting-started\/#install-with-docker", "slogan": "Penpot is the first Open Source design and prototyping platform for product teams.", - "compose": "dmVyc2lvbjogJzMuNScKc2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ3BlbnBvdGFwcC9mcm9udGVuZDpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdwZW5wb3QtYXNzZXRzOi9vcHQvZGF0YS9hc3NldHMnCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBlbnBvdC1iYWNrZW5kCiAgICAgIC0gcGVucG90LWV4cG9ydGVyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0ZST05URU5EX0ZMQUdTOi1lbmFibGUtbG9naW4td2l0aC1wYXNzd29yZH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwZW5wb3QtYmFja2VuZDoKICAgIGltYWdlOiAncGVucG90YXBwL2JhY2tlbmQ6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LWFzc2V0czovb3B0L2RhdGEvYXNzZXRzJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0JBQ0tFTkRfRkxBR1M6LWVuYWJsZS1sb2dpbi13aXRoLXBhc3N3b3JkIGVuYWJsZS1zbXRwIGVuYWJsZS1wcmVwbC1zZXJ2ZXJ9JwogICAgICAtIFBFTlBPVF9IVFRQX1NFUlZFUl9QT1JUPTYwNjAKICAgICAgLSBQRU5QT1RfU0VDUkVUX0tFWT0kU0VSVklDRV9SRUFMQkFTRTY0XzY0X1BFTlBPVAogICAgICAtIFBFTlBPVF9QVUJMSUNfVVJJPSRTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0JBQ0tFTkRfVVJJPWh0dHA6Ly9wZW5wb3QtYmFja2VuZCcKICAgICAgLSAnUEVOUE9UX0VYUE9SVEVSX1VSST1odHRwOi8vcGVucG90LWV4cG9ydGVyJwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVJJPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlcy8ke1BPU1RHUkVTX0RCOi1wZW5wb3R9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVNFUk5BTUU9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUEVOUE9UX1JFRElTX1VSST1yZWRpczovL3JlZGlzLzAnCiAgICAgIC0gUEVOUE9UX0FTU0VUU19TVE9SQUdFX0JBQ0tFTkQ9YXNzZXRzLWZzCiAgICAgIC0gUEVOUE9UX1NUT1JBR0VfQVNTRVRTX0ZTX0RJUkVDVE9SWT0vb3B0L2RhdGEvYXNzZXRzCiAgICAgIC0gJ1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRD0ke1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRDotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9ERUZBVUxUX0ZST009JHtQRU5QT1RfU01UUF9ERUZBVUxUX0ZST006LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfREVGQVVMVF9SRVBMWV9UTz0ke1BFTlBPVF9TTVRQX0RFRkFVTFRfUkVQTFlfVE86LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfSE9TVD0ke1BFTlBPVF9TTVRQX0hPU1Q6LW1haWxwaXR9JwogICAgICAtICdQRU5QT1RfU01UUF9QT1JUPSR7UEVOUE9UX1NNVFBfUE9SVDotMTAyNX0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1VTRVJOQU1FPSR7UEVOUE9UX1NNVFBfVVNFUk5BTUU6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1BBU1NXT1JEPSR7UEVOUE9UX1NNVFBfUEFTU1dPUkQ6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1RMUz0ke1BFTlBPVF9TTVRQX1RMUzotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9TU0w9JHtQRU5QT1RfU01UUF9TU0w6LWZhbHNlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo2MDYwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcGVucG90LWV4cG9ydGVyOgogICAgaW1hZ2U6ICdwZW5wb3RhcHAvZXhwb3J0ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUEVOUE9UX1BVQkxJQ19VUkk9JFNFUlZJQ0VfRlFETl9GUk9OVEVORAogICAgICAtICdQRU5QT1RfUkVESVNfVVJJPXJlZGlzOi8vcmVkaXMvMCcKICBtYWlscGl0OgogICAgaW1hZ2U6ICdheGxsZW50L21haWxwaXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX01BSUxQSVRfODAyNQogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BlbnBvdC1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfSU5JVERCX0FSR1M9LS1kYXRhLWNoZWNrc3VtcwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXBlbnBvdH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLWFwcGVuZG9ubHkgeWVzJwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK", + "compose": "c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ3BlbnBvdGFwcC9mcm9udGVuZDpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdwZW5wb3QtYXNzZXRzOi9vcHQvZGF0YS9hc3NldHMnCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBlbnBvdC1iYWNrZW5kCiAgICAgIC0gcGVucG90LWV4cG9ydGVyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0ZST05URU5EX0ZMQUdTOi1lbmFibGUtbG9naW4td2l0aC1wYXNzd29yZH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwZW5wb3QtYmFja2VuZDoKICAgIGltYWdlOiAncGVucG90YXBwL2JhY2tlbmQ6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LWFzc2V0czovb3B0L2RhdGEvYXNzZXRzJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUEVOUE9UX0ZMQUdTPSR7UEVOUE9UX0JBQ0tFTkRfRkxBR1M6LWVuYWJsZS1sb2dpbi13aXRoLXBhc3N3b3JkIGVuYWJsZS1zbXRwIGVuYWJsZS1wcmVwbC1zZXJ2ZXJ9JwogICAgICAtIFBFTlBPVF9IVFRQX1NFUlZFUl9QT1JUPTYwNjAKICAgICAgLSBQRU5QT1RfU0VDUkVUX0tFWT0kU0VSVklDRV9SRUFMQkFTRTY0XzY0X1BFTlBPVAogICAgICAtIFBFTlBPVF9QVUJMSUNfVVJJPSRTRVJWSUNFX0ZRRE5fRlJPTlRFTkQKICAgICAgLSAnUEVOUE9UX0JBQ0tFTkRfVVJJPWh0dHA6Ly9wZW5wb3QtYmFja2VuZCcKICAgICAgLSAnUEVOUE9UX0VYUE9SVEVSX1VSST1odHRwOi8vcGVucG90LWV4cG9ydGVyJwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVJJPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlcy8ke1BPU1RHUkVTX0RCOi1wZW5wb3R9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfVVNFUk5BTUU9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQRU5QT1RfREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUEVOUE9UX1JFRElTX1VSST1yZWRpczovL3JlZGlzLzAnCiAgICAgIC0gUEVOUE9UX0FTU0VUU19TVE9SQUdFX0JBQ0tFTkQ9YXNzZXRzLWZzCiAgICAgIC0gUEVOUE9UX1NUT1JBR0VfQVNTRVRTX0ZTX0RJUkVDVE9SWT0vb3B0L2RhdGEvYXNzZXRzCiAgICAgIC0gJ1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRD0ke1BFTlBPVF9URUxFTUVUUllfRU5BQkxFRDotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9ERUZBVUxUX0ZST009JHtQRU5QT1RfU01UUF9ERUZBVUxUX0ZST006LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfREVGQVVMVF9SRVBMWV9UTz0ke1BFTlBPVF9TTVRQX0RFRkFVTFRfUkVQTFlfVE86LW5vLXJlcGx5QGV4YW1wbGUuY29tfScKICAgICAgLSAnUEVOUE9UX1NNVFBfSE9TVD0ke1BFTlBPVF9TTVRQX0hPU1Q6LW1haWxwaXR9JwogICAgICAtICdQRU5QT1RfU01UUF9QT1JUPSR7UEVOUE9UX1NNVFBfUE9SVDotMTAyNX0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1VTRVJOQU1FPSR7UEVOUE9UX1NNVFBfVVNFUk5BTUU6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1BBU1NXT1JEPSR7UEVOUE9UX1NNVFBfUEFTU1dPUkQ6LXBlbnBvdH0nCiAgICAgIC0gJ1BFTlBPVF9TTVRQX1RMUz0ke1BFTlBPVF9TTVRQX1RMUzotZmFsc2V9JwogICAgICAtICdQRU5QT1RfU01UUF9TU0w9JHtQRU5QT1RfU01UUF9TU0w6LWZhbHNlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo2MDYwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcGVucG90LWV4cG9ydGVyOgogICAgaW1hZ2U6ICdwZW5wb3RhcHAvZXhwb3J0ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUEVOUE9UX1BVQkxJQ19VUkk9JFNFUlZJQ0VfRlFETl9GUk9OVEVORAogICAgICAtICdQRU5QT1RfUkVESVNfVVJJPXJlZGlzOi8vcmVkaXMvMCcKICBtYWlscGl0OgogICAgaW1hZ2U6ICdheGxsZW50L21haWxwaXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX01BSUxQSVRfODAyNQogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BlbnBvdC1wb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfSU5JVERCX0FSR1M9LS1kYXRhLWNoZWNrc3VtcwogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXBlbnBvdH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLWFwcGVuZG9ubHkgeWVzJwogICAgdm9sdW1lczoKICAgICAgLSAncGVucG90LXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK", "tags": [ "penpot", "design", From b74eab837786681a32971449c78b337e82d6ded8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 10:36:30 +0200 Subject: [PATCH 09/38] ui: fix tag view --- app/Livewire/Tags/Deployments.php | 1 + app/Livewire/Tags/Index.php | 15 ++++++--- resources/css/app.css | 2 +- .../views/livewire/tags/deployments.blade.php | 31 +++++++++---------- resources/views/livewire/tags/index.blade.php | 2 +- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/app/Livewire/Tags/Deployments.php b/app/Livewire/Tags/Deployments.php index 5c43edfb1..07034ed5d 100644 --- a/app/Livewire/Tags/Deployments.php +++ b/app/Livewire/Tags/Deployments.php @@ -26,6 +26,7 @@ class Deployments extends Component "server_id", "status" ])->sortBy('id')->groupBy('server_name')->toArray(); + $this->dispatch('deployments', $this->deployments_per_tag_per_server); } catch (\Exception $e) { return handleError($e, $this); } diff --git a/app/Livewire/Tags/Index.php b/app/Livewire/Tags/Index.php index d04bb53f9..c2b2a5928 100644 --- a/app/Livewire/Tags/Index.php +++ b/app/Livewire/Tags/Index.php @@ -20,6 +20,12 @@ class Index extends Component public $webhook = null; public $deployments_per_tag_per_server = []; + protected $listeners = ['deployments' => 'update_deployments']; + + public function update_deployments($deployments) + { + $this->deployments_per_tag_per_server = $deployments; + } public function tag_updated() { if ($this->tag == "") { @@ -39,14 +45,13 @@ class Index extends Component public function redeploy_all() { try { - $message = collect([]); - $this->applications->each(function ($resource) use ($message) { + $this->applications->each(function ($resource){ $deploy = new Deploy(); - $message->push($deploy->deploy_resource($resource)); + $deploy->deploy_resource($resource); }); - $this->services->each(function ($resource) use ($message) { + $this->services->each(function ($resource) { $deploy = new Deploy(); - $message->push($deploy->deploy_resource($resource)); + $deploy->deploy_resource($resource); }); $this->dispatch('success', 'Mass deployment started.'); } catch (\Exception $e) { diff --git a/resources/css/app.css b/resources/css/app.css index 5858f5cc9..bb05b783b 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -203,7 +203,7 @@ tr td:first-child { @apply flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 dark:text-white bg-neutral-50 border border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:text-black hover:no-underline text-black; } .box-without-bg { - @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem] border border-neutral-200 dark:border-black; + @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem] border border-neutral-200 dark:border-black; } .box-without-bg-without-border { @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem]; diff --git a/resources/views/livewire/tags/deployments.blade.php b/resources/views/livewire/tags/deployments.blade.php index 4d8581f79..03da021f9 100644 --- a/resources/views/livewire/tags/deployments.blade.php +++ b/resources/views/livewire/tags/deployments.blade.php @@ -1,25 +1,24 @@ -
+
@forelse ($deployments_per_tag_per_server as $server_name => $deployments)

{{ $server_name }}

-
+
@foreach ($deployments as $deployment) -
data_get($deployment, 'status') === 'queued', - 'border-yellow-500' => data_get($deployment, 'status') === 'in_progress', + 'dark:border-yellow-500' => + data_get($deployment, 'status') === 'in_progress', ])> - - +
+ @endforeach
@empty diff --git a/resources/views/livewire/tags/index.blade.php b/resources/views/livewire/tags/index.blade.php index 1fd62cefe..f91d4f00e 100644 --- a/resources/views/livewire/tags/index.blade.php +++ b/resources/views/livewire/tags/index.blade.php @@ -2,7 +2,7 @@

Tags

Tags help you to perform actions on multiple resources.
-
+
@if ($tags->count() === 0)
No tags yet defined yet. Go to a resource and add a tag there.
@else From 331cad276e8b801b6f9842821b2a1d1c64c075ea Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 10:36:38 +0200 Subject: [PATCH 10/38] chore: Refactor ApplicationDeploymentJob.php for improved readability and maintainability --- app/Jobs/ApplicationDeploymentJob.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 0645b1fab..69bfa29d0 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -1022,7 +1022,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted "command" => "docker rm -f {$this->deployment_uuid}", "ignore_errors" => true, "hidden" => true - ], + ] + ); + $this->execute_remote_command( [ $runCommand, "hidden" => true, From 2ea27acddeea955264030066e22c2f13e3f0d863 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 12:29:36 +0200 Subject: [PATCH 11/38] add cloud scripts --- other/scripts/get-subs.php | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 other/scripts/get-subs.php diff --git a/other/scripts/get-subs.php b/other/scripts/get-subs.php new file mode 100644 index 000000000..3a23fc073 --- /dev/null +++ b/other/scripts/get-subs.php @@ -0,0 +1,11 @@ +$handle = fopen("/tmp/export.csv", "w"); +App\Models\Team::chunk(100, function ($teams) use ($handle) { + foreach ($teams as $team) { + if ($team->subscription->stripe_invoice_paid == true) { + foreach ($team->members as $member) { + fputcsv($handle, [$member->email, $member->name], ","); + } + } + } +}); +fclose($handle); From c618e58a117b0b91bd685e8340f619e9ee1c938d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 14:22:35 +0200 Subject: [PATCH 12/38] feat: start Sentinel on servers. --- app/Actions/Docker/GetContainersStatus.php | 4 ++-- app/Actions/Server/StartSentinel.php | 15 +++++++++++++++ app/Jobs/ServerStatusJob.php | 1 + app/Models/Server.php | 15 +++++++++++++++ docker-compose.prod.yml | 3 +++ 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 app/Actions/Server/StartSentinel.php diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index d606c7532..80f8c7d98 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -55,10 +55,10 @@ class GetContainersStatus $sentinel_found = json_decode($sentinel_found, true); $status = data_get($sentinel_found, '0.State.Status', 'exited'); if ($status === 'running') { - ray('Sentinel'); + ray('Checking with Sentinel'); $this->sentinel(); } else { - ray('Old way'); + ray('Checking the Old way'); $this->old_way(); } } diff --git a/app/Actions/Server/StartSentinel.php b/app/Actions/Server/StartSentinel.php new file mode 100644 index 000000000..b8b89a706 --- /dev/null +++ b/app/Actions/Server/StartSentinel.php @@ -0,0 +1,15 @@ +server->isFunctional()) { $this->cleanup(notify: false); $this->removeCoolifyYaml(); + $this->server->checkSentinel(); } } catch (\Throwable $e) { send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage()); diff --git a/app/Models/Server.php b/app/Models/Server.php index 19da8d784..16659e2b8 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Actions\Server\InstallDocker; +use App\Actions\Server\StartSentinel; use App\Enums\ProxyTypes; use App\Notifications\Server\Revived; use App\Notifications\Server\Unreachable; @@ -462,6 +463,20 @@ $schema://$host { Storage::disk('ssh-keys')->delete($sshKeyFileLocation); Storage::disk('ssh-mux')->delete($this->muxFilename()); } + public function checkSentinel() { + ray("Checking sentinel on server: {$this->name}"); + if ($this->is_metrics_enabled) { + $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this, false); + $sentinel_found = json_decode($sentinel_found, true); + $status = data_get($sentinel_found, '0.State.Status', 'exited'); + if ($status !== 'running') { + ray('Sentinel is not running, starting it...'); + StartSentinel::dispatch($this); + } else { + ray('Sentinel is running'); + } + } + } public function isServerReady(int $tries = 3) { if ($this->skipServer()) { diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 57dedea92..b934d8222 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -112,6 +112,9 @@ services: - "127.0.0.1:8888:8888" healthcheck: test: curl --fail http://127.0.0.1:8888/api/health || exit 1 + interval: 10s + retries: 2 + timeout: 5s postgres: volumes: - coolify-db:/var/lib/postgresql/data From f6396f2e747fed77459c87e7d67d0b007325f84f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 8 May 2024 14:42:45 +0200 Subject: [PATCH 13/38] fix: turn off hc for dockerimage/docker base deployments by default fix: loading github app --- app/Livewire/Project/New/GithubPrivateRepository.php | 5 ++++- .../Project/New/GithubPrivateRepositoryDeployKey.php | 10 ++++++---- app/Livewire/Project/New/PublicGitRepository.php | 3 +++ .../project/new/github-private-repository.blade.php | 6 ++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index 322fd4a4e..58e3fe586 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -150,7 +150,7 @@ class GithubPrivateRepository extends Component 'repository_project_id' => $this->selected_repository_id, 'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}", 'git_branch' => $this->selected_branch_name, - 'build_pack' => 'nixpacks', + 'build_pack' => $this->build_pack, 'ports_exposes' => $this->port, 'publish_directory' => $this->publish_directory, 'environment_id' => $environment->id, @@ -162,6 +162,9 @@ class GithubPrivateRepository extends Component $application->settings->is_static = $this->is_static; $application->settings->save(); + if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') { + $application->health_check_enabled = false; + } $fqdn = generateFqdn($destination->server, $application->uuid); $application->fqdn = $fqdn; diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index ad52b9070..691b246fd 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -19,7 +19,7 @@ class GithubPrivateRepositoryDeployKey extends Component public $current_step = 'private_keys'; public $parameters; public $query; - public $private_keys =[]; + public $private_keys = []; public int $private_key_id; public int $port = 3000; @@ -125,7 +125,7 @@ class GithubPrivateRepositoryDeployKey extends Component 'name' => generate_random_name(), 'git_repository' => $this->git_repository, 'git_branch' => $this->branch, - 'build_pack' => 'nixpacks', + 'build_pack' => $this->build_pack, 'ports_exposes' => $this->port, 'publish_directory' => $this->publish_directory, 'environment_id' => $environment->id, @@ -138,7 +138,7 @@ class GithubPrivateRepositoryDeployKey extends Component 'name' => generate_random_name(), 'git_repository' => $this->git_repository, 'git_branch' => $this->branch, - 'build_pack' => 'nixpacks', + 'build_pack' => $this->build_pack, 'ports_exposes' => $this->port, 'publish_directory' => $this->publish_directory, 'environment_id' => $environment->id, @@ -149,7 +149,9 @@ class GithubPrivateRepositoryDeployKey extends Component 'source_type' => $this->git_source->getMorphClass() ]; } - + if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') { + $application_init['health_check_enabled'] = false; + } $application = Application::create($application_init); $application->settings->is_static = $this->is_static; $application->settings->save(); diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index b71a0b670..f4f3008d4 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -205,6 +205,9 @@ class PublicGitRepository extends Component ]; } + if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') { + $application_init['health_check_enabled'] = false; + } $application = Application::create($application_init); $application->settings->is_static = $this->is_static; diff --git a/resources/views/livewire/project/new/github-private-repository.blade.php b/resources/views/livewire/project/new/github-private-repository.blade.php index 21ffb2f67..f5df598db 100644 --- a/resources/views/livewire/project/new/github-private-repository.blade.php +++ b/resources/views/livewire/project/new/github-private-repository.blade.php @@ -33,14 +33,16 @@
+
+ +
@endforeach
@endif @if ($current_step === 'repository') @if ($repositories->count() > 0)
- + @foreach ($repositories as $repo) @if ($loop->first)