fix: show container on logs/executecontainer command views

fix: exclude containers with restart: no from hc
feat: add compose to predefined docker network
service: add glitchtip
This commit is contained in:
Andras Bacsai 2024-01-21 14:30:03 +01:00
parent 964ded1d0b
commit 2b394d6fea
11 changed files with 123 additions and 20 deletions

View File

@ -12,7 +12,6 @@ class StartService
public function handle(Service $service) public function handle(Service $service)
{ {
ray('Starting service: ' . $service->name); ray('Starting service: ' . $service->name);
$network = $service->destination->network;
$service->saveComposeConfigs(); $service->saveComposeConfigs();
$commands[] = "cd " . $service->workdir(); $commands[] = "cd " . $service->workdir();
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'"; $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
@ -24,10 +23,13 @@ public function handle(Service $service)
$commands[] = "echo 'Starting containers.'"; $commands[] = "echo 'Starting containers.'";
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build"; $commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true"; $commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
$compose = data_get($service, 'docker_compose', []); if (data_get($service, 'connect_to_docker_network')) {
$serviceNames = data_get(Yaml::parse($compose), 'services', []); $compose = data_get($service, 'docker_compose', []);
foreach ($serviceNames as $serviceName => $serviceConfig) { $network = $service->destination->network;
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true"; $serviceNames = data_get(Yaml::parse($compose), 'services', []);
foreach ($serviceNames as $serviceName => $serviceConfig) {
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
}
} }
$activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); $activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
return $activity; return $activity;

View File

@ -14,6 +14,7 @@ class StackForm extends Component
'service.docker_compose' => 'required', 'service.docker_compose' => 'required',
'service.name' => 'required', 'service.name' => 'required',
'service.description' => 'nullable', 'service.description' => 'nullable',
'service.connect_to_docker_network' => 'nullable',
]; ];
public $validationAttributes = []; public $validationAttributes = [];
public function mount() public function mount()
@ -44,6 +45,9 @@ public function saveCompose($raw)
$this->service->docker_compose_raw = $raw; $this->service->docker_compose_raw = $raw;
$this->submit(); $this->submit();
} }
public function instantSave() {
$this->service->save();
}
public function submit() public function submit()
{ {

View File

@ -79,21 +79,21 @@ public function getContainers()
$this->resource = $resource; $this->resource = $resource;
$this->server = $this->resource->destination->server; $this->server = $this->resource->destination->server;
$this->container = $this->resource->uuid; $this->container = $this->resource->uuid;
if (!str(data_get($this,'resource.status'))->startsWith('exited')) { // if (!str(data_get($this,'resource.status'))->startsWith('exited')) {
$this->containers->push($this->container); $this->containers->push($this->container);
} // }
} else if (data_get($this->parameters, 'service_uuid')) { } else if (data_get($this->parameters, 'service_uuid')) {
$this->type = 'service'; $this->type = 'service';
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
$this->resource->applications()->get()->each(function ($application) { $this->resource->applications()->get()->each(function ($application) {
if (str(data_get($application, 'status'))->contains('running')) { // if (str(data_get($application, 'status'))->contains('running')) {
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid')); $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
} // }
}); });
$this->resource->databases()->get()->each(function ($database) { $this->resource->databases()->get()->each(function ($database) {
if (str(data_get($database, 'status'))->contains('running')) { // if (str(data_get($database, 'status'))->contains('running')) {
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid')); $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
} // }
}); });
$this->server = $this->resource->server; $this->server = $this->resource->server;

View File

@ -70,21 +70,21 @@ public function mount()
$this->status = $this->resource->status; $this->status = $this->resource->status;
$this->server = $this->resource->destination->server; $this->server = $this->resource->destination->server;
$this->container = $this->resource->uuid; $this->container = $this->resource->uuid;
if (str(data_get($this, 'resource.status'))->startsWith('running')) { // if (str(data_get($this, 'resource.status'))->startsWith('running')) {
$this->containers->push($this->container); $this->containers->push($this->container);
} // }
} else if (data_get($this->parameters, 'service_uuid')) { } else if (data_get($this->parameters, 'service_uuid')) {
$this->type = 'service'; $this->type = 'service';
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
$this->resource->applications()->get()->each(function ($application) { $this->resource->applications()->get()->each(function ($application) {
if (str(data_get($application, 'status'))->contains('running')) { // if (str(data_get($application, 'status'))->contains('running')) {
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid')); $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
} // }
}); });
$this->resource->databases()->get()->each(function ($database) { $this->resource->databases()->get()->each(function ($database) {
if (str(data_get($database, 'status'))->contains('running')) { // if (str(data_get($database, 'status'))->contains('running')) {
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid')); $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
} // }
}); });
$this->server = $this->resource->server; $this->server = $this->resource->server;

View File

@ -19,7 +19,7 @@ public function __construct(
public string|null $helper = null, public string|null $helper = null,
public string|bool $instantSave = false, public string|bool $instantSave = false,
public bool $disabled = false, public bool $disabled = false,
public string $defaultClass = "toggle toggle-xs toggle-warning rounded disabled:bg-coolgray-200 disabled:opacity-50 placeholder:text-neutral-700" public string $defaultClass = "toggle toggle-xs toggle-warning rounded disabled:bg-coolgray-200 disabled:opacity-50 placeholder:text-neutral-700",
) { ) {
// //
} }

View File

@ -1036,6 +1036,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if (!data_get($service, 'restart')) { if (!data_get($service, 'restart')) {
data_set($service, 'restart', RESTART_MODE); data_set($service, 'restart', RESTART_MODE);
} }
if (data_get($service, 'restart') === 'no') {
$savedService->update(['exclude_from_status' => true]);
}
data_set($service, 'container_name', $containerName); data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content'); data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory'); data_forget($service, 'volumes.*.isDirectory');

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('services', function (Blueprint $table) {
$table->boolean('connect_to_docker_network')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('services', function (Blueprint $table) {
$table->dropColumn('connect_to_docker_network');
});
}
};

View File

@ -1,6 +1,6 @@
<div class="flex flex-row items-center gap-4 px-2 form-control min-w-fit hover:bg-coolgray-100"> <div class="flex flex-row items-center gap-4 px-2 form-control min-w-fit hover:bg-coolgray-100">
<label class="flex gap-4 px-0 label"> <label class="flex gap-4 px-0 min-w-fit label">
<span class="flex gap-2 label-text min-w-fit"> <span class="flex gap-2 label-text">
@if ($label) @if ($label)
{!! $label !!} {!! $label !!}
@else @else

View File

@ -14,6 +14,9 @@
<x-forms.input id="service.name" required label="Service Name" placeholder="My super wordpress site" /> <x-forms.input id="service.name" required label="Service Name" placeholder="My super wordpress site" />
<x-forms.input id="service.description" label="Description" /> <x-forms.input id="service.description" label="Description" />
</div> </div>
<div class="w-96">
<x-forms.checkbox instantSave id="service.connect_to_docker_network" label="Connect To Predefined Network" helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='text-white underline' href='https://coolify.io/docs/docker/compose#connect-to-predefined-networks'>this</a>." />
</div>
@if ($fields) @if ($fields)
<div> <div>
<h3>Service Specific Configuration</h3> <h3>Service Specific Configuration</h3>

View File

@ -0,0 +1,57 @@
version: "3.8"
services:
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- POSTGRES_DB=${POSTGRESQL_DATABASE:-glitchtip}
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: redis
web:
image: glitchtip/glitchtip
depends_on:
- postgres
- redis
environment:
- SERVICE_FQDN_GLITCHTIP
- DATABASE_URL=postgres://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgres:5432/${POSTGRESQL_DATABASE:-glitchtip}
- SECRET_KEY=$SERVICE_BASE64_64_ENCRYPTION
- EMAIL_URL=${EMAIL_URL:-consolemail://}
- GLITCHTIP_DOMAIN=${SERVICE_FQDN_GLITCHTIP}
- DEFAULT_FROM_EMAIL=${DEFAULT_FROM_EMAIL:-test@example.com}
- CELERY_WORKER_AUTOSCALE=${CELERY_WORKER_AUTOSCALE:-1,3}
- CELERY_WORKER_MAX_TASKS_PER_CHILD=${CELERY_WORKER_MAX_TASKS_PER_CHILD:-10000}
volumes:
- uploads:/code/uploads
worker:
image: glitchtip/glitchtip
command: ./bin/run-celery-with-beat.sh
depends_on:
- postgres
- redis
environment:
- DATABASE_URL=postgres://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgres:5432/${POSTGRESQL_DATABASE:-glitchtip}
- SECRET_KEY=$SERVICE_BASE64_64_ENCRYPTION
- EMAIL_URL=${EMAIL_URL:-consolemail://}
- DEFAULT_FROM_EMAIL=${DEFAULT_FROM_EMAIL:-test@example.com}
- CELERY_WORKER_AUTOSCALE=${CELERY_WORKER_AUTOSCALE:-1,3}
- CELERY_WORKER_MAX_TASKS_PER_CHILD=${CELERY_WORKER_MAX_TASKS_PER_CHILD:-10000}
volumes:
- uploads:/code/uploads
migrate:
image: glitchtip/glitchtip
restart: "no"
depends_on:
- postgres
- redis
command: "./manage.py migrate"
environment:
- DATABASE_URL=postgres://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgres:5432/${POSTGRESQL_DATABASE:-glitchtip}
- SECRET_KEY=$SERVICE_BASE64_64_ENCRYPTION
- EMAIL_URL=${EMAIL_URL:-consolemail://}
- DEFAULT_FROM_EMAIL=${DEFAULT_FROM_EMAIL:-test@example.com}
- CELERY_WORKER_AUTOSCALE=${CELERY_WORKER_AUTOSCALE:-1,3}
- CELERY_WORKER_MAX_TASKS_PER_CHILD=${CELERY_WORKER_MAX_TASKS_PER_CHILD:-10000}

View File

@ -230,6 +230,12 @@
"lightweight" "lightweight"
] ]
}, },
"glitchtip": {
"documentation": "https:\/\/coolify.io\/docs",
"slogan": "Glitchtip.yaml",
"compose": "dmVyc2lvbjogJzMuOCcKc2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgd2ViOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dMSVRDSFRJUAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdHTElUQ0hUSVBfRE9NQUlOPSR7U0VSVklDRV9GUUROX0dMSVRDSFRJUH0nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9JwogICAgdm9sdW1lczoKICAgICAgLSAndXBsb2FkczovY29kZS91cGxvYWRzJwogIHdvcmtlcjoKICAgIGltYWdlOiBnbGl0Y2h0aXAvZ2xpdGNodGlwCiAgICBjb21tYW5kOiAuL2Jpbi9ydW4tY2VsZXJ5LXdpdGgtYmVhdC5zaAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7REVGQVVMVF9GUk9NX0VNQUlMOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9BVVRPU0NBTEU9JHtDRUxFUllfV09SS0VSX0FVVE9TQ0FMRTotMSwzfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEPSR7Q0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEOi0xMDAwMH0nCiAgICB2b2x1bWVzOgogICAgICAtICd1cGxvYWRzOi9jb2RlL3VwbG9hZHMnCiAgbWlncmF0ZToKICAgIGltYWdlOiBnbGl0Y2h0aXAvZ2xpdGNodGlwCiAgICByZXN0YXJ0OiAnbm8nCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGNvbW1hbmQ6ICcuL21hbmFnZS5weSBtaWdyYXRlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1nbGl0Y2h0aXB9JwogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnRU1BSUxfVVJMPSR7RU1BSUxfVVJMOi1jb25zb2xlbWFpbDovL30nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9Jwo=",
"tags": null
},
"grafana-with-postgresql": { "grafana-with-postgresql": {
"documentation": "https:\/\/grafana.com\/docs\/grafana\/latest\/installation\/docker\/", "documentation": "https:\/\/grafana.com\/docs\/grafana\/latest\/installation\/docker\/",
"slogan": "Grafana is the open source analytics & monitoring solution for every database.", "slogan": "Grafana is the open source analytics & monitoring solution for every database.",