Have Sail and SSU. Runs a command on a remote host.

This commit is contained in:
Joao Patricio 2023-03-20 14:04:35 +00:00
parent a613e9f113
commit bfdae4339c
15 changed files with 234 additions and 77 deletions

View File

@ -8,6 +8,7 @@ GROUPID=
############################################################################################################ ############################################################################################################
APP_NAME=Laravel APP_NAME=Laravel
APP_SERVICE=php
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
@ -17,7 +18,7 @@ DB_CONNECTION=pgsql
DB_HOST=postgres DB_HOST=postgres
DB_PORT=5432 DB_PORT=5432
DB_DATABASE=coolify DB_DATABASE=coolify
DB_USERNAME=sail DB_USERNAME=coolify
DB_PASSWORD=password DB_PASSWORD=password
QUEUE_CONNECTION=redis QUEUE_CONNECTION=database

View File

@ -12,7 +12,7 @@ class RunCommand extends Component
public $manualKeepAlive = false; public $manualKeepAlive = false;
public $command = ''; public $command = 'ls';
public function render() public function render()
{ {
@ -21,30 +21,32 @@ public function render()
public function runCommand() public function runCommand()
{ {
// TODO Execute with Livewire Normally $this->isKeepAliveOn = true;
$this->activity = coolifyProcess($this->command, 'testing-host');
// Override manual to experiment // Override manual to experiment
// $sleepingBeauty = 'x=1; while [ $x -le 40 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done'; $override = 0;
//
// $commandString = <<<EOT
// cd projects/dummy-project
// ~/.docker/cli-plugins/docker-compose build --no-cache
// # $sleepingBeauty
// EOT;
//
// $this->activity = coolifyProcess($commandString, 'testing-host');
if($override) {
// Good to play with the throttle feature
$sleepingBeauty = 'x=1; while [ $x -le 40 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done';
$this->isKeepAliveOn = true; $this->activity = coolifyProcess(<<<EOT
cd projects/dummy-project
# ~/.docker/cli-plugins/docker-compose build --no-cache
$sleepingBeauty
EOT, 'testing-host');
return;
}
$this->activity = coolifyProcess($this->command, 'testing-host');
} }
public function polling() public function polling()
{ {
$this->activity?->refresh(); $this->activity?->refresh();
if ($this->activity?->properties['status'] === 'finished') { if (data_get($this->activity, 'properties.exitCode') !== null) {
$this->isKeepAliveOn = false; $this->isKeepAliveOn = false;
} }
} }

View File

@ -2,6 +2,7 @@
namespace App\Jobs; namespace App\Jobs;
use App\Services\ProcessStatus;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldBeUnique;
@ -11,7 +12,6 @@
use Illuminate\Process\ProcessResult; use Illuminate\Process\ProcessResult;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Process;
use Spatie\Activitylog\Contracts\Activity; use Spatie\Activitylog\Contracts\Activity;
@ -43,8 +43,7 @@ public function __construct(
*/ */
public function handle(): ?ProcessResult public function handle(): ?ProcessResult
{ {
ray()->clearAll(); $this->timeStart = hrtime(true);
$this->timeStart = $start = hrtime(true);
$user = $this->activity->getExtraProperty('user'); $user = $this->activity->getExtraProperty('user');
$destination = $this->activity->getExtraProperty('destination'); $destination = $this->activity->getExtraProperty('destination');
@ -53,24 +52,16 @@ public function handle(): ?ProcessResult
$delimiter = 'EOF-COOLIFY-SSH'; $delimiter = 'EOF-COOLIFY-SSH';
File::chmod(base_path('coolify_id25519'), 0600);
$sshCommand = 'ssh ' $sshCommand = 'ssh '
. '-i ./coolify_id25519 '
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' . '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
. '-o PasswordAuthentication=no ' . '-o PasswordAuthentication=no '
. "-p {$port} "
. "{$user}@{$destination} " . "{$user}@{$destination} "
. " 'bash -se' << \\$delimiter" . PHP_EOL . " 'bash -se' << \\$delimiter" . PHP_EOL
. $command . PHP_EOL . $command . PHP_EOL
. $delimiter; . $delimiter;
// $sshCommand = "whoami ; pwd ; ls "; $process = Process::start($sshCommand, $this->handleOutput(...));
$process = Process::start(
$sshCommand,
$this->handleOutput(...),
);
$res = $process->wait(); $res = $process->wait();
@ -78,13 +69,20 @@ public function handle(): ?ProcessResult
return $res; return $res;
} }
$status = match ($res->exitCode()) {
// TODO Why is this not persisting?? Immutable property?? 0 => ProcessStatus::FINISHED,
$this->activity->properties->put('pid', $process->id()); default => ProcessStatus::ERROR,
$this->activity->properties->put('exitCode', $res->exitCode()); };
$this->activity->properties->put('stdout', $res->output());
$this->activity->properties->put('stderr', $res->errorOutput()); $this->activity->properties = $this->activity->properties->merge([
'exitCode' => $res->exitCode(),
'stdout' => $res->output(),
'stderr' => $res->errorOutput(),
'status' => $status,
]);
$this->activity->save(); $this->activity->save();
return $res; return $res;
} }

View File

@ -19,12 +19,14 @@ public function __construct(
protected ?string $user = 'root', protected ?string $user = 'root',
){ ){
$this->activity = activity() $this->activity = activity()
->withProperty('type', 'COOLIFY_PROCESS') ->withProperties([
->withProperty('user', $this->user) 'type' => 'COOLIFY_PROCESS',
->withProperty('destination', $this->destination) 'user' => $this->user,
->withProperty('port', $this->port) 'destination' => $this->destination,
->withProperty('command', $this->command) 'port' => $this->port,
->withProperty('status', ProcessStatus::HOLDING) 'command' => $this->command,
'status' => ProcessStatus::HOLDING,
])
->log("Awaiting to start command...\n\n"); ->log("Awaiting to start command...\n\n");
} }

View File

@ -1,7 +0,0 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
-----END OPENSSH PRIVATE KEY-----

View File

@ -3,15 +3,18 @@ services:
php: php:
image: coolify:4 image: coolify:4
build: build:
dockerfile: Dockerfile
context: ./docker/dev context: ./docker/dev
dockerfile: Dockerfile
args:
WWWGROUP: '${WWWGROUP}'
ports: ports:
- "${APP_PORT:-8000}:80" - "${APP_PORT:-8000}:80"
- "${VITE_PORT:-5173}:${VITE_PORT:-5173}" - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
environment: environment:
PUID: "${USERID:-9999}" WWWUSER: "${WWWUSER}"
PGID: "${GROUPID:-9999}" LARAVEL_SAIL: 1
SSL_MODE: 'off' XDEBUG_MODE: "${SAIL_XDEBUG_MODE:-off}"
XDEBUG_CONFIG: "${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}"
volumes: volumes:
- .:/var/www/html - .:/var/www/html
networks: networks:

View File

@ -1,20 +1,93 @@
FROM serversideup/php:8.2-fpm-nginx FROM ubuntu:22.04
ARG WWWGROUP
ARG TARGETPLATFORM
ARG NODE_VERSION=18 ARG NODE_VERSION=18
ARG POSTGRES_VERSION=15 ARG POSTGRES_VERSION=14
# https://download.docker.com/linux/static/stable/
ARG DOCKER_VERSION=20.10.18
# https://github.com/docker/compose/releases
# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug.
ARG DOCKER_COMPOSE_VERSION=2.6.1
# https://github.com/buildpacks/pack/releases
ARG PACK_VERSION=v0.27.0
WORKDIR /var/www/html
ENV DEBIAN_FRONTEND noninteractive
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y gnupg gosu curl ca-certificates zip unzip git git-lfs supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils \
&& curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
&& apt-get update \
&& apt-get install -y php8.2-cli php8.2-dev \
php8.2-pgsql php8.2-sqlite3 php8.2-gd \
php8.2-curl \
php8.2-imap php8.2-mysql php8.2-mbstring \
php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \
php8.2-intl php8.2-readline \
php8.2-ldap \
php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \
php8.2-memcached php8.2-pcov php8.2-xdebug \
&& php -r "readfile('https://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \
&& curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \
&& apt-get install -y nodejs \ && apt-get install -y nodejs \
&& npm install -g npm && npm install -g npm \
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \
RUN apt-get install -y php-pgsql openssh-client && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
&& curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \
RUN apt-get -y autoremove \ && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
&& apt-get update \
&& apt-get install -y yarn \
&& apt-get install -y mysql-client \
&& apt-get install -y postgresql-client-$POSTGRES_VERSION \
&& apt-get -y autoremove \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2
RUN groupadd --force -g $WWWGROUP sail
RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail
USER sail
RUN mkdir -p ~/.ssh
RUN echo "-----BEGIN OPENSSH PRIVATE KEY-----\n\
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n\
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk\n\
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA\n\
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV\n\
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==\n\
-----END OPENSSH PRIVATE KEY-----" >> ~/.ssh/id_ed25519
RUN chmod 0600 ~/.ssh/id_ed25519
USER root USER root
# S6 Overlay config COPY start-container /usr/local/bin/start-container
COPY --chmod=755 etc/s6-overlay/ /etc/s6-overlay/ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY php.ini /etc/php/8.2/cli/conf.d/99-sail.ini
RUN chmod +x /usr/local/bin/start-container
# Install Docker CLI, Docker Compose, and Pack
RUN mkdir -p ~/.docker/cli-plugins
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose -o /home/sail/.docker/cli-plugins/docker-compose
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack
RUN curl -sSL https://nixpacks.com/install.sh | bash
RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack
RUN chmod +x /usr/local/bin/pack
RUN mkdir -p /home/sail/.docker/cli-plugins
RUN cp ~/.docker/cli-plugins/docker-compose /home/sail/.docker/cli-plugins/docker-compose
RUN chown -R sail:sail /home/sail
EXPOSE 8000
ENTRYPOINT ["start-container"]

4
docker/dev/php.ini Normal file
View File

@ -0,0 +1,4 @@
[PHP]
post_max_size = 100M
upload_max_filesize = 100M
variables_order = EGPCS

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
if [ ! -z "$WWWUSER" ]; then
usermod -u $WWWUSER sail
fi
if [ ! -d /.composer ]; then
mkdir /.composer
fi
chmod -R ugo+rw /.composer
if [ $# -gt 0 ]; then
exec gosu $WWWUSER "$@"
else
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
fi

View File

@ -0,0 +1,27 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[program:php]
command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80
user=sail
environment=LARAVEL_SAIL="1"
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan queue:listen
user=sail
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
numprocs=8
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log
stopwaitsecs=3600

20
docker/staging/Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM serversideup/php:8.2-fpm-nginx
ARG NODE_VERSION=18
ARG POSTGRES_VERSION=15
RUN apt-get update \
&& curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g npm
RUN apt-get install -y php-pgsql openssh-client
RUN apt-get -y autoremove \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
USER root
# S6 Overlay config
COPY --chmod=755 etc/s6-overlay/ /etc/s6-overlay/

View File

@ -6,7 +6,7 @@
</label> </label>
<button class="btn btn-success btn-xs rounded-none" wire:click="runCommand"> <button class="btn btn-success btn-xs rounded-none" wire:click="runCommand">
Run command Run command
<button> <button>
</div> </div>
@isset($activity?->id) @isset($activity?->id)
@ -18,6 +18,35 @@
<div class="w-full h-10"></div> <div class="w-full h-10"></div>
<div
@if($isKeepAliveOn || $manualKeepAlive) wire:poll.750ms="polling" @endif
>
<pre
style="
background-color: #FFFFFF;
width: 1200px;
height: 600px;
overflow-y: scroll;
display: flex;
flex-direction: column-reverse;
"
placeholder="Build output"
>
{{ data_get($activity, 'description') }}
</pre>
<div>
<input id="manualKeepAlive" name="manualKeepAlive" type="checkbox" wire:model="manualKeepAlive">
<label for="manualKeepAlive"> Live content </label>
</div>
@if($isKeepAliveOn || $manualKeepAlive)
Polling...
@endif
</div>
<pre <pre
style=" style="
background-color: #FFFFFF; background-color: #FFFFFF;
@ -28,17 +57,5 @@
flex-direction: column-reverse; flex-direction: column-reverse;
" "
placeholder="Build output" placeholder="Build output"
@if($isKeepAliveOn || $manualKeepAlive) wire:poll.750ms="polling" @endif >{{ json_encode(data_get($activity, 'properties'), JSON_PRETTY_PRINT) }}</pre>
>
{{ data_get($activity, 'description') }}
</pre>
<div>
<input id="manualKeepAlive" name="manualKeepAlive" type="checkbox" wire:model="manualKeepAlive">
<label for="manualKeepAlive"> Live content </label>
</div>
@if($isKeepAliveOn || $manualKeepAlive)
Polling...
@endif
</div> </div>